Skip to content

Commit a245166

Browse files
authored
feat(memory): support evicted event hook(#294) (#414)
1 parent d03cca6 commit a245166

File tree

3 files changed

+70
-12
lines changed

3 files changed

+70
-12
lines changed

src/EasyCaching.InMemory/Internal/IInMemoryCaching.cs

+12
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,17 @@ public interface IInMemoryCaching
2424
bool Replace<T>(string key, T value, TimeSpan? expiresIn = null);
2525
void Clear(string prefix = "");
2626
TimeSpan GetExpiration(string key);
27+
28+
event EventHandler<EvictedEventArgs> Evicted;
29+
}
30+
31+
public class EvictedEventArgs : EventArgs
32+
{
33+
public EvictedEventArgs(string key )
34+
{
35+
this.Key = key;
36+
}
37+
38+
public string Key { get; private set; }
2739
}
2840
}

src/EasyCaching.InMemory/Internal/InMemoryCaching.cs

+21-12
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public class InMemoryCaching : IInMemoryCaching
1717
private long _cacheSize = 0L;
1818
private const string _UPTOLIMIT_KEY = "inter_up_to_limit_key";
1919

20+
public event EventHandler<EvictedEventArgs> Evicted;
21+
2022
public InMemoryCaching(string name, InMemoryCachingOptions optionsAccessor)
2123
{
2224
ArgumentCheck.NotNull(optionsAccessor, nameof(optionsAccessor));
@@ -32,7 +34,7 @@ public InMemoryCaching(string name, InMemoryCachingOptions optionsAccessor)
3234
public void Clear(string prefix = "")
3335
{
3436
if (string.IsNullOrWhiteSpace(prefix))
35-
{
37+
{
3638
_memory.Clear();
3739

3840
if (_options.SizeLimit.HasValue)
@@ -53,9 +55,13 @@ public int GetCount(string prefix = "")
5355

5456
internal void RemoveExpiredKey(string key)
5557
{
56-
bool flag = _memory.TryRemove(key, out _);
57-
if (_options.SizeLimit.HasValue && flag)
58-
Interlocked.Decrement(ref _cacheSize);
58+
if (_memory.TryRemove(key, out _))
59+
{
60+
Evicted?.Invoke(this, new EvictedEventArgs(key));
61+
62+
if (_options.SizeLimit.HasValue)
63+
Interlocked.Decrement(ref _cacheSize);
64+
}
5965
}
6066

6167
public CacheValue<T> Get<T>(string key)
@@ -189,7 +195,7 @@ private bool SetInternal(CacheEntry entry, bool addOnly = false)
189195

190196
_memory.AddOrUpdate(deep.Key, deep, (k, cacheEntry) => deep);
191197

192-
if(_options.SizeLimit.HasValue)
198+
if (_options.SizeLimit.HasValue)
193199
Interlocked.Increment(ref _cacheSize);
194200
}
195201
}
@@ -239,7 +245,7 @@ public int RemoveAll(IEnumerable<string> keys = null)
239245
continue;
240246

241247
if (_memory.TryRemove(key, out _))
242-
{
248+
{
243249
removed++;
244250
if (_options.SizeLimit.HasValue)
245251
Interlocked.Decrement(ref _cacheSize);
@@ -254,7 +260,7 @@ public bool Remove(string key)
254260
bool flag = _memory.TryRemove(key, out _);
255261

256262
if (_options.SizeLimit.HasValue && !key.Equals(_UPTOLIMIT_KEY) && flag)
257-
{
263+
{
258264
Interlocked.Decrement(ref _cacheSize);
259265
}
260266

@@ -270,10 +276,10 @@ public int RemoveByPrefix(string prefix)
270276
public int RemoveByPattern(string searchKey, SearchKeyPattern searchPattern)
271277
{
272278
var keysToRemove = _memory.Keys.Where(x => FilterByPattern(x, searchKey, searchPattern)).ToList();
273-
279+
274280
return RemoveAll(keysToRemove);
275281
}
276-
282+
277283
private static bool FilterByPattern(string key, string searchKey, SearchKeyPattern searchKeyPattern)
278284
{
279285
switch (searchKeyPattern)
@@ -336,7 +342,10 @@ private void ScanForExpiredItems(InMemoryCaching cache)
336342
var now = SystemClock.UtcNow;
337343
foreach (var entry in cache._memory.Values.Where(x => x.ExpiresAt < now))
338344
{
339-
cache.Remove(entry.Key);
345+
if (cache.Remove(entry.Key))
346+
{
347+
Evicted?.Invoke(this, new EvictedEventArgs(entry.Key));
348+
}
340349
}
341350
}
342351

@@ -403,7 +412,7 @@ internal object Value
403412
public T GetValue<T>(bool isDeepClone = true)
404413
{
405414
object val = Value;
406-
415+
407416
var t = typeof(T);
408417

409418
if (t == TypeHelper.BoolType || t == TypeHelper.StringType || t == TypeHelper.CharType || t == TypeHelper.DateTimeType || t.IsNumeric())
@@ -412,7 +421,7 @@ public T GetValue<T>(bool isDeepClone = true)
412421
if (t == TypeHelper.NullableBoolType || t == TypeHelper.NullableCharType || t == TypeHelper.NullableDateTimeType || t.IsNullableNumeric())
413422
return val == null ? default(T) : (T)Convert.ChangeType(val, Nullable.GetUnderlyingType(t));
414423

415-
return isDeepClone
424+
return isDeepClone
416425
? DeepClonerGenerator.CloneObject<T>((T)val)
417426
: (T)val;
418427
}

test/EasyCaching.UnitTests/CachingTests/MemoryCachingProviderTest.cs

+37
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,43 @@ public void GetDatabase_Should_Succeed()
118118
Assert.NotNull(db);
119119
Assert.IsType<InMemoryCaching>(db);
120120
}
121+
122+
[Fact]
123+
public void Evicted_Event_Should_Trigger_When_StartScanForExpiredItems()
124+
{
125+
var key1 = "Evicted-111";
126+
var key2 = "Evicted-112";
127+
128+
var db = (IInMemoryCaching)_provider.Database;
129+
130+
db.Evicted += (s,e) =>
131+
{
132+
Assert.Equal(key1, e.Key);
133+
};
134+
135+
_provider.Set(key1, "111", TimeSpan.FromMilliseconds(500));
136+
_provider.Set(key2, "112", TimeSpan.FromMilliseconds(6000));
137+
138+
System.Threading.Thread.Sleep(1000);
139+
}
140+
141+
[Fact]
142+
public void Evicted_Event_Should_Trigger_When_GetExpiredItems()
143+
{
144+
var key1 = "Evicted-211";
145+
146+
var db = (IInMemoryCaching)_provider.Database;
147+
148+
db.Evicted += (s, e) =>
149+
{
150+
Assert.Equal(key1, e.Key);
151+
};
152+
153+
_provider.Set(key1, "211", TimeSpan.FromMilliseconds(500));
154+
System.Threading.Thread.Sleep(1000);
155+
_provider.Get<string>(key1);
156+
System.Threading.Thread.Sleep(500);
157+
}
121158
}
122159

123160
public class MemoryCachingProviderWithFactoryTest : BaseCachingProviderWithFactoryTest

0 commit comments

Comments
 (0)