One of the most commonly used patterns in software development is Caching. It’s a simple, but a very effective concept. The idea is to reuse operation results. When performing a heavy operation, we will save the result in our cache container. The next time that we need that result, we will pull it from the cache container, instead of performing the heavy operation again.
For example, to get a person’s Avatar you might need a trip to the database. Instead of performing that trip every time, we will save that Avatar in the cache, pulling it from memory every time you need it.
Caching works great for data that changes infrequently. Or even better, never changes. Data that constantly changes, like the current machine’s time shouldn’t be cached or you will get wrong results.
In-process Cache, Persistant in-process Cache, and Distributed Cache
There are 3 types of caches:
- In-Memory Cache is used for when you want to implement cache in a single process. When the process dies, the cache dies with it. If you’re running the same process on several servers, you will have a separate cache for each server.
- Persistent in-process Cache is when you back up your cache outside of process memory. It might be in a file, or in a database. This is more difficult, but if your process is restarted, the cache is not lost. Best used when getting the cached item is expansive, and your process tends to restart a lot.
- Distributed Cache is when you want to have shared cache for several machines. Usually, it will be several servers. With a distributed cache, it is stored in an external service. This means if one server saved a cache item, other servers can use it as well. Services like Redis are great for this.
in-process cache:
Microsoft.
https://www.nuget.org/packages/Microsoft.Extensions.Caching.Memory/
Microsoft. Extensions. Caching. Memory
public
class WaitToFinishMemoryCache<TItem>
{
private
MemoryCache
_cache
= new MemoryCache(new MemoryCacheOptions());
private
ConcurrentDictionary<object, SemaphoreSlim> _locks = new ConcurrentDictionary<object,
SemaphoreSlim>();
public
async
Task<TItem>
GetOrCreate(object key, Func<Task<TItem>>
createItem)
{
TItem cacheEntry;
if
(!_cache.TryGetValue(key, out cacheEntry))// Look for
cache key.
{
SemaphoreSlim mylock = _locks.GetOrAdd(key, k => new SemaphoreSlim(1, 1));
await mylock.WaitAsync();
try
{
if
(!_cache.TryGetValue(key, out cacheEntry))
{
//
Key not in cache, so get data.
cacheEntry
= await
createItem();
_cache.Set(key, cacheEntry);
}
}
finally
{
mylock.Release();
}
}
return
cacheEntry;
}
}
Usage:
var _avatarCache = new WaitToFinishMemoryCache<byte[]>();
//
...
var myAvatar =
await
_avatarCache.GetOrCreate(userId, async () => await _database.GetAvatar(userId));
With this, when trying to get an item, if the same item is in the middle of being created by another thread, you will wait for the other to finish first. Then, you will get the already cached item created by the other thread.
|
No comments:
Post a Comment