🐛 Normalize fractional rate-limit reset durations (#11254)

Round bucket reset intervals up to whole milliseconds before adding them to an instant. This prevents Clojure ratios from reaching duration conversion and disabling rate limiting for the request.

Add a regression test for a refill rate that produces fractional milliseconds.

Closes #11253

AI-assisted-by: gpt-5.6-luna
This commit is contained in:
Andrey Antukh 2026-08-17 15:27:06 +02:00 committed by GitHub
parent c797656d17
commit 8acb92b782
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 31 additions and 2 deletions

View File

@ -46,6 +46,7 @@
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.math :as mth]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as uri]
@ -180,8 +181,8 @@
result (rds/eval rconn script)
allowed? (boolean (nth result 0))
remaining (nth result 1)
reset (* (/ (inst-ms interval) rate)
(- capacity remaining))]
reset (long (mth/ceil (double (* (/ (inst-ms interval) rate)
(- capacity remaining)))))]
(l/trace :hint "limit processed"
:method method
:limit (name (::name limit))

View File

@ -0,0 +1,28 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC Sucursal en Espana SL
(ns backend-tests.rpc-rlimit-test
(:require
[app.common.time :as ct]
[app.redis :as rds]
[app.rpc.rlimit :as rlimit]
[clojure.test :as t]))
(t/deftest bucket-reset-supports-fractional-milliseconds
(let [now (ct/inst 0)
limit {::rlimit/name :test
::rlimit/strategy :bucket
::rlimit/key "test"
::rlimit/method "main.test"
::rlimit/capacity 5
::rlimit/rate 3
::rlimit/interval (ct/duration 1000)
::rlimit/params [1 3 5]
::rlimit/opts "5/3/1s"}]
(with-redefs [rds/eval (fn [_ _] [true 4])]
(let [result (rlimit/process-limit nil "profile" now limit)]
(t/is (= (ct/inst 334)
(:app.rpc.rlimit.result/reset result)))))))