Skip to main content
The rate limit request path turns a (workspace, namespace, identifier, duration) tuple into a sliding-window decision. The normal path is local-memory first and reaches regional state when a counter is cold, stale, or in strict mode.

Single request flow

Each request builds two counter keys: the current fixed window cell and the previous fixed window cell. The previous cell is needed because the public behavior is a sliding window. The compare-and-swap loop protects the local counter from concurrent accepted requests in the same process. If another goroutine changes the current counter between the read and the commit, the request recomputes the effective count before trying again.

Sliding-window math

The implementation stores fixed window cells, but the decision behaves like a sliding window. For a request at time t, the current sequence is:
The current cell contributes its full count. The previous cell contributes only the fraction that still overlaps the sliding window.
The weight starts near 1 at the beginning of a new fixed window and moves toward 0 as the current fixed window advances.
This prevents a caller from using the full limit at the end of one fixed window and immediately using the full limit again at the start of the next one.

Counter entries

counterEntry is the in-memory state for one window cell. It is intentionally small because it sits on the request path. The key invariant is that regional count and global count stay separate. Regional count can be published outward. Global count is imported from other regions and must not be published again.

Batch requests

RatelimitMany evaluates multiple limits with all-or-nothing semantics. The method temporarily increments each requested counter, evaluates the full batch, then either keeps every increment or rolls every increment back. Global publishing reads regional count minus speculative count. That prevents temporary batch state from leaking into cross-region convergence before the batch is committed.