Rate Limiter
Per-client sliding windows and explicit time
Ownership and transitions
The active interval is (now-window,now], so exactly the left boundary expires. Accepted history and observed time are separate: a denial adds no accepted event but still prevents later time regression for that client. Per-client state avoids coupling unrelated clocks. A deque can remove each accepted timestamp once; retained state scales with accepted events in active windows.
Java boundaries
Use the supplied nested records as immutable value carriers. A record is only shallowly immutable: copy every retained mutable input and every outward list/map, including lists inside records. Absence is null; validation failures use IllegalArgumentException, missing-key purchase errors use NoSuchElementException, and invalid transaction boundaries use IllegalStateException. Java primitive types already exclude booleans from numeric inputs. Numeric values use long where signatures say long and all computations must fit signed 64-bit values. Calls in these service exercises are serialized.
The left edge is excluded from a sliding window. Accepted history can remain unchanged even while the observed request time advances:
import java.util.*;
class Example {
public static void main(String[] args) {
long now = 5, window = 3;
List<Long> recent = List.of(2L, 3L, 5L);
boolean boundaryActive = recent.get(0) > now - window;
assert !boundaryActive;
assert recent.get(1) > now - window;
}
}Timestamp 2 expires exactly at now=5. Denials do not append timestamps, but must still advance the per-client clock so a later backward request is rejected.
Design boundary
Caller-supplied logical time makes boundary behavior deterministic without clocks or sleeps. A production distributed limiter would additionally need a shared authority and atomic decisions; independent in-memory instances do not enforce a common quota.
Put it into practice
Implement the exercise in a full-screen workspace, then run its tests.