@@ -13,6 +13,9 @@ internal class RedisFixedWindowManager
1313
1414 private static readonly LuaScript _redisScript = LuaScript . Prepare (
1515 @"local expires_at = tonumber(redis.call(""get"", @expires_at_key))
16+ local current = tonumber(redis.call(""get"", @rate_limit_key))
17+ local requested = tonumber(@increment_amount)
18+ local limit = tonumber(@permit_limit)
1619
1720 if not expires_at or expires_at < tonumber(@current_time) then
1821 -- this is either a brand new window,
@@ -27,13 +30,19 @@ internal class RedisFixedWindowManager
2730 redis.call(""expireat"", @expires_at_key, @next_expires_at + 1)
2831 -- since the database was updated, return the new value
2932 expires_at = @next_expires_at
33+ current = 0
3034 end
3135
32- -- now that the window either already exists or it was freshly initialized,
33- -- increment the counter(`incrby` returns a number)
34- local current = redis.call(""incrby"", @rate_limit_key, @increment_amount)
36+ local allowed = current + requested <= limit
3537
36- return { current, expires_at }" ) ;
38+ if allowed
39+ then
40+ -- now that the window either already exists or it was freshly initialized,
41+ -- increment the counter(`incrby` returns a number)
42+ current = redis.call(""incrby"", @rate_limit_key, @increment_amount)
43+ end
44+
45+ return { current, expires_at, allowed }" ) ;
3746
3847 public RedisFixedWindowManager (
3948 string partitionKey ,
@@ -46,7 +55,7 @@ public RedisFixedWindowManager(
4655 RateLimitExpireKey = new RedisKey ( $ "rl:{{{partitionKey}}}:exp") ;
4756 }
4857
49- internal async Task < RedisFixedWindowResponse > TryAcquireLeaseAsync ( )
58+ internal async Task < RedisFixedWindowResponse > TryAcquireLeaseAsync ( int permitCount )
5059 {
5160 var now = DateTimeOffset . UtcNow ;
5261 var nowUnixTimeSeconds = now . ToUnixTimeSeconds ( ) ;
@@ -59,9 +68,10 @@ internal async Task<RedisFixedWindowResponse> TryAcquireLeaseAsync()
5968 {
6069 rate_limit_key = RateLimitKey ,
6170 expires_at_key = RateLimitExpireKey ,
71+ permit_limit = _options . PermitLimit ,
6272 next_expires_at = now . Add ( _options . Window ) . ToUnixTimeSeconds ( ) ,
6373 current_time = nowUnixTimeSeconds ,
64- increment_amount = 1D ,
74+ increment_amount = permitCount ,
6575 } ) ;
6676
6777 var result = new RedisFixedWindowResponse ( ) ;
@@ -70,13 +80,14 @@ internal async Task<RedisFixedWindowResponse> TryAcquireLeaseAsync()
7080 {
7181 result . Count = ( long ) response [ 0 ] ;
7282 result . ExpiresAt = ( long ) response [ 1 ] ;
83+ result . Allowed = ( bool ) response [ 2 ] ;
7384 result . RetryAfter = TimeSpan . FromSeconds ( result . ExpiresAt - nowUnixTimeSeconds ) ;
7485 }
7586
7687 return result ;
7788 }
7889
79- internal RedisFixedWindowResponse TryAcquireLease ( )
90+ internal RedisFixedWindowResponse TryAcquireLease ( int permitCount )
8091 {
8192 var now = DateTimeOffset . UtcNow ;
8293 var nowUnixTimeSeconds = now . ToUnixTimeSeconds ( ) ;
@@ -89,9 +100,10 @@ internal RedisFixedWindowResponse TryAcquireLease()
89100 {
90101 rate_limit_key = RateLimitKey ,
91102 expires_at_key = RateLimitExpireKey ,
103+ permit_limit = _options . PermitLimit ,
92104 next_expires_at = now . Add ( _options . Window ) . ToUnixTimeSeconds ( ) ,
93105 current_time = nowUnixTimeSeconds ,
94- increment_amount = 1D ,
106+ increment_amount = permitCount ,
95107 } ) ;
96108
97109 var result = new RedisFixedWindowResponse ( ) ;
@@ -100,6 +112,7 @@ internal RedisFixedWindowResponse TryAcquireLease()
100112 {
101113 result . Count = ( long ) response [ 0 ] ;
102114 result . ExpiresAt = ( long ) response [ 1 ] ;
115+ result . Allowed = ( bool ) response [ 2 ] ;
103116 result . RetryAfter = TimeSpan . FromSeconds ( result . ExpiresAt - nowUnixTimeSeconds ) ;
104117 }
105118
@@ -112,5 +125,6 @@ internal class RedisFixedWindowResponse
112125 internal long ExpiresAt { get ; set ; }
113126 internal TimeSpan RetryAfter { get ; set ; }
114127 internal long Count { get ; set ; }
128+ internal bool Allowed { get ; set ; }
115129 }
116130}
0 commit comments