You are cleaning a cache wrong.
Tags: csharp, dotnet
Published: 2023-10-22 14:07:09

Today, I would like to talk about cache. No about how to create or as important caching. I want to talk about how to manage the cache correctly. The most famous cache provider is Memory Cache from Microsoft. I'm sure you know the `Microsoft.Extensions.Caching.Memory` NuGet package. The typical implementation is something like this:

```C#
using Microsoft.Extensions.Caching.Memory;

var options = new MemoryCacheOptions
{
    ExpirationScanFrequency = TimeSpan.FromMinutes(15)
};
IMemoryCache cache = new MemoryCache(options);
cache.Set("item1", "value1", TimeSpan.FromMinutes(1));
cache.Remove("item1");
```

So why am I writing this article if everything works fine and is very simple? The problem is in removing data from the memory. The typical setting is data lifecycle, after which it'll be deleted. The issue is in the probable blocking of the main thread, which can lead to a deadlock. 

Let's consider another NuGet package as `CacheManager.Microsoft.Extensions.Caching.Memory`. This package allows you to manage data more effectively and safely.
Let's implement a cache manager:

```C#
var cacheManager = new BaseCacheManager<string>(
            new ConfigurationBuilder()
                .WithMicrosoftMemoryCacheHandle()
                .EnablePerformanceCounters()
                .EnableStatistics()
                .WithExpiration(ExpirationMode.Sliding, TimeSpan.FromMinutes(15))
                .Build()
        );
```
The cache manager allows you to work with different providers, even Redis. Also, you can choose different time expiration modes, performance counters, and statistics.

Further, let me show you how to add items:

```C#
// Add items to the cache
        cacheManager.Add("item1",
            DateTime.Now.AddMinutes(1).ToString(CultureInfo.InvariantCulture),
            "region1");
        cacheManager.Add("item2",
            DateTime.Now.AddMinutes(2).ToString(CultureInfo.InvariantCulture),
            "region1");
        cacheManager.Add("item1",
            DateTime.Now.AddMinutes(3).ToString(CultureInfo.InvariantCulture), "region2");
        var item = new CacheItem<string>(
            "item2",
            "region2",
            DateTime.Now.AddMinutes(4)
                .ToString(CultureInfo.InvariantCulture),
            ExpirationMode.Absolute,
            TimeSpan.FromSeconds(6));
        cacheManager.Add(item);
```

Here, you can see the difference between Memory Cache. The Cache Manager allows you to add regions for each item. For what purposes is it needed? First of all, you can add equal keys to storage. Second, you can remove the batch of things. The Cache Manager also knows how to set expiration with different modes for each item. Below, I showed how to batch-delete items:

```C#
cacheManager.ClearRegion("region1");
```

Most interesting, we'll clear data in a different thread.

```C#
// Start the cache cleanup task (runs in the background)
        await Task.Run(async () =>
        {
            while (true)
            {
                await Task.Delay(TimeSpan.FromSeconds(5));
                DisplayValue(cacheManager, "item1", "region1");
                DisplayValue(cacheManager, "item2", "region1");
                DisplayValue(cacheManager, "item1", "region2");
                DisplayValue(cacheManager, "item2", "region2");
                await Task.Delay(TimeSpan.FromSeconds(10));
                cacheManager.Clear();
                DisplayValue(cacheManager, "item1", "region1");
                DisplayValue(cacheManager, "item2", "region1");
                DisplayValue(cacheManager, "item1", "region2");
                DisplayValue(cacheManager, "item2", "region2");
            }
        });
```
For reusing of code, I used this method:

```C#
static void DisplayValue(ICacheManager<string> cache, string key, string region)
    {
        var value = cache.Get(key, region);
        Console.WriteLine(value != null
            ? $"Retrieved value for {key} in {region}: {value}"
            : $"Item {key} not found in {region}.");
    }
```

If we run it, we'll see that the first two rows are empty. The deleted items by region caused it.

```
Item item1 not found in region1.
Item item2 not found in region1.
Retrieved value for item1 in region2: 10/22/2023 16:36:37
Retrieved value for item2 in region2: 10/22/2023 16:37:37
```
And in the end, the storage was entirely cleared.

```
Item item1 not found in region1.
Item item2 not found in region1.
Item item1 not found in region2.
Item item2 not found in region2.
```

For more information about this library's opportunities and features, I recommend visiting the official documentation. I hope this information was helpful to you. See you next week, and happy coding.
The source code is, as always, on my [GitHub](https://github.com/SergKorol/CacheManagerSample).

<a href="https://www.buymeacoffee.com/b9z99kntvtQ" target="_blank"><img src="http://dartfordwaffler.co.uk/wp-content/uploads/2021/03/buy-me-a-beer.jpg" alt="Buy Me A Beer" style="height: 60px !important;width: 317px !important;" ></a>