Announcing Intelligent Cache, our caching library

It is with great pleasure that we are announcing today our newest open source project intelligent cache. We are very enthusiastic about this project because we think we constructed a very usable interface and ecosystem which you can rely on and extend extremely easily. Let me talk to you about it.

Intelligent Cache is currently a dotnet-core, netstandard and .net 4.6.1 library but it's easily extensible to almost any other language.

intelligent cache

Fork it here -- Nuget Package here

The Cache

At the heart of the library, there is a pattern (actually a monad) that defines two important things:

  • what a cache should look like (an interface)
  • how to combine caches so they work together (a composition class)

The cache interface at the moment is very minimalistic, as we try to include only what we actually need in production and not to add features based on perceived, but not actual, needs.

public interface ICache
{
    Task<T> GetSetAsync<T>(string key, Func<CancellationToken, Task<T>> calculateValue, TimeSpan duration, CancellationToken cancellationToken = default) where T: class;

    T GetSet<T>(string key, Func<T> calculateValue, TimeSpan duration) where T: class;

    Task InvalidateAsync(string key, CancellationToken cancellationToken = default);

    void Invalidate(string key);
}

There are both async and sync versions of the cache to support both future and current systems that might not have adopted async or that will adopt it in the future. The two main methods are GetSet and Invalidate. The first returns an object from the cache and fills the cache using the given lambda if the key specified is empty or null. The second method, Invalidate, clears the current key and it's useful in case we know that the current cache has become stale, typically in case of updates.

The composition function chains together different caches so they "fall through": if the requested key is not present in the first cache, it looks in the second before actually fetching a new value. This is useful to create distributed caches, but also to add other kinds of behaviors as we will see below. The composition class has this signature.

public class CompositeCache : ICache
{
    public CompositeCache(ICache level1, ICache level2)
    {
            // ...
    }

    // ICache implementation ...
}

At the moment we have 3 implementations of the cache, a local cache based on MemoryCache, a distributed cache based on Redis, and a passthrough cache. On top of these three basic building blocks, we have two classes that should be used together to distribute invalidation messages in case you need to keep caches in sync over multiple servers.

Here is what the dataflow of a full-fledged 2 tier distributed library looks like:

This cache can be built with the following composition pattern.

ISubscriber subscriber = GetRedisSubscriber();
var invalidationChannel = "cache-invalidations";
var cache = new CompositeCache(
    new RedisInvalidationReceiver(
        new MemoryCache(/* arguments */),
        subscriber,
        invalidationChannel
    ),
    new CompositeCache(
        new RedisCache(/* arguments */),
        new RedisInvalidationSender(subscriber, invalidationChannel)
    )
);

Of course, there's much more information including working examples and tests on the intelligent cache repo.

Extending the cache

The caching library is built to be trivial to extend with your own implementation. Let's say that you wanted to log to console all operations of a cache. This can be done very easily by extending ICache and composing:

public class LogToConsoleBehavior: ICache
{
    public T GetSet<T>(string key, Func<T> calculateValue, TimeSpan duration) where T: class
    {
        Console.WriteLine("GetSet called");
        return calculateValue();
    }

    // other methods are implemented similarly

}

ICache cache = new Cache(); // this is the cache we want to extend with logging
var logToConsoleCache = new CompositeCache(new LogToConsoleBehavior(), cache);

// now logToConsoleCache is just like cache, except it logs to console

Contributions

We are looking for contributors, please look at our open issues if you are looking for something to do. If you find this project useful, please give us a star on GitHub and share the library.

The project has been developed through the sponsorship of Intelligent Hack and iSolutions. Please give them a shout out if you can.




Hi, I'm Marco Cecconi. I am the founder of Intelligent Hack, developer, hacker, blogger, conference lecturer. Bio: ex Stack Overflow core team, ex Toptal EM.

Read more

Newest Posts

Guest blog: Building, in partnership with communities by Shog9

A lesson in building communities by Stack Overflow's most prominent community manager emeritus, Shog9

Read more
Can you migrate a company from on-premise to remote-only?

Some lessons learned over the past 8 years of remote work in some of the best remote companies on the planet

Read more
Announcing Intelligent Cache, our caching library

Our newest open source initiative, intelligent cache, is available for use

Read more
Guest Blog: The mythical 10x programmer by Antirez

In this post, Salvatore Sanfilippo puts together a list of qualities that I believe make the most difference in programmers’ productivity.

Read more
Team EMEA

Today I want to introduce our second engineering team: Team EMEA

Read more

Gleanings

And the Most Realistic Developer in Fiction is...
Julia Silge • Mar 28, 2017

We can say that Mr. Robot is having a moment. The main character was one of the top choices and thus is perhaps the most/least realistic/annoying/inspiring portrayal of what it’s like to be a computer programmer today.

Read more…