1
- using DistributedCache . Dtos ;
1
+ using System . Reflection ;
2
+ using DistributedCache . Dtos ;
2
3
using DistributedCache . Enums ;
4
+ using DistributedCache . Helpers ;
5
+ using DistributedCache . Options ;
3
6
using DistributedCache . Services . Interfaces ;
4
- using Microsoft . AspNetCore . Http ;
7
+ using Microsoft . Extensions . Options ;
8
+ using StackExchange . Redis . Extensions . Core . Abstractions ;
5
9
6
10
namespace DistributedCache . Services . Implementations ;
7
11
8
- public class RedisRateLimitService ( ICacheService < RateLimitCache > cacheService , RedisLockService lockService )
9
- : IRateLimitService
12
+ public class RedisRateLimitService (
13
+ IRedisClient redisClient ,
14
+ IOptions < CacheConfigurationOptions > options ,
15
+ RedisLockService lockService ) : IRateLimitService
10
16
{
11
- public async ValueTask < RateLimitState > RateLimitAsync ( RateLimitConfiguration rateLimitConfiguration ,
12
- CancellationToken cancellationToken = default )
13
- {
14
- var key = rateLimitConfiguration . GetKey ( ) ;
15
-
16
- var cache = await cacheService . GetAsync ( key , cancellationToken ) ;
17
-
18
- if ( cache == null )
19
- {
17
+ private readonly IRedisDatabase _redisDatabase = redisClient . GetDefaultDatabase ( ) ;
18
+ private readonly CacheConfigurationOptions _config = options . Value ;
19
+
20
+ private readonly string _moduleName = Assembly . GetExecutingAssembly ( )
21
+ . FullName ! ;
22
+
23
+ public async ValueTask < RateLimitState > RateLimitAsync ( RateLimitConfiguration rateLimitConfiguration ,
24
+ CancellationToken cancellationToken = default )
25
+ {
26
+ var key = _config . KeyPrefixForIsolation == KeyPrefix . None
27
+ ? KeyFormatHelper . GetPrefixedKey ( rateLimitConfiguration . GetKey ( ) )
28
+ : KeyFormatHelper . GetPrefixedKey ( rateLimitConfiguration . GetKey ( ) , _moduleName ) ;
29
+
30
+
31
+ var lockValue = Guid . NewGuid ( )
32
+ . ToString ( ) ;
33
+
34
+ while ( true )
35
+ {
36
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
37
+
38
+ var isLocked = await lockService . CheckForLockAsync ( key ) ;
39
+
40
+ if ( isLocked )
41
+ {
42
+ await lockService . WaitForLockReleaseAsync ( key , cancellationToken ) ;
43
+ continue ;
44
+ }
45
+
46
+ var lockAcquired = await lockService . AcquireLockAsync ( key , lockValue ) ;
47
+ if ( ! lockAcquired )
48
+ {
49
+ await lockService . WaitForLockReleaseAsync ( key , cancellationToken ) ;
50
+ continue ;
51
+ }
52
+
53
+ break ;
54
+ }
55
+
56
+ try
57
+ {
58
+ var cache = await _redisDatabase . GetAsync < RateLimitCache > ( key ) ;
59
+
60
+ if ( cache is null )
61
+ {
20
62
var newCache = RateLimitCache . CreateRateLimitCache ( rateLimitConfiguration ) ;
21
- await cacheService . SetAsync ( key , newCache , rateLimitConfiguration . TimeToLive , null , cancellationToken ) ;
22
-
63
+ await _redisDatabase . AddAsync ( key , newCache , rateLimitConfiguration . TimeToLive ) ;
64
+
23
65
return new RateLimitState (
24
- RateLimitStatus . NotExceeded ,
25
- rateLimitConfiguration . TimeToLive ,
26
- rateLimitConfiguration . MaxAttempts - 1
66
+ RateLimitStatus . NotExceeded ,
67
+ rateLimitConfiguration . TimeToLive ,
68
+ rateLimitConfiguration . MaxAttempts - 1
27
69
) ;
28
- }
70
+ }
29
71
30
- var isUpdated = cache . TryUpdateAttempts ( ) ;
31
- var newExpiration = cache . GetNewExpiration ( ) ;
72
+ var isUpdated = cache . TryUpdateAttempts ( ) ;
73
+ var newExpiration = cache . GetNewExpiration ( ) ;
32
74
33
- if ( ! isUpdated )
34
- {
75
+ if ( ! isUpdated )
76
+ {
35
77
return new RateLimitState (
36
- RateLimitStatus . Exceeded ,
37
- newExpiration ,
38
- 0
78
+ RateLimitStatus . Exceeded ,
79
+ newExpiration ,
80
+ 0
39
81
) ;
40
- }
82
+ }
41
83
42
- await cacheService . SetAsync ( key , cache , newExpiration , null , cancellationToken ) ;
84
+ await _redisDatabase . AddAsync ( key , cache , newExpiration ) ;
43
85
44
- return new RateLimitState (
45
- RateLimitStatus . NotExceeded ,
46
- newExpiration ,
86
+ return new RateLimitState (
87
+ RateLimitStatus . NotExceeded ,
88
+ newExpiration ,
47
89
cache . MaxAttempts - cache . Attempts
48
- ) ;
49
- }
90
+ ) ;
91
+ }
92
+ finally
93
+ {
94
+ await lockService . ReleaseLockAsync ( key , lockValue ) ;
95
+ }
96
+ }
50
97
}
0 commit comments