RateThrottle v1.0.0
The first stable release of RateThrottle, a framework-agnostic fixed-window rate limiter for modern C++, backed by CachePro::LRUCache.
Highlights
- Fixed-window rate limiting: per-key request count and window-start timestamp, tracked via
CachePro::LRUCache. - Framework-agnostic core — no knowledge of HTTP, requests, or middleware;
RateLimiter::allow(key, now)is the entire surface. - Constructor-configurable limits: requests-per-window, window duration, and cache capacity are runtime parameters, not hardcoded constants.
- Thread-safe by design: all cache access guarded by an internal mutex, since
CachePro::LRUCacheprovides no built-in synchronization of its own. - Deterministic testing support:
allow()accepts an explicitnowtimestamp, so window-boundary behavior can be tested without real time passing. - Documented, accepted v1 trade-off: a client can burst up to ~2x the configured limit at a window boundary (fixed-window algorithm, not sliding-window or token-bucket).
- Documented LRU-eviction interaction: an evicted-but-still-active key's window silently restarts on its next request, rather than being hidden as an edge case.
Performance
The implementation favors simplicity and a small surface over the pool-allocated, open-addressing design of CachePro::LRUCache itself — RateLimiter is a thin, mutex-guarded layer on top of it, not a from-scratch data structure.
- A single global mutex guards the whole cache rather than sharding per key — see the Access benchmark category for the resulting contention cost under concurrent load on a shared key.
- The increment path (
allow()on an existing key, within its window) mutates the stored window in place via a live pointer fromget(), with no re-insertion into the cache. - Capacity is fixed at construction; there is no
resize()-family API, so per-call cost as capacity scales is measured by comparing limiters constructed at different fixed capacities (see the Scaling category).
Benchmarks
RateThrottle has no naive baseline to compare against — there is no stdThrottle, so figures below are RateLimiter measured alone. Full results, all three iteration tiers: benchmarks/results/v1_0_0.md.
| Operation | Tier | RateThrottle |
|---|---|---|
| Allow() Window Reset | 1M | 28.41 ms |
| Construction | 100 | 35.72 ms |
| Allow() Within Window | 1M | 49.92 ms |
| Allow() Deny | 1M | 50.00 ms |
| Allow() Small Capacity | 1M | 115.87 ms |
| Allow() Eviction Pressure | 1M | 131.94 ms |
| Allow() New Key | 1M | 198.63 ms |
| Allow() Large Capacity | 1M | 212.54 ms |
| Allow() Contention | 1M | 691.20 ms |
The branch taken (new window vs. increment vs. deny) turns out not to be the dominant cost — Within Window, Deny, and Window Reset all reuse the same fixed key every call and land in the same ~28–50 ms band regardless of which branch they exercise. What actually separates the fast group from the slow group is whether each call constructs and hashes a brand-new unique key: New Key, Large Capacity, and Eviction Pressure all do, and all cost 4–7x more.
Large Capacity (1,000,000 slots) costs more than Small Capacity (100 slots) despite doing less eviction work — the small table stays cache-resident, while the large one doesn't, so memory locality dominates over algorithmic eviction cost. Contention is the clear outlier: a single global mutex guarding the whole cache, hammered by four background threads on the same key, is by far the most expensive path measured — a direct, honest cost of not sharding the lock per key in v1.
Testing
The project includes a test suite covering:
- Allow/deny transitions across a window boundary
- Boundary-burst behavior (proven to occur, not prevented — a known, accepted v1 trade-off)
- Window reset after expiry
- LRU-eviction-mid-window edge case (evicted-but-active key's window restarting)
- Concurrent access from multiple threads
Code Coverage
Measured with LCOV 2.0-1 (coverage.filtered.info, generated 2026-08-15):
- 100.0% line coverage (17/17)
- 100.0% function coverage (2/2)
Coverage reports exclude test infrastructure and third-party dependencies, focusing on the RateThrottle library implementation.
Continuous Integration
Automated builds and tests are configured for:
- GCC — Debug
- GCC — Release
- Clang — Debug
- Clang — Release
- MSVC — Debug
- MSVC — Release
- AppleClang — Debug
- AppleClang — Release
Release
This release represents the first stable version of RateThrottle and establishes the initial API, fixed-window algorithm design, testing infrastructure, and cross-platform CI pipeline.
Installation
Clone the repository and integrate RateThrottle into your C++ project using the provided CMake configuration.
See the project documentation for build instructions, API usage, and integration details.