RulesEngine/src/RulesEngine/HelperFunctions/MemCache.cs

116 lines
3.4 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace RulesEngine.HelperFunctions
{
public class MemCacheConfig {
public int SizeLimit { get; set; } = 1000;
}
internal class MemCache
{
private readonly MemCacheConfig _config;
private ConcurrentDictionary<string, (object value, DateTimeOffset expiry)> _cacheDictionary;
private ConcurrentQueue<(string key, DateTimeOffset expiry)> _cacheEvictionQueue;
public MemCache(MemCacheConfig config)
{
if(config == null)
{
config = new MemCacheConfig();
}
_config = config;
_cacheDictionary = new ConcurrentDictionary<string, (object value, DateTimeOffset expiry)>();
_cacheEvictionQueue = new ConcurrentQueue<(string key, DateTimeOffset expiry)>();
}
public bool TryGetValue<T>(string key,out T value)
{
value = default;
if (_cacheDictionary.TryGetValue(key, out var cacheItem))
{
if(cacheItem.expiry < DateTimeOffset.UtcNow)
{
_cacheDictionary.TryRemove(key, out _);
return false;
}
else
{
value = (T)cacheItem.value;
return true;
}
}
return false;
}
public T Get<T>(string key)
{
TryGetValue<T>(key, out var value);
return value;
}
/// <summary>
/// Returns all known keys. May return keys for expired data as well
/// </summary>
/// <returns></returns>
public IEnumerable<string> GetKeys()
{
return _cacheDictionary.Keys;
}
public T GetOrCreate<T>(string key, Func<T> createFn, DateTimeOffset? expiry = null)
{
if(!TryGetValue<T>(key,out var value))
{
value = createFn();
return Set<T>(key,value,expiry);
}
return value;
}
public T Set<T>(string key, T value, DateTimeOffset? expiry = null)
{
var fixedExpiry = expiry ?? DateTimeOffset.MaxValue;
while (_cacheDictionary.Count > _config.SizeLimit)
{
if (_cacheEvictionQueue.IsEmpty)
{
_cacheDictionary.Clear();
}
if(_cacheEvictionQueue.TryDequeue(out var result)
&& _cacheDictionary.TryGetValue(result.key,out var dictionaryValue)
&& dictionaryValue.expiry == result.expiry)
{
_cacheDictionary.TryRemove(result.key, out _);
}
}
_cacheDictionary.AddOrUpdate(key, (value, fixedExpiry), (k, v) => (value, fixedExpiry));
_cacheEvictionQueue.Enqueue((key, fixedExpiry));
return value;
}
public void Remove(string key)
{
_cacheDictionary.TryRemove(key, out _);
}
public void Clear()
{
_cacheDictionary.Clear();
_cacheEvictionQueue = new ConcurrentQueue<(string key, DateTimeOffset expiry)>();
}
}
}