From a6361a21941b18241f79f25bf4954c5cb7ed7fbe Mon Sep 17 00:00:00 2001 From: l Date: Mon, 1 Jun 2026 07:51:33 +0000 Subject: [PATCH] fix: switch ratelimiter to MonotonicClock (#167) There is a known bug that `quanta` clock used by default sometimes jumps back: . 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 --- filtermail/Cargo.lock | 61 -------------------------------------- filtermail/Cargo.toml | 5 +++- filtermail/src/outbound.rs | 22 ++++++++++++-- 3 files changed, 23 insertions(+), 65 deletions(-) diff --git a/filtermail/Cargo.lock b/filtermail/Cargo.lock index 4ae81fe6..4c547417 100644 --- a/filtermail/Cargo.lock +++ b/filtermail/Cargo.lock @@ -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" diff --git a/filtermail/Cargo.toml b/filtermail/Cargo.toml index 981f4eb9..2d826982 100644 --- a/filtermail/Cargo.toml +++ b/filtermail/Cargo.toml @@ -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" diff --git a/filtermail/src/outbound.rs b/filtermail/src/outbound.rs index d6e32fc5..cacf6e52 100644 --- a/filtermail/src/outbound.rs +++ b/filtermail/src/outbound.rs @@ -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, - send_rate_limiter: DefaultKeyedRateLimiter, + + // 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: + // + send_rate_limiter: RateLimiter< + String, + governor::state::keyed::DashMapStateStore, + MonotonicClock, + NoOpMiddleware, + >, smtp_connection_pool: Arc, } @@ -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(), }) }