Skip to content

server.max_rate_limit_cache_size has no upper bound, so a large value OOM/aborts at startup when the sharded RateLimiter preallocates its LRU stripes #323

Description

@erskingardner

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-1093max_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::newLruCache::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).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions