Skip to content

Commit

Permalink
feat: changed memory cache key list implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
aochmann committed May 26, 2021
1 parent e7e10d9 commit e2a3cfb
Show file tree
Hide file tree
Showing 3 changed files with 308 additions and 193 deletions.
20 changes: 20 additions & 0 deletions Source/Cogworks.Essentials/EventArgs/CacheEvictionArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.Extensions.Caching.Memory;

namespace Cogworks.Essentials.EventArgs
{
public class CacheEvictionArgs : System.EventArgs
{
public object Key { get; }

public object Value { get; }

public EvictionReason EvictionReason { get; set; }

public CacheEvictionArgs(object key, object value, EvictionReason evictionReason)
{
Key = key;
Value = value;
EvictionReason = evictionReason;
}
}
}
99 changes: 49 additions & 50 deletions Source/Cogworks.Essentials/Services/MemoryCacheService.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Cogworks.Essentials.Constants;
using Cogworks.Essentials.EventArgs;
using Cogworks.Essentials.Extensions;
using Cogworks.Essentials.Services.Interfaces;
using Microsoft.Extensions.Caching.Memory;
Expand All @@ -13,10 +15,10 @@ namespace Cogworks.Essentials.Services
{
public class MemoryCacheService : ICacheService, IDisposable
{
private const string CacheKeyList = "CacheKeyList";

private readonly IMemoryCache _memoryCache;

private ImmutableHashSet<string> _cacheKeys = ImmutableHashSet<string>.Empty;

private static ConcurrentDictionary<object, SemaphoreSlim> Locks => new ConcurrentDictionary<object, SemaphoreSlim>();

public MemoryCacheService(IMemoryCache memoryCache)
Expand All @@ -34,10 +36,7 @@ public T GetCacheItem<T>(string cacheKey)
: default;

public void RemoveCacheItem(string cacheKey)
{
RemoveCacheKeyList(cacheKey);
_memoryCache.Remove(cacheKey);
}
=> _memoryCache.Remove(cacheKey);

public void AddCacheItem(string cacheKey, object value, int? cacheDurationInSeconds = null)
{
Expand All @@ -46,7 +45,11 @@ public void AddCacheItem(string cacheKey, object value, int? cacheDurationInSeco
cacheDurationInSeconds ??= DateTimeConstants.TimeInSecondsConstants.Hour;
var cacheDurationDateTime = DateTime.UtcNow.AddSeconds(cacheDurationInSeconds.Value);

_memoryCache.Set(cacheKey, value, cacheDurationDateTime);
var entryOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(cacheDurationDateTime)
.RegisterPostEvictionCallback(CacheCallback);

_memoryCache.Set(cacheKey, value, entryOptions);
}

public T GetOrAddCacheItem<T>(string cacheKey, Func<T> getValueFunction, int? cacheDurationInSeconds = null)
Expand All @@ -57,6 +60,12 @@ public T GetOrAddCacheItem<T>(string cacheKey, Func<T> getValueFunction, int? ca
var cacheDurationDateTime = DateTime.UtcNow.AddSeconds(cacheDurationInSeconds.Value);

entry.AbsoluteExpiration = cacheDurationDateTime;

entry.PostEvictionCallbacks.Add(new PostEvictionCallbackRegistration()
{
EvictionCallback = CacheCallback
});

AddCacheKeyList(cacheKey);

return getValueFunction();
Expand Down Expand Up @@ -91,7 +100,11 @@ public async Task<T> GetOrAddCacheItemAsync<T>(string cacheKey, Func<Task<T>> ge
cacheDurationInSeconds ??= DateTimeConstants.TimeInSecondsConstants.Hour;
var cacheDurationDateTime = DateTime.UtcNow.AddSeconds(cacheDurationInSeconds.Value);

_memoryCache.Set(cacheKey, cacheEntry, cacheDurationDateTime);
var entryOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(cacheDurationDateTime)
.RegisterPostEvictionCallback(CacheCallback);

_memoryCache.Set(cacheKey, cacheEntry, entryOptions);
}
}
finally
Expand All @@ -105,7 +118,7 @@ public async Task<T> GetOrAddCacheItemAsync<T>(string cacheKey, Func<Task<T>> ge

public void ClearAllStartingWith(string prefixKey)
{
var cacheKeys = GetOrAddCacheKeyList()
var cacheKeys = _cacheKeys
.Where(x => x.StartsWith(prefixKey))
.ToList();

Expand All @@ -118,15 +131,13 @@ public void ClearAllStartingWith(string prefixKey)
{
_memoryCache.Remove(key);
}

RemoveCacheKeyList(cacheKeys);
}

public void ClearAll()
{
var cacheKeys = GetOrAddCacheKeyList();
var cacheKeys = _cacheKeys.ToArray();

if (!cacheKeys.HasAny())
if (!_cacheKeys.HasAny())
{
return;
}
Expand All @@ -135,55 +146,43 @@ public void ClearAll()
{
_memoryCache.Remove(key);
}

RemoveCacheKeyList(cacheKeys);
}

public void Dispose()
=> _memoryCache.Dispose();
public IEnumerable<string> GetKeys()
=> _cacheKeys.ToList();

private void AddCacheKeyList(string cacheKey)
public void Dispose()
{
var cacheKeyList = GetOrAddCacheKeyList();

cacheKeyList.AddUnique(cacheKey);

var cacheDurationDateTime = DateTime.UtcNow.AddSeconds(DateTimeConstants.TimeInSecondsConstants.Year);

_memoryCache.Set(CacheKeyList, cacheKeyList, cacheDurationDateTime);
_cacheKeys = _cacheKeys.Clear();
_memoryCache.Dispose();
}

private void RemoveCacheKeyList(string cacheKey)
{
var cacheKeyList = GetOrAddCacheKeyList();

cacheKeyList.Remove(cacheKey);
private void AddCacheKeyList(string cacheKey)
=> ImmutableInterlocked.Update(
ref _cacheKeys,
(collection, item) => collection.Add(item),
cacheKey);

UpdateCacheKeyList(cacheKeyList);
}
private void RemoveCacheKeyList(string cacheKey)
=> ImmutableInterlocked.Update(
ref _cacheKeys,
(collection, item) => collection.Remove(item),
cacheKey);

private void RemoveCacheKeyList(IEnumerable<string> toBeRemovedItems)
private void CacheCallback(object key, object value, EvictionReason reason, object state)
{
var cacheKeys = GetOrAddCacheKeyList();
if (reason == EvictionReason.Replaced || key is not string cacheKey)
{
return;
}

cacheKeys = cacheKeys.Except(toBeRemovedItems).ToList();
CacheEvictionEvent?.Invoke(
this,
new CacheEvictionArgs(key, value, reason));

UpdateCacheKeyList(cacheKeys);
RemoveCacheKeyList(cacheKey);
}

private void UpdateCacheKeyList(IEnumerable<string> cacheKeys)
=> _memoryCache.Set(
CacheKeyList,
cacheKeys,
DateTime.UtcNow.AddSeconds(DateTimeConstants.TimeInSecondsConstants.Year));

private List<string> GetOrAddCacheKeyList()
=> _memoryCache.GetOrCreate(CacheKeyList, entry =>
{
var cacheDurationDateTime = DateTime.UtcNow.AddSeconds(DateTimeConstants.TimeInSecondsConstants.Year);
entry.AbsoluteExpiration = cacheDurationDateTime;

return new List<string>();
});
public event EventHandler<CacheEvictionArgs> CacheEvictionEvent;
}
}
Loading

0 comments on commit e2a3cfb

Please sign in to comment.