]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
fix dynamic bucket-specific rate-limits
authorKonstantin Zangerle <konstantin.zangerle@kit.edu>
Mon, 2 Feb 2026 13:38:46 +0000 (14:38 +0100)
committerKonstantin Zangerle <konstantin.zangerle@kit.edu>
Mon, 2 Feb 2026 13:38:46 +0000 (14:38 +0100)
lualib/plugins/ratelimit.lua
src/plugins/lua/ratelimit.lua

index 0b674f9c1e0e385a38d503774feea0e70a98ecf5..53c3e16cd55478bbceaafde40e8af682c1478572 100644 (file)
@@ -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)
index 5de71c213d23a3e6dc7281f60faa742a64d6b647..e9ef0af83ec227f49a5b9e64e24bcdd388e39590 100644 (file)
@@ -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