Skip to content

Latest commit

 

History

History
93 lines (66 loc) · 4.97 KB

DiskCache.md

File metadata and controls

93 lines (66 loc) · 4.97 KB

FusionCache logo

💾 Disk Cache

⚡ TL;DR (quick version)
When we want to ease cold starts but don't want need multi-nodes support, we can use an implementation of IDistributedCache based on SQLite to achieve that.

In certain situations we may like to have some of the benefits of a 2nd level like better cold starts (when the memory cache is initially empty) but at the same time we don't want to have a separate actual distributed cache to handle, or we simply cannot have it. A good example of that may be a mobile app, where everything should be self contained.

In those situations we may want a distributed cache that is "not really distributed", something like an implementation of IDistributedCache that reads and writes directly to one or more local files: makes sense, right?

Yes, kinda, but there is more to that.

We should also think about the details, about all the things it should handle for a real-real world usage:

  • have the ability to read and write data in a persistent way to local files (so the cached data will survive restarts)
  • ability to prevent data corruption when writing to disk
  • support some form of compression, to avoid wasting too much space on disk
  • support concurrent access without deadlocks, starvations and whatnot
  • be fast and resource optimized, so to consume as little cpu cycles and memory as possible
  • and probably something more that I'm forgetting

That's a lot to do... but wait a sec, isn't that exactly what a database is?

Yes, yes it is!

😐 An actual database? Are you kidding me?

Of course I'm not suggesting to install (and manage) a local MySql/SqlServer/PostgreSQL instance or similar, that would be hard to do in most cases, impossible in others and frankly overkill.

So, what should we use?

💪 SQLite to the rescue!

If case you didn't know it yet, SQLite is an incredible piece of software:

Ok so SQLite is the best, how can we use it as the 2nd level?

👩‍🏫 Ok but how?

Luckily someone in the community created an implementation of IDistributedCache based on SQLite, and released it as the NeoSmart.Caching.Sqlite Nuget package (GitHub repo here).

The package:

  • supports both the sync and async models natively, meaning it's not doing async-over-sync or vice versa, but a real double impl (like FusionCache does) which is very nice and will use the underlying system resources best
  • uses a pooling mechanism which means the memory allocation will be lower since they reuse existing objects instead of creating new ones every time and consequently, because of that, less cpu usage in the long run because less pressure on the GC (Garbage Collector)
  • supports CancellationTokens, meaning that it will gracefully handle cancellations in case it's needed, like for example a mobile app pause/shutdown events or similar

It's a really good package, let's see how to use it.

👩‍💻 Example

We simply use the SqliteCache implementation and we'll be good to go:

services.AddFusionCache()
    .WithSerializer(
        new FusionCacheNewtonsoftJsonSerializer()
    )
    .WithDistributedCache(
        new SqliteCache(new SqliteCacheOptions { CachePath = "CACHE PATH" })
    )
;

Alternatively, we can register it as THE IDistributedCache implementation, and just tell FusionCache to use the registered one, whatever that may be:

// REGISTER SQLITE AS THE IDistributedCache IMPL
services.AddSqliteCache(options => {
    options.CachePath = "CACHE PATH";
});

services.AddFusionCache()
    .WithSerializer(
        new FusionCacheNewtonsoftJsonSerializer()
    )
    // USE THE REGISTERED IDistributedCache IMPL
    .WithRegisteredDistributedCache()
;

If you like what you are seeing, remember to give that repo a star ⭐ and share it!