|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using ZeroLevel.Services.Shedulling;
|
|
|
|
|
|
|
|
|
|
namespace ZeroLevel.Services.Cache
|
|
|
|
|
{
|
|
|
|
|
internal sealed class TimerCachee<T>
|
|
|
|
|
: IDisposable
|
|
|
|
|
{
|
|
|
|
|
private sealed class CacheeItem<T>
|
|
|
|
|
{
|
|
|
|
|
public T Value { get; set; }
|
|
|
|
|
public DateTime LastAcessTime { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private readonly IDictionary<string, CacheeItem<T>> _cachee;
|
|
|
|
|
private readonly object _cacheeLock = new object();
|
|
|
|
|
|
|
|
|
|
private readonly ISheduller _sheduller;
|
|
|
|
|
private readonly Func<string, T> _factory;
|
|
|
|
|
private readonly Action<T> _onDisposeAction;
|
|
|
|
|
private readonly TimeSpan _expirationPeriod;
|
|
|
|
|
public TimerCachee(TimeSpan expirationPeriod, Func<string, T> factory, Action<T> onDisposeAction, int capacity = 512)
|
|
|
|
|
{
|
|
|
|
|
_factory = factory;
|
|
|
|
|
_onDisposeAction = onDisposeAction;
|
|
|
|
|
_sheduller = Sheduller.Create();
|
|
|
|
|
_cachee = new Dictionary<string, CacheeItem<T>>(capacity);
|
|
|
|
|
_expirationPeriod = expirationPeriod;
|
|
|
|
|
var ts = TimeSpan.FromSeconds(60);
|
|
|
|
|
if (ts.TotalSeconds > expirationPeriod.TotalSeconds)
|
|
|
|
|
{
|
|
|
|
|
ts = expirationPeriod;
|
|
|
|
|
}
|
|
|
|
|
_sheduller.RemindEvery(ts, _ => CheckAndCleacnCachee());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public T Get(string key)
|
|
|
|
|
{
|
|
|
|
|
lock (_cacheeLock)
|
|
|
|
|
{
|
|
|
|
|
if (_cachee.TryGetValue(key, out var v))
|
|
|
|
|
{
|
|
|
|
|
v.LastAcessTime = DateTime.UtcNow;
|
|
|
|
|
return v.Value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var obj = _factory.Invoke(key);
|
|
|
|
|
var item = new CacheeItem<T> { Value = obj, LastAcessTime = DateTime.UtcNow };
|
|
|
|
|
lock (_cacheeLock)
|
|
|
|
|
{
|
|
|
|
|
_cachee[key] = item;
|
|
|
|
|
}
|
|
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Drop(string key)
|
|
|
|
|
{
|
|
|
|
|
lock (_cacheeLock)
|
|
|
|
|
{
|
|
|
|
|
if (_cachee.TryGetValue(key, out var v))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_onDisposeAction != null)
|
|
|
|
|
{
|
|
|
|
|
_onDisposeAction.Invoke(v.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Log.SystemError(ex, $"[TimerCachee.Drop] Key '{key}'. Fault dispose.");
|
|
|
|
|
}
|
|
|
|
|
_cachee.Remove(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CheckAndCleacnCachee()
|
|
|
|
|
{
|
|
|
|
|
lock (_cacheeLock)
|
|
|
|
|
{
|
|
|
|
|
var keysToRemove = new List<string>(_cachee.Count);
|
|
|
|
|
foreach (var pair in _cachee)
|
|
|
|
|
{
|
|
|
|
|
if ((DateTime.UtcNow - pair.Value.LastAcessTime) > _expirationPeriod)
|
|
|
|
|
{
|
|
|
|
|
keysToRemove.Add(pair.Key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
foreach (var key in keysToRemove)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_onDisposeAction != null)
|
|
|
|
|
{
|
|
|
|
|
_onDisposeAction.Invoke(_cachee[key].Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Log.SystemError(ex, $"[TimerCachee.CheckAndCleacnCachee] Key '{key}'");
|
|
|
|
|
}
|
|
|
|
|
_cachee.Remove(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DropAll()
|
|
|
|
|
{
|
|
|
|
|
lock (_cacheeLock)
|
|
|
|
|
{
|
|
|
|
|
foreach (var pair in _cachee)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_onDisposeAction != null)
|
|
|
|
|
{
|
|
|
|
|
_onDisposeAction.Invoke(pair.Value.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Log.SystemError(ex, $"[TimerCachee.DropAll] Key '{pair.Key}'");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_cachee.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
_sheduller?.Clean();
|
|
|
|
|
_sheduller?.Dispose();
|
|
|
|
|
DropAll();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|