commit f847a98: [Feature] Improve ratelimit redis scripts
Vsevolod Stakhov
vsevolod at rspamd.com
Fri Dec 23 19:56:03 UTC 2022
Author: Vsevolod Stakhov
Date: 2022-12-23 19:44:02 +0000
URL: https://github.com/rspamd/rspamd/commit/f847a98c8e085d82be09de880b727ffcf7f6a207 (HEAD -> master)
[Feature] Improve ratelimit redis scripts
* Use multi keys calls where useful
* Carefully refine `nrcpt` usage
* Add an additional `pending` field to ratelimit bursty senders earlier
---
src/plugins/lua/ratelimit.lua | 37 ++++++++++++++++++++++---------------
1 file changed, 22 insertions(+), 15 deletions(-)
diff --git a/src/plugins/lua/ratelimit.lua b/src/plugins/lua/ratelimit.lua
index 34d6788c9..7e0afb8fa 100644
--- a/src/plugins/lua/ratelimit.lua
+++ b/src/plugins/lua/ratelimit.lua
@@ -65,27 +65,31 @@ local settings = {
-- Redis keys used:
-- l - last hit
-- b - current burst
+-- p - pending messages (those that are currently processing)
-- dr - current dynamic rate multiplier (*10000)
-- db - current dynamic burst multiplier (*10000)
local bucket_check_script = [[
local last = redis.call('HGET', KEYS[1], 'l')
local now = tonumber(KEYS[2])
+ local nrcpt = tonumber(KEYS[6])
local dynr, dynb, leaked = 0, 0, 0
if not last then
-- New bucket
- redis.call('HSET', KEYS[1], 'l', KEYS[2])
- redis.call('HSET', KEYS[1], 'b', '0')
- redis.call('HSET', KEYS[1], 'dr', '10000')
- redis.call('HSET', KEYS[1], 'db', '10000')
+ redis.call('HMSET', KEYS[1], 'l', KEYS[2], 'b', '0', 'dr', '10000', 'db', '10000', 'p', tostring(nrcpt))
redis.call('EXPIRE', KEYS[1], KEYS[5])
return {0, '0', '1', '1', '0'}
end
last = tonumber(last)
- local burst = tonumber(redis.call('HGET', KEYS[1], 'b'))
+ local burst,pending = redis.call('HMGET', KEYS[1], 'b', 'p')
+ burst,pending = tonumber(burst or '0'),tonumber(pending or '0')
+ -- Sanity to avoid races
+ if burst < 0 then burst = 0 end
+ if pending < 0 then pending = 0 end
+ pending = pending + nrcpt -- this message
-- Perform leak
- if burst > 0 then
- if last < tonumber(KEYS[2]) then
+ if burst + pending > 0 then
+ if burst > 0 and last < tonumber(KEYS[2]) then
local rate = tonumber(KEYS[3])
dynr = tonumber(redis.call('HGET', KEYS[1], 'dr')) / 10000.0
if dynr == 0 then dynr = 0.0001 end
@@ -100,12 +104,15 @@ local bucket_check_script = [[
dynb = tonumber(redis.call('HGET', KEYS[1], 'db')) / 10000.0
if dynb == 0 then dynb = 0.0001 end
+ burst = burst + pending
if burst > 0 and (burst + tonumber(KEYS[6])) > tonumber(KEYS[4]) * dynb then
- return {1, tostring(burst), tostring(dynr), tostring(dynb), tostring(leaked)}
+ return {1, tostring(burst - pending), tostring(dynr), tostring(dynb), tostring(leaked)}
end
+ -- Increase pending if we allow ratelimit
+ redis.call('HINCRBY', KEYS[1], 'p', nrcpt)
else
burst = 0
- redis.call('HSET', KEYS[1], 'b', '0')
+ redis.call('HMSET', KEYS[1], 'b', '0', 'p', tostring(nrcpt))
end
return {0, tostring(burst), tostring(dynr), tostring(dynb), tostring(leaked)}
@@ -125,17 +132,16 @@ local bucket_check_id
-- Redis keys used:
-- l - last hit
-- b - current burst
+-- p - messages pending (must be decreased by 1)
-- dr - current dynamic rate multiplier
-- db - current dynamic burst multiplier
local bucket_update_script = [[
local last = redis.call('HGET', KEYS[1], 'l')
local now = tonumber(KEYS[2])
+ local nrcpt = tonumber(KEYS[8])
if not last then
- -- New bucket
- redis.call('HSET', KEYS[1], 'l', KEYS[2])
- redis.call('HSET', KEYS[1], 'b', '1')
- redis.call('HSET', KEYS[1], 'dr', '10000')
- redis.call('HSET', KEYS[1], 'db', '10000')
+ -- New bucket (why??)
+ redis.call('HMSET', KEYS[1], 'l', KEYS[2], 'b', tostring(nrcpt), 'dr', '10000', 'db', '10000', 'p', '0')
redis.call('EXPIRE', KEYS[1], KEYS[7])
return {1, 1, 1}
end
@@ -189,7 +195,8 @@ local bucket_update_script = [[
local burst = tonumber(redis.call('HGET', KEYS[1], 'b'))
if burst < 0 then burst = 0 end
- redis.call('HINCRBYFLOAT', KEYS[1], 'b', tonumber(KEYS[8]))
+ redis.call('HINCRBYFLOAT', KEYS[1], 'b', nrcpt)
+ redis.call('HINCRBY', KEYS[1], 'p', -(nrcpt))
redis.call('HSET', KEYS[1], 'l', KEYS[2])
redis.call('EXPIRE', KEYS[1], KEYS[7])
More information about the Commits
mailing list