Apex Cache Tracker to remove session cache

Here is the problem that needed to be solved:
1. Use session cache to cache some user information like their profile or user settings
2. When a external systems update the information and try to remove the session cache as it got updated it fails because it does not have access to a users session cache

Solution:
Build a session tracker in Org Cache to track missed removal of session cache

Step 1: When a session cache remove fails add it to the Org level tracker cache. The format of session keys are the following:
key + userId (Salesforce userId)

That way I can keep track of what userId’s I need to remove session cache
String splitStrKey = key.substring(0, key.length()-18);
String splitUserId = key.substring(key.length()-18, key.length());

will split into key and userId

public Boolean remove(String key) {
      if (!System.isBatch() && !System.isFuture() && !Test.isRunningTest()){
        Boolean removed = false;
        if (sessionCacheEnabled && sessionPartition!=null) {
              System.debug('inside session cache remove key: ' + key);
              removed = sessionPartition.remove(key);
              if (!removed && key!=null && key.length() > 18){
                String splitStrKey = key.substring(0, key.length()-18);
                String splitUserId = key.substring(key.length()-18, key.length());
                System.debug('Session remove failed key ' + splitStrKey + ' for ' + splitUserId + ' adding to tracker');
                removeFailedAddToTracker(splitStrKey, splitUserId, null);
              }
          } else if (orgPartition!=null){
              removed = orgPartition.remove(key);
          }

        if (removed) {
          System.debug(LoggingLevel.DEBUG, 'Removed key ' + key);
          return true;
        } else{
          System.debug(LoggingLevel.DEBUG, 'Not removed key not found ' + key);
          return false;
        }
      } else {
        System.debug(LoggingLevel.DEBUG, 'Skipping cache remove in batch ' + key);
        return false;
      }
    }

Step 2: Add session keys to Org level tracker cache (CACHE_USER_CACHE_TRACKER), we will add it as Map<Id, Map> where Map contains UserId as key and Map of all the session keys that needs to be removed

public void removeFailedAddToTracker(String keyPrefix, Id userId, Set<Id> clientIds){
      if (App_Service.isIntegrationUser(userId)){
        Map<String, String> mapOfContactUserIds = (Map<String, String>)getOrgPartition().get(App_Rest_Constants.CACHE_USER_CONTACT_MAP);
        Map<Id, Map<String, Id>> mapOfExistingCacheQueueTracker = (Map<Id, Map<String, Id>>)getOrgPartition().get(App_Rest_Constants.CACHE_USER_CACHE_TRACKER);
        if (clientIds!=null && !clientIds.isEmpty()){
          for (Id clientId : clientIds){
            if (mapOfContactUserIds!=null && mapOfContactUserIds.containsKey(clientId)){
              Id userIdToClearCache = mapOfContactUserIds.get(clientId);

              if (mapOfExistingCacheQueueTracker!=null){
                if (mapOfExistingCacheQueueTracker.containsKey(userIdToClearCache)){
                  Map<String, Id> existingCacheStrings = mapOfExistingCacheQueueTracker.get(userIdToClearCache);
                  existingCacheStrings.put(keyPrefix, userIdToClearCache);
                  mapOfExistingCacheQueueTracker.put(userIdToClearCache, existingCacheStrings);
                } else {
                  mapOfExistingCacheQueueTracker.put(userIdToClearCache, new Map<String, Id>{keyPrefix=>userIdToClearCache});
                }
              } else {
                mapOfExistingCacheQueueTracker = new Map<Id, Map<String, Id>>();
                mapOfExistingCacheQueueTracker.put(userIdToClearCache, new Map<String, Id>{keyPrefix=>userIdToClearCache});
              }
            }
          }
        } else {
          if (mapOfExistingCacheQueueTracker!=null && mapOfExistingCacheQueueTracker.containsKey(userId)){
            Map<String, Id> existingMap = mapOfExistingCacheQueueTracker.get(userId);
            existingMap.put(keyPrefix, userId);
            mapOfExistingCacheQueueTracker.put(userId, existingMap);
          } else {
            if (mapOfExistingCacheQueueTracker==null){
              mapOfExistingCacheQueueTracker = new Map<Id, Map<String, Id>>();
            }
            mapOfExistingCacheQueueTracker.put(userId, new Map<String, Id>{keyPrefix=>userId});
          }
        }

        if (mapOfExistingCacheQueueTracker!=null)
          getOrgPartition().put(App_Constants.CACHE_USER_CACHE_TRACKER, mapOfExistingCacheQueueTracker);
      }
    }

Step 3: Every time before we check if session cache contains a key we will see if the same key is contained in the CACHE_USER_CACHE_TRACKER cache. If yes remove it from the users session cache so that cache can be removed and new query can be done

  public Boolean containsKey(String cacheKey){
      Boolean containsKey = false;
      if (!System.isBatch() && !System.isFuture() && !Test.isRunningTest()){
           if (sessionCacheEnabled==false && orgPartition!=null){
              if (orgPartition.get(cacheKey)!=null){
                containsKey = true;
              }
          } else if (sessionCacheEnabled && sessionPartition!=null) {
              if (sessionPartition.get(cacheKey)!=null){
                containsKey = true;

                Map<Id, Map<String, Id>> mapOfExistingCacheQueueTracker = (Map<Id, Map<String, Id>>)getOrgPartition().get(App_Rest_Constants.CACHE_USER_CACHE_TRACKER);
              	if (mapOfExistingCacheQueueTracker!=null && mapOfExistingCacheQueueTracker.containsKey(UserInfo.getUserId())){
            			Map<String, Id> flagsToClear = mapOfExistingCacheQueueTracker.get(UserInfo.getUserId());

                  Boolean removeFlag = false;
            			for (String flagToClear : flagsToClear.keySet()){
            				if (flagToClear.equalsIgnoreCase(cacheKey)){
                      String keyToRemove = flagToClear + flagsToClear.get(flagToClear);
            					Boolean removeItemFromCache = getSessionPartition().remove(keyToRemove);
                      
                      removeFlag = true;
                      containsKey = false;
            				}
            			}

                  if (removeFlag){
                    for (String flagToClear : flagsToClear.keySet()){
                      flagsToClear.remove(cacheKey);
                      if (flagsToClear.isEmpty() && flagsToClear.size()==0){
                        mapOfExistingCacheQueueTracker.remove(UserInfo.getUserId());
                      } else {
                        mapOfExistingCacheQueueTracker.put(UserInfo.getUserId(), flagsToClear);
                      }
                    }
                    
                    getOrgPartition().put(App_Rest_Constants.CACHE_USER_CACHE_TRACKER, mapOfExistingCacheQueueTracker);
                  }

            		}
              }
          }
       }
      return containsKey;
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s