Skip to content

v1.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 15 Aug 14:26
· 16 commits to main since this release

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::LRUCache provides no built-in synchronization of its own.
  • Deterministic testing support: allow() accepts an explicit now timestamp, 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 from get(), 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.