1
1
using System ;
2
2
using System . Collections . Concurrent ;
3
3
using System . Collections . Generic ;
4
+ using System . Collections . Immutable ;
4
5
using System . Linq ;
5
6
using System . Threading ;
6
7
using System . Threading . Tasks ;
7
8
using Cogworks . Essentials . Constants ;
9
+ using Cogworks . Essentials . EventArgs ;
8
10
using Cogworks . Essentials . Extensions ;
9
11
using Cogworks . Essentials . Services . Interfaces ;
10
12
using Microsoft . Extensions . Caching . Memory ;
@@ -13,10 +15,10 @@ namespace Cogworks.Essentials.Services
13
15
{
14
16
public class MemoryCacheService : ICacheService , IDisposable
15
17
{
16
- private const string CacheKeyList = "CacheKeyList" ;
17
-
18
18
private readonly IMemoryCache _memoryCache ;
19
19
20
+ private ImmutableHashSet < string > _cacheKeys = ImmutableHashSet < string > . Empty ;
21
+
20
22
private static ConcurrentDictionary < object , SemaphoreSlim > Locks => new ConcurrentDictionary < object , SemaphoreSlim > ( ) ;
21
23
22
24
public MemoryCacheService ( IMemoryCache memoryCache )
@@ -34,10 +36,7 @@ public T GetCacheItem<T>(string cacheKey)
34
36
: default ;
35
37
36
38
public void RemoveCacheItem ( string cacheKey )
37
- {
38
- RemoveCacheKeyList ( cacheKey ) ;
39
- _memoryCache . Remove ( cacheKey ) ;
40
- }
39
+ => _memoryCache . Remove ( cacheKey ) ;
41
40
42
41
public void AddCacheItem ( string cacheKey , object value , int ? cacheDurationInSeconds = null )
43
42
{
@@ -46,7 +45,11 @@ public void AddCacheItem(string cacheKey, object value, int? cacheDurationInSeco
46
45
cacheDurationInSeconds ??= DateTimeConstants . TimeInSecondsConstants . Hour ;
47
46
var cacheDurationDateTime = DateTime . UtcNow . AddSeconds ( cacheDurationInSeconds . Value ) ;
48
47
49
- _memoryCache . Set ( cacheKey , value , cacheDurationDateTime ) ;
48
+ var entryOptions = new MemoryCacheEntryOptions ( )
49
+ . SetAbsoluteExpiration ( cacheDurationDateTime )
50
+ . RegisterPostEvictionCallback ( CacheCallback ) ;
51
+
52
+ _memoryCache . Set ( cacheKey , value , entryOptions ) ;
50
53
}
51
54
52
55
public T GetOrAddCacheItem < T > ( string cacheKey , Func < T > getValueFunction , int ? cacheDurationInSeconds = null )
@@ -57,6 +60,12 @@ public T GetOrAddCacheItem<T>(string cacheKey, Func<T> getValueFunction, int? ca
57
60
var cacheDurationDateTime = DateTime . UtcNow . AddSeconds ( cacheDurationInSeconds . Value ) ;
58
61
59
62
entry . AbsoluteExpiration = cacheDurationDateTime ;
63
+
64
+ entry . PostEvictionCallbacks . Add ( new PostEvictionCallbackRegistration ( )
65
+ {
66
+ EvictionCallback = CacheCallback
67
+ } ) ;
68
+
60
69
AddCacheKeyList ( cacheKey ) ;
61
70
62
71
return getValueFunction ( ) ;
@@ -91,7 +100,11 @@ public async Task<T> GetOrAddCacheItemAsync<T>(string cacheKey, Func<Task<T>> ge
91
100
cacheDurationInSeconds ??= DateTimeConstants . TimeInSecondsConstants . Hour ;
92
101
var cacheDurationDateTime = DateTime . UtcNow . AddSeconds ( cacheDurationInSeconds . Value ) ;
93
102
94
- _memoryCache . Set ( cacheKey , cacheEntry , cacheDurationDateTime ) ;
103
+ var entryOptions = new MemoryCacheEntryOptions ( )
104
+ . SetAbsoluteExpiration ( cacheDurationDateTime )
105
+ . RegisterPostEvictionCallback ( CacheCallback ) ;
106
+
107
+ _memoryCache . Set ( cacheKey , cacheEntry , entryOptions ) ;
95
108
}
96
109
}
97
110
finally
@@ -105,7 +118,7 @@ public async Task<T> GetOrAddCacheItemAsync<T>(string cacheKey, Func<Task<T>> ge
105
118
106
119
public void ClearAllStartingWith ( string prefixKey )
107
120
{
108
- var cacheKeys = GetOrAddCacheKeyList ( )
121
+ var cacheKeys = _cacheKeys
109
122
. Where ( x => x . StartsWith ( prefixKey ) )
110
123
. ToList ( ) ;
111
124
@@ -118,15 +131,13 @@ public void ClearAllStartingWith(string prefixKey)
118
131
{
119
132
_memoryCache . Remove ( key ) ;
120
133
}
121
-
122
- RemoveCacheKeyList ( cacheKeys ) ;
123
134
}
124
135
125
136
public void ClearAll ( )
126
137
{
127
- var cacheKeys = GetOrAddCacheKeyList ( ) ;
138
+ var cacheKeys = _cacheKeys . ToArray ( ) ;
128
139
129
- if ( ! cacheKeys . HasAny ( ) )
140
+ if ( ! _cacheKeys . HasAny ( ) )
130
141
{
131
142
return ;
132
143
}
@@ -135,55 +146,43 @@ public void ClearAll()
135
146
{
136
147
_memoryCache . Remove ( key ) ;
137
148
}
138
-
139
- RemoveCacheKeyList ( cacheKeys ) ;
140
149
}
141
150
142
- public void Dispose ( )
143
- => _memoryCache . Dispose ( ) ;
151
+ public IEnumerable < string > GetKeys ( )
152
+ => _cacheKeys . ToList ( ) ;
144
153
145
- private void AddCacheKeyList ( string cacheKey )
154
+ public void Dispose ( )
146
155
{
147
- var cacheKeyList = GetOrAddCacheKeyList ( ) ;
148
-
149
- cacheKeyList . AddUnique ( cacheKey ) ;
150
-
151
- var cacheDurationDateTime = DateTime . UtcNow . AddSeconds ( DateTimeConstants . TimeInSecondsConstants . Year ) ;
152
-
153
- _memoryCache . Set ( CacheKeyList , cacheKeyList , cacheDurationDateTime ) ;
156
+ _cacheKeys = _cacheKeys . Clear ( ) ;
157
+ _memoryCache . Dispose ( ) ;
154
158
}
155
159
156
- private void RemoveCacheKeyList ( string cacheKey )
157
- {
158
- var cacheKeyList = GetOrAddCacheKeyList ( ) ;
159
-
160
- cacheKeyList . Remove ( cacheKey ) ;
160
+ private void AddCacheKeyList ( string cacheKey )
161
+ => ImmutableInterlocked . Update (
162
+ ref _cacheKeys ,
163
+ ( collection , item ) => collection . Add ( item ) ,
164
+ cacheKey ) ;
161
165
162
- UpdateCacheKeyList ( cacheKeyList ) ;
163
- }
166
+ private void RemoveCacheKeyList ( string cacheKey )
167
+ => ImmutableInterlocked . Update (
168
+ ref _cacheKeys ,
169
+ ( collection , item ) => collection . Remove ( item ) ,
170
+ cacheKey ) ;
164
171
165
- private void RemoveCacheKeyList ( IEnumerable < string > toBeRemovedItems )
172
+ private void CacheCallback ( object key , object value , EvictionReason reason , object state )
166
173
{
167
- var cacheKeys = GetOrAddCacheKeyList ( ) ;
174
+ if ( reason == EvictionReason . Replaced || key is not string cacheKey )
175
+ {
176
+ return ;
177
+ }
168
178
169
- cacheKeys = cacheKeys . Except ( toBeRemovedItems ) . ToList ( ) ;
179
+ CacheEvictionEvent ? . Invoke (
180
+ this ,
181
+ new CacheEvictionArgs ( key , value , reason ) ) ;
170
182
171
- UpdateCacheKeyList ( cacheKeys ) ;
183
+ RemoveCacheKeyList ( cacheKey ) ;
172
184
}
173
185
174
- private void UpdateCacheKeyList ( IEnumerable < string > cacheKeys )
175
- => _memoryCache . Set (
176
- CacheKeyList ,
177
- cacheKeys ,
178
- DateTime . UtcNow . AddSeconds ( DateTimeConstants . TimeInSecondsConstants . Year ) ) ;
179
-
180
- private List < string > GetOrAddCacheKeyList ( )
181
- => _memoryCache . GetOrCreate ( CacheKeyList , entry =>
182
- {
183
- var cacheDurationDateTime = DateTime . UtcNow . AddSeconds ( DateTimeConstants . TimeInSecondsConstants . Year ) ;
184
- entry . AbsoluteExpiration = cacheDurationDateTime ;
185
-
186
- return new List < string > ( ) ;
187
- } ) ;
186
+ public event EventHandler < CacheEvictionArgs > CacheEvictionEvent ;
188
187
}
189
188
}
0 commit comments