1- using DistributedCache . Dtos ;
1+ using System . Reflection ;
2+ using DistributedCache . Dtos ;
23using DistributedCache . Enums ;
4+ using DistributedCache . Helpers ;
5+ using DistributedCache . Options ;
36using DistributedCache . Services . Interfaces ;
4- using Microsoft . AspNetCore . Http ;
7+ using Microsoft . Extensions . Options ;
8+ using StackExchange . Redis . Extensions . Core . Abstractions ;
59
610namespace DistributedCache . Services . Implementations ;
711
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
1016{
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+ {
2062 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+
2365 return new RateLimitState (
24- RateLimitStatus . NotExceeded ,
25- rateLimitConfiguration . TimeToLive ,
26- rateLimitConfiguration . MaxAttempts - 1
66+ RateLimitStatus . NotExceeded ,
67+ rateLimitConfiguration . TimeToLive ,
68+ rateLimitConfiguration . MaxAttempts - 1
2769 ) ;
28- }
70+ }
2971
30- var isUpdated = cache . TryUpdateAttempts ( ) ;
31- var newExpiration = cache . GetNewExpiration ( ) ;
72+ var isUpdated = cache . TryUpdateAttempts ( ) ;
73+ var newExpiration = cache . GetNewExpiration ( ) ;
3274
33- if ( ! isUpdated )
34- {
75+ if ( ! isUpdated )
76+ {
3577 return new RateLimitState (
36- RateLimitStatus . Exceeded ,
37- newExpiration ,
38- 0
78+ RateLimitStatus . Exceeded ,
79+ newExpiration ,
80+ 0
3981 ) ;
40- }
82+ }
4183
42- await cacheService . SetAsync ( key , cache , newExpiration , null , cancellationToken ) ;
84+ await _redisDatabase . AddAsync ( key , cache , newExpiration ) ;
4385
44- return new RateLimitState (
45- RateLimitStatus . NotExceeded ,
46- newExpiration ,
86+ return new RateLimitState (
87+ RateLimitStatus . NotExceeded ,
88+ newExpiration ,
4789 cache . MaxAttempts - cache . Attempts
48- ) ;
49- }
90+ ) ;
91+ }
92+ finally
93+ {
94+ await lockService . ReleaseLockAsync ( key , lockValue ) ;
95+ }
96+ }
5097}
0 commit comments