Low-probability edge case: it requires an operator to configure an absurd value — the same probability class as the already-filed #293 / #179. Filed because the field is a distinct gap from those and the fix mirrors them exactly.
Summary
ServerConfig::validate() only rejects max_rate_limit_cache_size == 0; it never imposes an upper bound. Because the rate limiter's LRU stripes are preallocated eagerly at startup, a large value OOM/aborts the process before any traffic, with no config error naming the field.
Location
src/config.rs:1090-1093 — max_rate_limit_cache_size is only zero-checked (in the zero_checked_fields list); no upper-bound check.
- Failure occurs in
src/rate_limiter.rs:310-329 (RateLimiter::new → LruCache::new(cap) per stripe).
Root cause
At startup two rate limiters are constructed (encrypted-token and device-token). Each shards max_entries across stripes so the summed stripe capacity equals max_entries exactly (rate_limiter.rs:318-325), and every stripe is built with LruCache::new(cap). The lru crate's LruCache::new eagerly allocates a HashMap::with_capacity(cap), so total preallocation is ≈ max_rate_limit_cache_size slots per limiter × 2 limiters — before any traffic. (MAX_SHARDS caps the shard count, not the total capacity.)
Trigger
TRANSPONDER_SERVER_MAX_RATE_LIMIT_CACHE_SIZE=4000000000 (or the same inline TOML). Load-time validation passes (non-zero). RateLimiter::new then tries to preallocate ~4e9 hashmap buckets twice → allocator abort / OOM kill at startup, with no config error naming the field.
Suggested fix
Add an upper-bound check for max_rate_limit_cache_size in ServerConfig::validate(), alongside the existing zero-checks — mirroring the fix applied for #293 (max_dedup_cache_size / max_tokens_per_event).
Why this is not a duplicate
#293 enumerates only max_dedup_cache_size / max_tokens_per_event (both feeding the dedup store). max_rate_limit_cache_size feeds a different structure (the sharded RateLimiter) and is not named there. #300 is specifically the dedup terminal_tokens LRU. #68 is the rate limiter's runtime worst-case timestamp growth, not startup preallocation. #119 covered the zero-value validation of this field, not the missing upper bound. #166 was the zero-coercion (now fixed with NonZeroUsize).
Low-probability edge case: it requires an operator to configure an absurd value — the same probability class as the already-filed #293 / #179. Filed because the field is a distinct gap from those and the fix mirrors them exactly.
Summary
ServerConfig::validate()only rejectsmax_rate_limit_cache_size == 0; it never imposes an upper bound. Because the rate limiter's LRU stripes are preallocated eagerly at startup, a large value OOM/aborts the process before any traffic, with no config error naming the field.Location
src/config.rs:1090-1093—max_rate_limit_cache_sizeis only zero-checked (in thezero_checked_fieldslist); no upper-bound check.src/rate_limiter.rs:310-329(RateLimiter::new→LruCache::new(cap)per stripe).Root cause
At startup two rate limiters are constructed (encrypted-token and device-token). Each shards
max_entriesacross stripes so the summed stripe capacity equalsmax_entriesexactly (rate_limiter.rs:318-325), and every stripe is built withLruCache::new(cap). Thelrucrate'sLruCache::neweagerly allocates aHashMap::with_capacity(cap), so total preallocation is ≈max_rate_limit_cache_sizeslots per limiter × 2 limiters — before any traffic. (MAX_SHARDScaps the shard count, not the total capacity.)Trigger
TRANSPONDER_SERVER_MAX_RATE_LIMIT_CACHE_SIZE=4000000000(or the same inline TOML). Load-time validation passes (non-zero).RateLimiter::newthen tries to preallocate ~4e9 hashmap buckets twice → allocator abort / OOM kill at startup, with no config error naming the field.Suggested fix
Add an upper-bound check for
max_rate_limit_cache_sizeinServerConfig::validate(), alongside the existing zero-checks — mirroring the fix applied for #293 (max_dedup_cache_size/max_tokens_per_event).Why this is not a duplicate
#293 enumerates only
max_dedup_cache_size/max_tokens_per_event(both feeding the dedup store).max_rate_limit_cache_sizefeeds a different structure (the shardedRateLimiter) and is not named there. #300 is specifically the dedupterminal_tokensLRU. #68 is the rate limiter's runtime worst-case timestamp growth, not startup preallocation. #119 covered the zero-value validation of this field, not the missing upper bound. #166 was the zero-coercion (now fixed withNonZeroUsize).