Policy-based ValueCache

In one of my last posts I analyzed how policy based class design could look like in C#. It turned out that policy based class design can help decomposing functionality which would usually be implemented in one class into smaller parts (policies). Those can then be put together (configure) in different combinations in order to compose one class which fits exactly the user’s current needs. In this post I want to work on a policy based class which provides caching functionality for one single datum.

 First I want to differentiate between 2 approaches declaring a cache of data as outdated:
  • The cache does not know when its data “runs out of date” and therefore uses some heuristics or strategies to determine that. For example a common strategy to declare the cache as invalid is when its content is older than a certain timespan. A web browser could follow such a strategy when it determines whether it displays a page from its cache or reloads it from the web.
  • The source of data which is being cached has access to every cache and can actively invalidate them. Consider an in-process cache of an artifact doing database inserts and queries.

The cache I want to implement is going to support the first approach for now. Its only direct operation supported from a user’s perspective is getting the cached value. Internally it has to distinguish two different cases:

  1. The cache is valid: return the cached value
  2. The cache is invalid: cached value must be renewed and returned

The description of those two cases leaves open what the definiton of “when is the cache valid or invalid?” and “how can the cache be renewed?” is. Those definitions will depend on the actual usage scenario of the cache. Some developers might need a cache which becomes invalid after a certain time, others might need a cache which expires after it has been requested for a certain amount of times. Clearly the definition of when the cache is valid or invalid must be made flexible and exchangeable when implementing the cache class. In other words: It should be implemented as a policy. The same applies when obtaining fresh data in case the cache became invalid. Is calling a delegate provided by the user enough? Should that be done in a multi threading safe way? and so on. Eventually we identify two policies: An “invalidation policy” being responsible for the validity of the cache and a “retrieval policy” taking care of getting fresh data.

With that in mind we construct a basic workflow of the cache class:

  1. Inform invalidation policy that the cache is requested
  2. If invalidation policy claims that the cache is valid return the cached value
  3. Otherwise run retrieval policy to get update for the cached value
  4. Inform invalidation policy that the cache has been updated
  5. Return cached value

or in code:

if (this.cacheInvalidationPolicy.IsCacheValid())
    return this.value;
this.value = this.cacheValueRetrievingPolicy.GetValue();
return this.value;

Looking at this workflow the retrieval policy turns out to be quite simple. It supports exactly one operation called “GetValue”:

public interface ICacheValueRetrievingPolicy<T>
    T GetValue();

Regarding the invalidation policy it needs to support the following operations:

  • Cache class informs the policy that the user requests the content of cache
  • Cache class wants to know whether the cache is valid
  • Cache class informs the policy that the cache has been renewed
public interface ICacheInvalidationPolicy

    void CacheRequested();
    bool IsCacheValid();
    void CacheRenewed();

Adding all things together – the cache class and the 2 policies – we are done with the first version of the value cache. I have created a small project on CodePlex called “Byleist” which contains the complete source code. You can find it here. Please note that Byleist is a portable library project. Thus you need to install the Portable Library Tools before opening the Byleist project.

Flattr this!

Leave a Reply

Your email address will not be published. Required fields are marked *