SlimCache
Recordsets to HashMaps make it easy to interact with data, especially
for custom queries where the element sets are dynamic. They give us key
value pairs for all our data. However, often we have a certain dataset
that we use frequently but whose signature changes infrequently. When
this data is saved to cache, each entry contains not only the values,
but also their associated keys. For example, say you save 1,000 entries
of a certain type to cache, with 15 fields. You now have an extra
15,0000 worth of labels that will get serialized into the cache, then
unserialized again when it’s pulled out of cache. Also, the commonly
used structure for this, a HashMap is heavier then two simple key/value
strings. This greatly increases the size of those memcache objects,
unnecessary considering that you do not use the memcache entries
directly, yet bootstrap each of them with extra information.
Typically by using a certain name prefix to an element is cache, we
know it’s non-changing data footprint. We can leverage this by storing
only the dynamic data in cache (values), rather then also storing the
associated fixed labels (keys) for each of the elements.
You might be thinking this sounds complicated. Somehow you’d have to
store values to memcache, and still have them tied to some aggregate
record hashmap representation. When you retrieve from cache you’d have
to still add the key’s back in to the values, so other functions could
easily access the data. Last you would want some easy way for others to
readily see what the element properties are.
You are right, you would need all of these things and fortunately code has been written to do this easily.
In these instructions, I will use as an example implementation
called memberTiny, which was the original motivation for the slimCache
framework.
First you will want to create an array of strings to hold
information about your slimCache object. Each element should hold:
HashMap label, database column name, and data type. Each of these
elements should be in an array as well.Here are the properties for our example cache type:
private static final String[][] CACHE_TINY_PROPERTIES = {{"member_id", "member_id", "string"},
{"display_name","display_name", "string"},
{"member_ind", "member_ind", "string"},
{"last_active_timestamp", "last_sign_in_dttm", "date"},
{"gender_cd","gender_cd", "string"},
{"photo_medium_file_name", "photo_medium_file_name", "string"}};
This shared signature should never be changed, hence the static final modifier.You will also need to give ID’s for the cache prefix name and for its counter used for tracking. Also which cache type we will be using, (Big or Small). In our example:
private static final String CACHE_NAME_TINY = "MemberTiny";
private static final String COUNTER_NAME_TINY = "MemberTiny";
private static final String CACHE_TYPE = "Big";
Next we need some information specific to the databasetable we are interested in if we have any additional where clause we
can use it here:
private static final String DB_TABLE = "Member";
private static final String DB_KEY = "member_id";
private static final String ADDITIONAL_CLAUSE = "AND member_ind = 'Y' AND frozen_dttm is NULL";
Optionally, we have special handlers for circumstances
were the value mapping from the database field to the HashMap is not
one-to-one, instead we want to do an explicit conversion. For those
special fields we add a CustomHandler to and it’s field name a Map.
Here is the abstract class in SlimCache that you would implement in
your subclass:
public static abstract class CustomHandler {
protected Object fieldToHashValue(String tableKey, PageState pageState, Object dbField){
return null;
}
}
for example MemberTiny
tempHandlers.put("display_name", new CustomHandler() {
@Override
public Object fieldToHashValue(String keyId, PageState pageState, Object dbField) {
return Text.dbDecode((String)dbField);
}
});
CUSTOM_HANDLERS = tempHandlers;
You are now halfway done. I will now, for the most part, simply be explaining how the framework is used.
To get your data:
public static HashMap
if your data is not already in the memcache it will go to the db, put the data in cache, and return to the caller:
static Map
If you want to get multiple members you can use:
static Map
Examples:
public static Map
return getMultiFromProperties(memberIds, PageState, COUNTER_NAME_TINY, CACHE_NAME_TINY, CACHE_TINY_PROPERTIES);
}
public static HashMap
return getCacheFromProperties(memberId, PageState, COUNTER_NAME_TINY, CACHE_NAME_TINY, CACHE_TINY_PROPERTIES);
}
As a rule, whenever a “wider” cache is set, that is, one where your
elements are a subset of the former, you should always go and
set/update your cache as well since the other cache has alread paid the
price for getting the data, also to keep memcache data in sync.
Example: Here is a get of member small data with an update of member tiny as well:
public static HashMap
. . .
Cache.setLocalCache("BIG", PageState, COUNTER_NAME, CACHE_NAME + memberId, colHash, PageState.expire48);
Cache.setLocalCache("BIG", PageState, COUNTER_NAME_TINY, CACHE_NAME_TINY + memberId, toStringFromHash(CACHE_TINY_PROPERTIES, colHash), PageState.expire48);
. . .
}
*UPDATE: The code below was from an initial version, it’s been revised many times since then, but the motivation / algorithm remain about the same *
Here is the source for SlimCache and MemberTiny:
Also see associated tests
See the attached Javadocs (unzip, click index in generated docs folder)
Comments
Leave a comment Trackback