Problem
A performance architecture review identified that rate limiter, cache, and session middleware all hold a global mutex while performing external storage I/O (Redis, database, etc.). This serializes all requests through these middleware regardless of key, collapsing throughput to ~1/storage-latency.
Affected middleware
| Middleware |
File |
Lock |
I/O under lock |
| Limiter (fixed) |
middleware/limiter/limiter_fixed.go:52-91 |
mux.Lock() |
manager.get() + manager.set() |
| Limiter (sliding) |
middleware/limiter/limiter_sliding.go:54-102 |
mux.Lock() |
manager.get() + manager.set() |
| Cache |
middleware/cache/cache.go:313-546 |
mux.Lock() |
storage.Get() / storage.Set() |
| Session |
middleware/session/middleware.go:144-157 |
m.mu.Lock() |
store.Get() → storage.GetWithContext() |
Impact
With a 2ms Redis round-trip:
- Effective throughput through any of these middleware caps at ~500 RPS regardless of hardware or concurrency.
- All requests for different keys/IPs serialize on a single mutex.
- At 10K QPS, this becomes the dominant bottleneck.
Proposed fix
Pattern: Per-key striped locking.
- Replace the single
sync.RWMutex with a fixed array of 32 or 64 shards, each with its own mutex.
- Hash the key (IP, cache key, session ID) to select the shard.
- Never hold any shard lock across storage I/O — lock, read/write in-memory state, unlock, then perform storage I/O without the lock, then re-lock only to update in-memory state.
- Optionally keep an in-memory atomic counter as a fast path, syncing to storage asynchronously.
Benchmark suggestion
Before/after with benchstat:
go test -bench=BenchmarkLimiter -benchmem -count=6 ./middleware/limiter/ | tee /tmp/limiter-before.txt
# apply fix
go test -bench=BenchmarkLimiter -benchmem -count=6 ./middleware/limiter/ | tee /tmp/limiter-after.txt
benchstat /tmp/limiter-before.txt /tmp/limiter-after.txt
Priority
P0 — This is the single highest-impact performance issue in the framework. It affects every deployment using external storage with these middleware.
Identified during a full performance architecture review of the Fiber codebase.
Problem
A performance architecture review identified that rate limiter, cache, and session middleware all hold a global mutex while performing external storage I/O (Redis, database, etc.). This serializes all requests through these middleware regardless of key, collapsing throughput to ~1/storage-latency.
Affected middleware
middleware/limiter/limiter_fixed.go:52-91mux.Lock()manager.get()+manager.set()middleware/limiter/limiter_sliding.go:54-102mux.Lock()manager.get()+manager.set()middleware/cache/cache.go:313-546mux.Lock()storage.Get()/storage.Set()middleware/session/middleware.go:144-157m.mu.Lock()store.Get()→storage.GetWithContext()Impact
With a 2ms Redis round-trip:
Proposed fix
Pattern: Per-key striped locking.
sync.RWMutexwith a fixed array of 32 or 64 shards, each with its own mutex.Benchmark suggestion
Before/after with
benchstat:Priority
P0 — This is the single highest-impact performance issue in the framework. It affects every deployment using external storage with these middleware.
Identified during a full performance architecture review of the Fiber codebase.