Skip to content

[Store] Read the DFS shard topology without a global lock - #4770

Merged
ykwd merged 2 commits into
kvcache-ai:mainfrom
daegyu94:perf/dfs-shard-topology-lockfree
Oct 26, 2026
Merged

ykwd merged 2 commits into
kvcache-ai:mainfrom
daegyu94:perf/dfs-shard-topology-lockfree

Conversation

@daegyu94

Copy link
Copy Markdown
Contributor

Description

ShardAllocator reads its shard topology with
std::atomic_load_explicit(&shards_) on every Allocate, Free and
UpdateAccess. libstdc++ implements this with a global mutex pool, so master
RPC workers serialize there even when they touch different shards.
UpdateAccess runs on every DFS read and PutEnd.

ShardList is an immutable snapshot that changes rarely (only on Init and
a growing ExpandShards via the serialized admin API), and old snapshots can
be kept until the allocator is destroyed. This PR therefore publishes it
through std::atomic<const ShardList*>, so lookups are a lock-free acquire
load, safe without shared_ptr reference counting. The ShardState objects
it points to remain shared_ptr, and behavior is unchanged.

Trade-off: since readers hold no reference, superseded snapshots are not
reclaimed. Each costs 16 bytes per shard, and one is added only when an
operator grows the shard count through the admin API, which sets a new total
(e.g. 64 → 128). Normal expansions therefore retain a few KiB in total; only
an unusual pattern of many single-shard expansions grows it quadratically. A
new master term creates a new allocator, so nothing accumulates across leader
changes. In return, every Allocate, Free and UpdateAccess stops taking
a process-wide mutex. std::atomic<std::shared_ptr> was not used because it
still updates the shared reference count on every load.

Microbenchmark (64 shards, median of 3 runs; local benchmark not included in
this PR; AMD Ryzen Threadripper 9960X, GCC 13, Release):

threads UpdateAccess Allocate+Free
1 21.6 → 37 Mops/s 3.8 → 4.4 Mops/s
16 (master default) 1.8 → 25 Mops/s 0.99 → 7.5 Mops/s

Note: this will conflict textually with #4018 on the shards_ declaration.

Suggested Merge Priority

  • P2 — Normal: Routine bug fix, validated performance/compatibility improvement, or new feature for a confirmed use case

Module

  • Mooncake Store (mooncake-store)

Type of Change

  • Performance improvement

How Has This Been Tested?

Test commands:

./build/mooncake-store/tests/dfs_posix_test
./build/mooncake-store/tests/master_service_dfs_scenario_test
setarch $(uname -m) -R ./build-tsan/mooncake-store/tests/dfs_posix_test  # TSan

Test results:

  • Unit tests pass

Both pass, also under ASan. TSan reports no races, including
ConcurrentExpansionAllocationAndEvictionRestore.

Checklist

  • I have performed a self-review of my own code
  • I have run pre-commit on the files changed in this PR and all hooks pass

AI Assistance Disclosure

  • AI tools were used (specify below)

Claude Code assisted with analysis, implementation, testing and this
description. I reviewed every changed line.

ShardAllocator kept its shard topology in a std::shared_ptr and loaded it
with std::atomic_load_explicit() in Allocate, Free, UpdateAccess and the
eviction paths. libstdc++ implements that free function by locking one of
16 global mutexes chosen by address and then copying the shared_ptr, so
every caller takes the same mutex and bounces the same reference count.
UpdateAccess runs for every DFS replica on GetReplicaList, BatchGet and
PutEnd, so concurrent master RPC workers serialize there even when they
touch different shards.

Publish each topology snapshot through std::atomic<const ShardList*>,
so readers do a plain acquire load. Readers keep no reference, so
superseded snapshots are kept until the allocator is destroyed rather
than reclaimed. Snapshots are only published by Init and by a growing
ExpandShards, under expansion_mutex_, and each retains 16 bytes (one
shared_ptr) per shard; lists are reserved to their final size. Shard
states, publication order and reader semantics are unchanged.

With 16 master RPC threads, allocator throughput no longer collapses
under the shared lock.

@fcczzz fcczzz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ykwd
ykwd merged commit ad546de into kvcache-ai:main Oct 26, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants