@@ -52,9 +52,9 @@ const pushRequestLuaScript = `
5252local bucket_key = KEYS[1]
5353local meta_key = KEYS[2]
5454local window_seconds = tonumber(ARGV[1])
55- local current_time = tonumber(ARGV[2 ])
56- local max_requests = tonumber(ARGV[3 ])
57- local n = tonumber(ARGV[4 ])
55+ local current_time = tonumber(redis.call('TIME')[1 ])
56+ local max_requests = tonumber(ARGV[2 ])
57+ local n = tonumber(ARGV[3 ])
5858local cutoff_slice = current_time - window_seconds
5959
6060local function parse_count(value)
@@ -81,7 +81,8 @@ local count = tonumber(redis.call('HGET', meta_key, 'total_normal')) or 0
8181local over_count = tonumber(redis.call('HGET', meta_key, 'total_over')) or 0
8282local last_cleaned = tonumber(redis.call('HGET', meta_key, 'last_cleaned_second'))
8383
84- if not last_cleaned then
84+ if not last_cleaned or last_cleaned > cutoff_slice or
85+ cutoff_slice - last_cleaned > window_seconds then
8586 last_cleaned = cutoff_slice
8687 local all_fields = redis.call('HGETALL', bucket_key)
8788 count = 0
@@ -131,9 +132,8 @@ return string.format("%d:%d:%d", count, over_count, current_second_count)
131132
132133const getRequestCountLuaScript = `
133134local exact_meta_key = KEYS[1]
134- local pattern = KEYS[2]
135135local window_seconds = tonumber(ARGV[1])
136- local current_time = tonumber(ARGV[2 ])
136+ local current_time = tonumber(redis.call('TIME')[1 ])
137137local cutoff_slice = current_time - window_seconds
138138
139139local function parse_count(value)
@@ -143,11 +143,18 @@ local function parse_count(value)
143143end
144144
145145local function cleanup_meta(bucket_key, meta_key)
146+ local bucket_ttl = redis.call('PTTL', bucket_key)
147+ if bucket_ttl == -2 then
148+ redis.call('DEL', meta_key)
149+ return 0, 0
150+ end
151+
146152 local count = tonumber(redis.call('HGET', meta_key, 'total_normal')) or 0
147153 local over = tonumber(redis.call('HGET', meta_key, 'total_over')) or 0
148154 local last_cleaned = tonumber(redis.call('HGET', meta_key, 'last_cleaned_second'))
149155
150- if not last_cleaned then
156+ if not last_cleaned or last_cleaned > cutoff_slice or
157+ cutoff_slice - last_cleaned > window_seconds then
151158 last_cleaned = cutoff_slice
152159 local all_fields = redis.call('HGETALL', bucket_key)
153160 count = 0
@@ -185,32 +192,20 @@ local function cleanup_meta(bucket_key, meta_key)
185192 cutoff_slice
186193 )
187194
188- return count, over
189- end
190-
191- if exact_meta_key ~= '' then
192- local exact_bucket_key = string.gsub(exact_meta_key, ':meta$', ':buckets')
193- local count, over = cleanup_meta(exact_bucket_key, exact_meta_key)
194- local current_value = redis.call('HGET', exact_bucket_key, tostring(current_time))
195- local current_c, current_oc = parse_count(current_value)
196- return string.format("%d:%d", count + over, current_c + current_oc)
197- end
198-
199- local total = 0
200- local current_second_count = 0
201-
202- local keys = redis.call('KEYS', pattern)
203- for _, meta_key in ipairs(keys) do
204- local bucket_key = string.gsub(meta_key, ':meta$', ':buckets')
205- local count, over = cleanup_meta(bucket_key, meta_key)
206- total = total + count + over
195+ if bucket_ttl == -1 then
196+ bucket_ttl = window_seconds * 1000
197+ redis.call('PEXPIRE', bucket_key, bucket_ttl)
198+ end
199+ redis.call('PEXPIRE', meta_key, bucket_ttl)
207200
208- local current_value = redis.call('HGET', bucket_key, tostring(current_time))
209- local current_c, current_oc = parse_count(current_value)
210- current_second_count = current_second_count + current_c + current_oc
201+ return count, over
211202end
212203
213- return string.format("%d:%d", total, current_second_count)
204+ local exact_bucket_key = string.gsub(exact_meta_key, ':meta$', ':buckets')
205+ local count, over = cleanup_meta(exact_bucket_key, exact_meta_key)
206+ local current_value = redis.call('HGET', exact_bucket_key, tostring(current_time))
207+ local current_c, current_oc = parse_count(current_value)
208+ return string.format("%d:%d", count + over, current_c + current_oc)
214209`
215210
216211var (
@@ -240,20 +235,25 @@ func (r *redisRateRecord) GetRequest(
240235 return 0 , 0 , errors .New ("redis client is nil" )
241236 }
242237
243- exactMetaKey := ""
238+ if hasWildcard (keys ) {
239+ snapshots , err := r .SnapshotByPattern (ctx , duration , keys ... )
240+ if err != nil {
241+ return 0 , 0 , err
242+ }
244243
245- pattern := r .buildMetaKey (keys ... )
246- if ! hasWildcard (keys ) {
247- exactMetaKey = pattern
248- pattern = ""
244+ for _ , snapshot := range snapshots {
245+ totalCount += snapshot .TotalCount
246+ secondCount += snapshot .SecondCount
247+ }
248+
249+ return totalCount , secondCount , nil
249250 }
250251
251252 result , err := getRequestCountScript .Run (
252253 ctx ,
253254 rdb ,
254- []string {exactMetaKey , pattern },
255+ []string {r . buildMetaKey ( keys ... ) },
255256 duration .Seconds (),
256- time .Now ().Unix (),
257257 ).Text ()
258258 if err != nil {
259259 return 0 , 0 , err
@@ -297,7 +297,6 @@ func (r *redisRateRecord) PushRequest(
297297 rdb ,
298298 []string {bucketKey , metaKey },
299299 duration .Seconds (),
300- time .Now ().Unix (),
301300 overed ,
302301 n ,
303302 ).Text ()
@@ -345,26 +344,25 @@ func (r *redisRateRecord) SnapshotByPattern(
345344 return nil , errors .New ("redis client is nil" )
346345 }
347346
348- metaPattern := r .buildMetaKey (keys ... )
349- if ! hasWildcard (keys ) {
350- metaPattern = r .buildMetaKey (keys ... )
351- }
352-
353- pattern := metaPattern
347+ pattern := r .buildMetaKey (keys ... )
354348 iter := rdb .Scan (ctx , 0 , pattern , 0 ).Iterator ()
355- nowUnix := time .Now ().Unix ()
356349 windowSeconds := duration .Seconds ()
357350 snapshots := make ([]recordSnapshot , 0 )
351+ seenMetaKeys := make (map [string ]struct {})
358352
359353 for iter .Next (ctx ) {
360354 metaKey := iter .Val ()
355+ if _ , seen := seenMetaKeys [metaKey ]; seen {
356+ continue
357+ }
358+
359+ seenMetaKeys [metaKey ] = struct {}{}
361360
362361 result , err := getRequestCountScript .Run (
363362 ctx ,
364363 rdb ,
365- []string {metaKey , "" },
364+ []string {metaKey },
366365 windowSeconds ,
367- nowUnix ,
368366 ).Text ()
369367 if err != nil {
370368 return nil , err
0 commit comments