From: Konstantin Zangerle Date: Mon, 2 Feb 2026 13:38:46 +0000 (+0100) Subject: fix dynamic bucket-specific rate-limits X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=191df9e2cb97e0f877a56b6bfdaef62196304e31;p=thirdparty%2Frspamd.git fix dynamic bucket-specific rate-limits --- diff --git a/lualib/plugins/ratelimit.lua b/lualib/plugins/ratelimit.lua index 0b674f9c1e..53c3e16cd5 100644 --- a/lualib/plugins/ratelimit.lua +++ b/lualib/plugins/ratelimit.lua @@ -110,6 +110,12 @@ local bucket_schema = T.table({ symbol = T.string():optional():doc({ summary = "Custom symbol name" }), message = T.string():optional():doc({ summary = "Custom reject message" }), skip_soft_reject = T.boolean():optional():doc({ summary = "Skip soft reject" }), + ham_factor_rate = T.number():optional():doc({ summary = "Bucket-specific ham_factor_rate"}), + spam_factor_rate = T.number():optional():doc({ summary = "Bucket-specific spam_factor_rate"}), + ham_factor_burst = T.number():optional():doc({ summary = "Bucket-specific ham_factor_burst"}), + spam_factor_burst = T.number():optional():doc({ summary = "Bucket-specific spam_factor_burst"}), + max_rate_mult = T.number():optional():doc({ summary = "Bucket-specific rate multiplicator limit"}), + max_bucket_mult = T.number():optional():doc({ summary = "Bucket-specific bucket multiplicator limit"}), }):doc({ summary = "Ratelimit bucket configuration" }) exports.parse_limit = function(name, data) diff --git a/src/plugins/lua/ratelimit.lua b/src/plugins/lua/ratelimit.lua index 5de71c213d..e9ef0af83e 100644 --- a/src/plugins/lua/ratelimit.lua +++ b/src/plugins/lua/ratelimit.lua @@ -244,34 +244,29 @@ local function make_prefix(redis_key, name, bucket) -- Fill defaults -- If settings.dynamic_rate_limit is false, then the default dynamic rate limits are 1.0 -- We always allow per-bucket overrides of the dyn rate limits - - local seen_specific_dyn_rate = false - - if not bucket.spam_factor_rate then - bucket.spam_factor_rate = settings.dynamic_rate_limit and settings.spam_factor_rate or 1.0 - else - seen_specific_dyn_rate = true - end - if not bucket.ham_factor_rate then - bucket.ham_factor_rate = settings.dynamic_rate_limit and settings.ham_factor_rate or 1.0 - else - seen_specific_dyn_rate = true - end - if not bucket.spam_factor_burst then - bucket.spam_factor_burst = settings.dynamic_rate_limit and settings.spam_factor_burst or 1.0 + -- print("print bucket table", table.concat(bucket, '\n')); + + local seen_specific_dyn_rate = bucket.spam_factor_rate or bucket.ham_factor_rate or bucket.spam_factor_burst or bucket.ham_factor_burst or false + bucket.specific_dyn_rate = seen_specific_dyn_rate + + if seen_specific_dyn_rate or settings.dynamic_rate_limit then + bucket.dyn_rate_enabled = true + bucket.spam_factor_rate = bucket.spam_factor_rate or settings.spam_factor_rate + bucket.ham_factor_rate = bucket.ham_factor_rate or settings.ham_factor_rate + bucket.spam_factor_burst = bucket.spam_factor_burst or settings.spam_factor_burst + bucket.ham_factor_burst = bucket.ham_factor_burst or settings.ham_factor_burst + rspamd_logger.infox('creating dynamic prefix %s, %s, %s, %s', bucket.spam_factor_rate, bucket.ham_factor_rate, bucket.spam_factor_burst, bucket.ham_factor_burst) else - seen_specific_dyn_rate = true - end - if not bucket.ham_factor_burst then - bucket.ham_factor_burst = settings.dynamic_rate_limit and settings.ham_factor_burst or 1.0 - else - seen_specific_dyn_rate = true + bucket.dyn_rate_enabled = false + bucket.spam_factor_rate = 1.0 + bucket.ham_factor_rate = 1.0 + bucket.spam_factor_burst = 1.0 + bucket.ham_factor_burst = 1.0 end - if seen_specific_dyn_rate then - -- Use if afterwards in case we don't use global dyn rates - bucket.specific_dyn_rate = true - end + bucket.max_rate_mult = bucket.max_rate_mult or settings.max_rate_mult + bucket.max_bucket_mult = bucket.max_bucket_mult or settings.max_bucket_mult + return { bucket = bucket, @@ -459,15 +454,13 @@ local function ratelimit_cb(task) bincr = 1 end - local dyn_rate_enabled = settings.dynamic_rate_limit or bucket.specific_dyn_rate - lua_util.debugm(N, task, "check limit %s:%s -> %s (%s/%s)", value.name, pr, value.hash, bucket.burst, bucket.rate) lua_redis.exec_redis_script(bucket_check_id, { key = value.hash, task = task, is_write = true }, gen_check_cb(pr, bucket, value.name, value.hash), { value.hash, tostring(now), tostring(rate), tostring(bucket.burst), - tostring(settings.expire), tostring(bincr), tostring(dyn_rate_enabled), + tostring(settings.expire), tostring(bincr), tostring(bucket.dyn_rate_enabled), tostring(settings.lfb_cache_prefix), tostring(settings.lfb_max_cache_size) }) end end @@ -556,26 +549,29 @@ local function ratelimit_update_cb(task) local mult_rate = 1.0 if verdict == 'spam' or verdict == 'junk' then - mult_burst = bucket.spam_factor_burst or 1.0 - mult_rate = bucket.spam_factor_rate or 1.0 + mult_burst = bucket.spam_factor_burst + mult_rate = bucket.spam_factor_rate elseif verdict == 'ham' then - mult_burst = bucket.ham_factor_burst or 1.0 - mult_rate = bucket.ham_factor_rate or 1.0 + mult_burst = bucket.ham_factor_burst + mult_rate = bucket.ham_factor_rate end local bincr = nrcpt if bucket.skip_recipients then bincr = 1 end - - local dyn_rate_enabled = settings.dynamic_rate_limit or bucket.specific_dyn_rate + lua_util.debugm(N, task, "run bucket_update script, verdict %s, with mult_rate %s, mult_burst %s, max_rate %s, max_bucket %s, dyn_rate_enabled %s", + verdict, + tostring(mult_rate), tostring(mult_burst), + tostring(bucket.max_rate_mult), tostring(bucket.max_bucket_mult), + tostring(bucket.dyn_rate_enabled)) lua_redis.exec_redis_script(bucket_update_id, { key = v.hash, task = task, is_write = true }, update_bucket_cb, { v.hash, tostring(now), tostring(mult_rate), tostring(mult_burst), - tostring(settings.max_rate_mult), tostring(settings.max_bucket_mult), - tostring(settings.expire), tostring(bincr), tostring(dyn_rate_enabled) }) + tostring(bucket.max_rate_mult), tostring(bucket.max_bucket_mult), + tostring(settings.expire), tostring(bincr), tostring(bucket.dyn_rate_enabled) }) end end end