fix: switch ratelimiter to MonotonicClock (#167)

There is a known bug that `quanta` clock
used by default sometimes jumps back:
<https://github.com/metrics-rs/quanta/issues/111>.
This results in `governor` rate limiter
incorrectly rejecting the messages
even when less than the configured burst size
has been sent.

Switched to standard monotonic clock
which does not have this problem.
I tested that this fixed the problem for me,
chatmail relay test `test_exceed_rate_limit`
stopped failing randomly.

Fixes <https://github.com/chatmail/filtermail/issues/166>
This commit is contained in:
l
2026-06-01 07:51:33 +00:00
committed by GitHub
parent febfb90510
commit a6361a2194
3 changed files with 23 additions and 65 deletions
-61
View File
@@ -676,11 +676,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi 5.3.0",
"wasip2",
"wasm-bindgen",
]
[[package]]
@@ -714,13 +712,10 @@ dependencies = [
"futures-sink",
"futures-timer",
"futures-util",
"getrandom 0.3.4",
"hashbrown 0.16.1",
"nonzero_ext",
"parking_lot",
"portable-atomic",
"quanta",
"rand 0.9.2",
"smallvec",
"spinning_top",
"web-time",
@@ -1594,21 +1589,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "quanta"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7"
dependencies = [
"crossbeam-utils",
"libc",
"once_cell",
"raw-cpuid",
"wasi",
"web-sys",
"winapi",
]
[[package]]
name = "quote"
version = "1.0.43"
@@ -1711,15 +1691,6 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "raw-cpuid"
version = "11.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
@@ -2577,16 +2548,6 @@ dependencies = [
"semver",
]
[[package]]
name = "web-sys"
version = "0.3.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.1.0"
@@ -2612,22 +2573,6 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.11"
@@ -2637,12 +2582,6 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.2.1"
+4 -1
View File
@@ -19,7 +19,10 @@ mailparse = "0.16.1"
memchr = "2.8.0"
log = "0.4.29"
env_logger = "0.11.10"
governor = "0.10.4"
governor = { version = "0.10.4", default-features = false, features = [
"std",
"dashmap",
] }
viadkim = { version = "0.2.0" }
hickory-resolver = { version = "0.26.1", features = ["dnssec-ring"] }
lru = "0.17.0"
+19 -3
View File
@@ -8,7 +8,9 @@ pub use crate::smtp_server::Envelope;
use crate::smtp_server::SmtpHandler;
use crate::utils::{build_resolver, extract_address};
use async_trait::async_trait;
use governor::{DefaultKeyedRateLimiter, Quota, RateLimiter};
use governor::clock::MonotonicClock;
use governor::middleware::NoOpMiddleware;
use governor::{Quota, RateLimiter};
use hickory_resolver::TokioResolver;
use mailparse::{MailHeaderMap, parse_mail};
use std::sync::Arc;
@@ -17,7 +19,20 @@ use std::sync::Arc;
pub struct OutgoingBeforeQueueHandler {
config: Config,
dns_resolver: Arc<TokioResolver>,
send_rate_limiter: DefaultKeyedRateLimiter<String>,
// We explicitly use standard MonotonicClock here.
// governor 0.10.4 by default uses "quanta" clock
// if the feature "quanta" is enabled
// and it has a known problem
// of sometimes jumping back in time
// when moved between CPU cores:
// <https://github.com/metrics-rs/quanta/issues/111>
send_rate_limiter: RateLimiter<
String,
governor::state::keyed::DashMapStateStore<String>,
MonotonicClock,
NoOpMiddleware<std::time::Instant>,
>,
smtp_connection_pool: Arc<SmtpConnectionPool>,
}
@@ -26,10 +41,11 @@ impl OutgoingBeforeQueueHandler {
let quota = Quota::per_minute(config.max_user_send_per_minute)
.allow_burst(config.max_user_send_burst_size);
let dns_resolver = Arc::new(build_resolver()?);
let send_rate_limiter = RateLimiter::dashmap_with_clock(quota, MonotonicClock);
Ok(Self {
config,
dns_resolver,
send_rate_limiter: RateLimiter::keyed(quota),
send_rate_limiter,
smtp_connection_pool: SmtpConnectionPool::new(),
})
}