0 votes
1 view
by

I'* considering using a combination of static variables and the List object's hashCode method to cache the results of a SOQL call based on the values being passed in (accountIds). The methods will be service methods (not always called from Lightning Components).

Here's a simple example:

private static Map<Integer, List<Account>> accountCache;
public static List<Account> GetAccounts(List<Id> accountIds) {
    if(accountCache.get(accountIds.getHashcode()) == null) {
        accountCache.put(accountIds.getHashcode(), [select Id from Accounts limit 10]);
    }
    return accountCache.values();
}

A few questions:

  1. Can I count on the List's hashCode to be a reliable key for this caching scenario?
  2. Are there any other issues that might cause this code to not work as I expect it to?
  3. Are there any best practices (or platform services) I'* overlooking to accomplish what I'* trying to do?

Thank you.

1 Answer

0 votes
by

Yes, hashCode is reliable, although strictly speaking, unnecessary. You could use the object itself:

private static Map<Object, List<Account>> accountCache;
public static List<Account> GetAccounts(List<Id> accountIds) {
    if(accountCache.get(accountIds) == null) {
        accountCache.put(accountIds, [select Id from Accounts limit 10]);
    }
    return accountCache.get(accountIds);
}

Can I count on the List's hashCode to be a reliable key for this caching scenario?

Yes, it's perfectly reliable.

Are there any other issues that might cause this code to not work as I expect it to?

Your caching algorithm doesn't account for partial matches. For example, if you query accounts 1,2,3,4, then later, accounts 1,2,3,4,5, you've queried four extra accounts a second time, and you have two copies of them in memory.

In addition, there's a very, very small (but non-zero) chance that the hash will collide with a different set of values. Using the object version, above, as the key, prevents this collision possibility.

Are there any best practices (or platform services) I'* overlooking to accomplish what I'* trying to do?

What I can say is that a more comprehensive strategy should be used. Something like this:

static Map<Id, Account> accountCache = new Map<Id, Account>();
public static Account[] getAccounts(Id[] accountIds) {
  Set<Id> newAccounts = new Set<Id>(accountIds);
  newAccounts.removeAll(accountCache.keySet());
  if(!newAccounts.isEmpty()) {
    accountCache.putAll([SELECT Id FROM Account WHERE Id = :newAccounts]);
  }
  Map<Id, Account> results = new Map<Id, Account>(accountCache.values());
  results.keySet().retainAll(accountIds);
  return results.values();
}
Welcome to Memory Exceeded, where you can ask questions and receive answers from other members of the community.
...