Skip to content

🔥 feat: shard memory storage and idempotency locker to reduce lock contention #4361

@pageton

Description

@pageton

Problem

Two internal components use a single global mutex to protect all keys, creating contention under concurrent access:

1. Memory storage

File: internal/storage/memory/memory.go:20

A single sync.RWMutex protects the entire db map[string]Entry. Every Get, Set, Delete, Reset, and Keys call contends on this one lock. When used as the storage backend for sessions, rate limiting, or CSRF, this becomes a bottleneck.

2. Idempotency locker

File: middleware/idempotency/locker.go:21-35

MemoryLock.mu is a single sync.Mutex protecting the keys map. Every Lock() and Unlock() call acquires this global lock, even for different idempotency keys.

Impact

  • Memory storage: Write operations (Set/Delete) block all reads. Noticeable at >10K concurrent requests.
  • Idempotency locker: All concurrent idempotent requests with different keys serialize.

Proposed fix

Sharded map pattern (apply to both):

const numShards = 32

type shardedMap struct {
    shards [numShards]struct {
        mu sync.RWMutex
        m  map[string]entry
    }
}

func (s *shardedMap) getShard(key string) *shard {
    hash := fnv32(key)
    return &s.shards[hash%numShards]
}

This reduces contention by ~32x with minimal memory overhead.

Priority

P1 — Matters at >10K QPS or when memory storage is the default backend.


Identified during a full performance architecture review of the Fiber codebase.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Done

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions