Published Build3 min read
A Go Rate Limiter That Argues the Algorithm Was Never the Problem
distlimit publishes zero-allocation benchmarks and a single-probe Redis recovery path.
Written for builders.See today for builders

What happened
- A dev.to post announces distlimit, an open-source, distributed rate-limiting library for Go, written by the post's author.
- The author states that many existing Go rate-limiting libraries suffer three major issues: global lock contention from single sync.Mutex architectures causing bottleneck spikes during concurrent surges; garbage collection pressure from heap allocations on every limit evaluation; and cascading outages, where applications crash or block when Redis goes down and are hit by a thundering herd when Redis recovers.
- Published benchmark output: BenchmarkSlidingLog_EvaluateMemory-2 20.36 ns/op 0 B/op 0 allocs/op; BenchmarkTokenBucket_EvaluateMemory-2 46.18 ns/op 0 B/op 0 allocs/op; BenchmarkLeakyBucket_EvaluateMemory-2 47.76 ns/op 0 B/op 0 allocs/op; BenchmarkFixedWindow_EvaluateMemory-2 77.27 ns/op 0 B/op 0 allocs/op; BenchmarkSlidingCounter_EvaluateMemory-2 129.10 ns/op 0 B/op 0 allocs/op.
- Every published benchmark name contains EvaluateMemory, and the post gives no latency or allocation figures for the Redis-backed path.
- The benchmark names as printed in the post end with the suffix -2, and the post does not state what hardware or configuration produced the results.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
A Go developer has published distlimit, an open-source distributed rate limiter, along with benchmark output claiming zero heap allocations per limit evaluation and a hybrid driver that falls back from Redis to in-process memory behind a half-open circuit breaker [1][3][8]. The notable part is not the algorithm menu, which runs to five interchangeable options, but the argument the design makes: limiter libraries fail on concurrency and recovery, not on which counting scheme you picked [13][2].
The author names three failure modes in existing libraries: a single sync.Mutex creating a bottleneck under concurrent surges, heap allocations on every evaluation feeding GC pauses, and cascading outages when Redis drops and then a thundering herd when it comes back [2]. All three are operational, not mathematical. The framing scenario is a service at 100k requests per second, a transient Redis hiccup, and a spoofed X-Forwarded-For header [15].
The published numbers: sliding window log at 20.36 ns/op, token bucket at 46.18, leaky bucket at 47.76, fixed window at 77.27, sliding window counter at 129.10, all at 0 B/op and 0 allocs/op [3]. Two things about that table. The benchmark names all end in EvaluateMemory, so what is measured is the in-memory evaluation path, not a round trip to Redis, and the post publishes no latency figures for the distributed path at all [4]. And the names as printed end in -2, with no statement of what machine produced them [5]. Zero allocations is the durable claim here; the nanosecond figures are only comparable to themselves.
Inside that table the ordering is worth noting. Fixed window, described as an ultra-fast counter reset, benchmarks 1.67 times slower than token bucket [13][3][2], and the spread between fastest and slowest algorithm is roughly 6.3x [1]. If you are picking an algorithm for throughput reasons, the ranking is not the one the descriptions imply.
Contention is handled by splitting the in-memory map into 64 shards, indexed by FNV-1a of the key modulo 64 [6]. The post claims up to 64x higher concurrent throughput [7]. That number is the shard count [3], which makes it an arithmetic ceiling under perfectly uniform key distribution, not a measurement. Real key sets are skewed; one hot tenant lands in one shard.
The recovery path is the more interesting engineering. After a cool-off, an atomic compare-and-swap lets exactly one request probe Redis while everything else keeps running on the memory fallback until Redis is confirmed healthy [9]. That is the correct shape for herd suppression. What the post does not quantify is what your limit actually means during the fallback window, when enforcement is per process rather than shared [10]. A 100-per-minute limit across ten pods on local memory is not a 100-per-minute limit.
Two smaller details do real work: middleware refuses to trust X-Forwarded-For or X-Real-IP unless the hop matches CIDR rules passed to WithTrustedProxies [11], and keys are wrapped in Redis hash tags as distlimit:{key} to avoid CROSSSLOT errors on cluster [12]. Both are the kind of thing you otherwise discover in production.
Watch for benchmarks on the Redis path with contention and stated hardware, and for a documented statement of enforcement semantics during fallback. The repository is github.com/balramadan/distlimit [14].
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
A dev.to post announces distlimit, an open-source, distributed rate-limiting library for Go, written by the post's author.
- [2]
The author states that many existing Go rate-limiting libraries suffer three major issues: global lock contention from single sync.Mutex architectures causing bottleneck spikes during concurrent surges; garbage collection pressure from heap allocations on every limit evaluation; and cascading outages, where applications crash or block when Redis goes down and are hit by a thundering herd when Redis recovers.
- [3]
Published benchmark output: BenchmarkSlidingLog_EvaluateMemory-2 20.36 ns/op 0 B/op 0 allocs/op; BenchmarkTokenBucket_EvaluateMemory-2 46.18 ns/op 0 B/op 0 allocs/op; BenchmarkLeakyBucket_EvaluateMemory-2 47.76 ns/op 0 B/op 0 allocs/op; BenchmarkFixedWindow_EvaluateMemory-2 77.27 ns/op 0 B/op 0 allocs/op; BenchmarkSlidingCounter_EvaluateMemory-2 129.10 ns/op 0 B/op 0 allocs/op.
- [4]
Every published benchmark name contains EvaluateMemory, and the post gives no latency or allocation figures for the Redis-backed path.
- [5]
The benchmark names as printed in the post end with the suffix -2, and the post does not state what hardware or configuration produced the results.
- [6]
Instead of a single global lock, distlimit divides the in-memory map into 64 independent shards, with shard_index = FNV-1a(key) mod 64.
Sources & coverage · 1 publisher
The reporting this story was synthesized from, earliest first. Every link goes to the original.
- dev.toIqbal RamadanAug 13Building a Zero-Allocation, Nanosecond Distributed Rate Limiter in Go
Cited in this coverage: dev.to post by balramadan31

