using System; using System.Collections.Generic; using ZeroLevel.Services.Shedulling; namespace ZeroLevel.Services.Cache { internal sealed class TimerCachee : IDisposable { private sealed class CacheeItem { public T Value { get; set; } public DateTime LastAcessTime { get; set; } } private readonly IDictionary> _cachee; private readonly object _cacheeLock = new object(); private readonly ISheduller _sheduller; private readonly Func _factory; private readonly Action _onDisposeAction; private readonly TimeSpan _expirationPeriod; public TimerCachee(TimeSpan expirationPeriod, Func factory, Action onDisposeAction, int capacity = 512) { _factory = factory; _onDisposeAction = onDisposeAction; _sheduller = Sheduller.Create(); _cachee = new Dictionary>(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 { 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(_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(); } } }