concurrent-map
A thread-safe, sharded map for Go that avoids the lock contention of a single global mutex.
Repository Health
Technical Analysis
concurrent-map provides a thread-safe map type for Go, filling the gap left by the language’s built-in map, which does not support concurrent reads and writes. Rather than guarding the whole map with one mutex, it shards keys across a configurable number of independently-locked sub-maps (32 by default), so goroutines touching different shards don’t block each other.
The library predates Go’s stdlib sync.Map (added in Go 1.9) and remains a common choice for workloads that aren’t append-only, such as in-memory caches and counters, where sync.Map is a poor fit. It exposes a generics-based API (Go 1.18+) with Set, Get, Upsert, Remove, and buffered/callback-based iteration, plus JSON marshal/unmarshal support so a ConcurrentMap can be serialized like a plain map.
What You Get
- A generic
ConcurrentMap[K, V]type usable as a drop-in thread-safe alternative to Go’s built-in map - Sharded internal storage (32 shards by default) with per-shard
sync.RWMutexlocking to reduce contention - Atomic
Upsertand conditionalRemoveCboperations for safe read-modify-write patterns under lock - Buffered and callback-based iteration (
IterBuffered,IterCb,Keys,Items) that snapshots shards without holding all locks at once - Built-in
MarshalJSON/UnmarshalJSONso a concurrent map serializes and deserializes like a regular map
Common Use Cases
- In-memory caches shared across many goroutines in a web server or worker pool
- Concurrent counters or aggregation tables updated by parallel pipelines
- Session or connection registries in network servers where entries are frequently added and removed (not append-only)
- Any hot path currently wrapping a plain map in a single
sync.Mutexthat has become a contention bottleneck
Under The Hood
Architecture
The entire package is a single file (concurrent_map.go) built around one exported type, ConcurrentMap[K, V], which holds a slice of ConcurrentMapShared[K, V] shards, each an unexported map plus its own sync.RWMutex. GetShard hashes a key via FNV-32 (fnv32/strfnv32) to pick a shard index, so every read (Get, Has, Count) takes an RLock on just one shard and every write (Set, Upsert, Remove, Pop) takes a Lock on just one shard, instead of contending on a single map-wide lock. Iteration is the one place that needs a full-map view: snapshot() fans out a goroutine per shard to RLock it, drain its items into a buffered channel, and fanIn merges those channels into one, so a full scan never holds every shard lock simultaneously. It’s a flat, single-abstraction design with no layering or dependency injection — changing the shard count or hash function touches every consumer of GetShard at once, since all locking and iteration logic is keyed off that index.
Tech Stack
Pure Go standard library only — sync for the per-shard RWMutexes and WaitGroups, encoding/json for marshal/unmarshal, and fmt for the Stringer constraint used by NewStringer. The module (go.mod) targets Go 1.18, the version that introduced generics, which the API relies on throughout (ConcurrentMap[K comparable, V any]). There are no runtime dependencies, no build tooling beyond go build/go test, and CI is configured through a legacy Travis CI file (.travis.yml) rather than GitHub Actions.
Code Quality
The repo carries both a substantial test file (concurrent_map_test.go) exercising creation, insertion, upsert, removal, iteration, and JSON round-tripping, and a dedicated benchmark file (concurrent_map_bench_test.go) comparing sharded-map performance against naive locking approaches. Error handling is idiomatic Go rather than exception-based: lookups return a (value, bool) pair, and only MarshalJSON/UnmarshalJSON return a standard error. Naming follows conventional Go style (exported PascalCase API, unexported camelCase helpers), and type safety comes from the comparable/any generic constraints rather than runtime type assertions. No dedicated linter configuration is present in the repo; formatting relies on gofmt conventions.
What Makes It Unique
The public API is deliberately shaped to mirror Go’s built-in map (Set/Get/Remove/Has/Count), keeping the learning curve close to zero for anyone already using native maps, while adding concurrency-specific primitives — Upsert for atomic read-modify-write under a callback, and RemoveCb for conditional deletion under lock — that the built-in map and sync.Map don’t offer. Multiple generic constructors (New, NewStringer, NewWithCustomShardingFunction) let callers opt into custom key types and sharding strategies without subclassing, and transparent JSON support means a concurrent map can be dropped into existing serialization code with no extra glue.
Used by 2 apps in this directory
opencloud
File Storage
Open source file management and collaboration platform that keeps your data under your control, no database required.
Portainer
Devops
A lightweight, open-source web UI that puts Docker, Kubernetes, and Podman management within reach of any team—no CLI expertise required.