The finale runs a single scripted request stream through the fixed-window, sliding-window-counter, and token-bucket limiters at once and asserts each one's exact allow/deny timeline. The three agree on the easy part and split precisely at the window boundary - the whole project in one comparison.
Replay one timestamp stream through three limiters and assert where they agree and diverge.
This is the comparison the whole project was built to make. One request stream - three at the end of a window (tick 9), three at the very start of the next (tick 10)
[true, true, true].At the boundary they diverge, exactly as designed. The fixed window resets
at tick 10 and hands out a whole fresh allowance, admitting all six requests - the 2x
boundary burst from chapter two, still there. The sliding counter refuses: at the
start of window 1 its overlap term is 1.0, so the previous window’s three requests
still count in full and the estimate sits at the limit. The token bucket also
refuses: after draining at tick 9, only 1 * 0.3 = 0.3 of a token has refilled by
tick 10, far short of the one a request needs. Two different mechanisms, the same
correct answer - and a clean picture of why the fixed window is the cheap-but-flawed
option and the other two are what real systems run. From a virtual clock and a
count-against-a-limit test, you have built the honest core of a production rate
limiter. That is a real rate limiter, and it is yours.
stream := []int64{9, 9, 9, 10, 10, 10}fw := Replay(&FixedWindow{limit: 3, window: 10}, stream)sc := Replay(&SlidingCounter{limit: 3, window: 10}, stream)tb := Replay(NewTokenBucket(3, 0.3), stream)// fw == [T T T T T T]; sc == [T T T F F F]; tb == [T T T F F F]