Skip to content

[Store] Add the namespace layer over the object route - #4721

Merged
Aionw merged 1 commit into
kvcache-ai:mainfrom
Aionw:refactor/namespace-layer
Oct 20, 2026
Merged

Aionw merged 1 commit into
kvcache-ai:mainfrom
Aionw:refactor/namespace-layer

Conversation

@Aionw

@Aionw Aionw commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Step 6 of #4557: the namespace layer over the object route from step 5 (#4585). Nothing calls it yet; MasterService still serves from its 1024 metadata shards.

The route keeps each key's lock, record and ObjectState, but a tenant's metadata needs more kept in step with every write: the keys that carry work in flight, so the expiry sweep need not walk every object; the order eviction visits objects in, so it need not take a census of every shard; and a place for a policy such as tenant quotas, so it is not booked by hand on each mutating path. This PR adds that layer and replaces the unwired metadata/tenant.h and metadata/tenant_registry.h it grew from.

  • Route owner state. Each KeySlot gains a few words for the route's owner (SlotOwnerState: whether the key is on the in-flight list, whether and at what rank the eviction index files it, two policy words) and two intrusive list hooks. The route stores them and never reads them; the owner reaches them through a write guard's or write cursor's owner_state(). The owner links a slot only while it holds an object, so a slot the route drops is never linked, and safe-link hooks assert it.
  • metadata::Namespace owns one tenant's ObjectRoute and is its RouteObserver. As each write guard is released, with the key still locked, it lists or unlists the key on a 64-stripe in-flight list, refiles it in the eviction index, and passes the release on to the policy. A sweep reads InFlightKeys() as a snapshot to resolve again. Builds with assertions on check that a key left with no object has its owner state back to the initial value.
  • metadata::EvictionIndex is the order eviction visits a namespace's objects in, read through a cursor, lowest rank first. LeaseEvictionIndex files the keys eviction can take memory from (not hard pinned, with a completed memory replica) by lease deadline in 100 ms buckets. A read only ever moves a key's deadline later, so it needs no read hook: the cursor's StillAt, asked under the key's write lock, catches a key that moved on, and that lock's release refiles it.
  • NamespacePolicy is the seam a policy plugs into: admit a write, approve growth before allocation, replace, tear down, write release, restore, capacity change and a maintenance tick, each called at a fixed point under fixed locks. StoreControl is what the core offers a policy back. Tenant quota becomes the first policy in step 8.
  • metadata::NamespaceTable maps tenant ids to namespaces. A namespace is created atomically through the table's factory and never moves or goes away while the service runs, so a resolved Namespace& needs no ownership, unlike the registry's shared_ptr<Tenant>. The default namespace, the only one when multi-tenancy is off, resolves through an atomic without the map lock.

The table's get-or-create and walk come from the tenant registry of #4215 by @CAICAIIs.

Not in this PR: the eviction::Evictor that merges the namespaces' cursors (step 7), the tenant quota policy (step 8), and anything in MasterService (step 15). The reference design had a NamespaceTable::Clear() for the old snapshot reload; #4406 removed that path, so it is left out.

Behavior is unchanged: no RPC, snapshot or OpLog format changes, and no code path in MasterService reaches the new layer.

Module

  • Mooncake Store (mooncake-store)

Type of Change

  • New feature
  • Refactor

How Has This Been Tested?

Test commands:

make -j12 metadata_namespace_test metadata_namespace_table_test eviction_index_test object_route_test group_index_test master_service_test
ctest -R '^(metadata_namespace_test|metadata_namespace_table_test|eviction_index_test|object_route_test|group_index_test|master_service_test)$'

Test results:

  • Unit tests pass

  • Integration tests pass (if applicable)

  • Manual testing done (describe below)

  • Tests: metadata_namespace_test (12 cases), metadata_namespace_table_test (7), eviction_index_test (6), object_route_test (36), group_index_test (7) and master_service_test pass with GCC 16 (Release, assertions on).

  • metadata_namespace_test covers the in-flight list (listing on release, replacement, teardown, destruction with listed keys, concurrent writers sharing stripes) and the policy calls with their context and policy words; its concurrency case passes 200 repeated runs. metadata_namespace_table_test covers racing creation for the default and a named namespace, walks that create namespaces, and lookups during creation.

  • They replace metadata_tenant_test and metadata_tenant_registry_test, removed with the headers they tested.

  • GCC 11.4 (Ubuntu 22.04, the CI floor): every changed translation unit passes -fsyntax-only.

  • Formatting: ./scripts/code_format.sh --check --changed-lines --base origin/main is clean with clang-format 20.1.8.

  • pre-commit on the changed files: the format and codespell hooks pass; cmake-format rewrites only pre-existing lines of mooncake-store/tests/CMakeLists.txt outside this change, which are left out.

Checklist

AI Assistance Disclosure

  • AI tools were used (specify below)

AI assistance was used to port the namespace layer and its tests and to draft this description. The submitter has reviewed every changed line and can defend the change end to end.

The striped object route keeps each key's lock, record and per-object
state, but a tenant's metadata needs more kept in step with every write:
the keys with work in flight, so the expiry sweep need not walk every
object; the order eviction visits objects in, so it need not take a
census of every shard; and a seam for policies such as tenant quotas,
so they stop being booked by hand on each mutating path.

Add that layer, unwired:

- The route gives each slot a few owner words (SlotOwnerState) and two
  intrusive list hooks, which it stores and never reads, and lets the
  owner reach them through a write guard or write cursor.
- metadata::Namespace owns one tenant's route and observes it: as each
  write guard is released it lists or unlists the key on a striped
  in-flight list, refiles it in the eviction index, and passes the
  release on to the namespace policy.
- EvictionIndex is the order eviction visits a namespace's objects in;
  LeaseEvictionIndex ranks keys by lease deadline in 100 ms buckets and
  needs no read hook, since a read only ever moves a key later.
- NamespacePolicy is the seam a policy plugs into (admit a write,
  approve growth, replace, tear down, write release, restore, capacity
  change, maintenance), and StoreControl is what the core offers back.
- metadata::NamespaceTable maps tenant ids to namespaces. A namespace is
  created atomically and never moves or goes away while the service
  runs, so a resolved reference needs no ownership, and the default
  namespace resolves through an atomic without the map lock.

These replace metadata/tenant.h and metadata/tenant_registry.h, the
unwired layer they grew from, and their tests. MasterService does not
use any of this yet.

Co-authored-by: CAICAIIs <3360776475@qq.com>
@Aionw
Aionw force-pushed the refactor/namespace-layer branch from 11a3f59 to 397ae5a Compare October 19, 2026 03:33

@Icedcoco Icedcoco 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

@CAICAIIs CAICAIIs left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm

@Aionw
Aionw merged commit d855a6a into kvcache-ai:main Oct 20, 2026
31 checks passed
@Aionw
Aionw deleted the refactor/namespace-layer branch October 20, 2026 09:43
zxpdemonio added a commit to zxpdemonio/Mooncake that referenced this pull request Oct 21, 2026
…20261021

* upstream/main: (507 commits)
  [Reshard] Plan Store KV reuse across TP, PP and host layouts (kvcache-ai#4762)
  [Store] Walk tenants through shard-backed cursors and build the removes on them (kvcache-ai#4796)
  [Store-RS][Bugfix] Match TENT Request Layout to the C ABI (kvcache-ai#4732)
  [TransferEngine] Recover auto GID after data-path retry exhaustion (kvcache-ai#4290)
  [Store] Add the namespace layer over the object route (kvcache-ai#4721)
  [Store] Fix silent loss of offload keys deferred to the ungrouped pool (kvcache-ai#3006) (kvcache-ai#3009)
  [Store] Remove redundant alignment headroom from DFS shard allocation (kvcache-ai#4753)
  [CI/Build] Gate ROCm hardware tests and bound nightly Docker builds (kvcache-ai#4750)
  [Bugfix][Store] Fix double-counting of segment OpLog metrics in OpLogApplier (kvcache-ai#4771)
  [Store] Skip hot-cache writeback for device-resident read destinations (kvcache-ai#4764)
  [Reshard] Add bounded in-flight execution for weight transfers (kvcache-ai#4628)
  [Store] Avoid racing with detached hot-cache fills during key scans (kvcache-ai#4334)
  [Store] HA: drain the master on SIGTERM/SIGINT before exit (part 1 of kvcache-ai#4710) (kvcache-ai#4745)
  TENT: report a notification the receiver could not take (kvcache-ai#4754)
  [Store] Reach object metadata through a per-key accessor contract (kvcache-ai#4717)
  [Store][Bugfix] Fix DFS sync client test retry failure (kvcache-ai#4781)
  [Wheel] Keep integer dtype for typed_ragged rows when a row is an empty list (kvcache-ai#4611)
  [Doc] Sync README news onto the docs homepage (kvcache-ai#4767)
  [CI] remove stale Torch version binding after PG JIT (kvcache-ai#4760)
  [TransferEngine] Add buffer-protocol and page-gathered batch transfer APIs (kvcache-ai#4763)
  ...
zxpdemonio added a commit to zxpdemonio/Mooncake that referenced this pull request Oct 21, 2026
…-ranges-20261021

* upstream/main: (394 commits)
  [Reshard] Plan Store KV reuse across TP, PP and host layouts (kvcache-ai#4762)
  [Store] Walk tenants through shard-backed cursors and build the removes on them (kvcache-ai#4796)
  [Store-RS][Bugfix] Match TENT Request Layout to the C ABI (kvcache-ai#4732)
  [TransferEngine] Recover auto GID after data-path retry exhaustion (kvcache-ai#4290)
  [Store] Add the namespace layer over the object route (kvcache-ai#4721)
  [Store] Fix silent loss of offload keys deferred to the ungrouped pool (kvcache-ai#3006) (kvcache-ai#3009)
  [Store] Remove redundant alignment headroom from DFS shard allocation (kvcache-ai#4753)
  [CI/Build] Gate ROCm hardware tests and bound nightly Docker builds (kvcache-ai#4750)
  [Bugfix][Store] Fix double-counting of segment OpLog metrics in OpLogApplier (kvcache-ai#4771)
  [Store] Skip hot-cache writeback for device-resident read destinations (kvcache-ai#4764)
  [Reshard] Add bounded in-flight execution for weight transfers (kvcache-ai#4628)
  [Store] Avoid racing with detached hot-cache fills during key scans (kvcache-ai#4334)
  [Store] HA: drain the master on SIGTERM/SIGINT before exit (part 1 of kvcache-ai#4710) (kvcache-ai#4745)
  TENT: report a notification the receiver could not take (kvcache-ai#4754)
  [Store] Reach object metadata through a per-key accessor contract (kvcache-ai#4717)
  [Store][Bugfix] Fix DFS sync client test retry failure (kvcache-ai#4781)
  [Wheel] Keep integer dtype for typed_ragged rows when a row is an empty list (kvcache-ai#4611)
  [Doc] Sync README news onto the docs homepage (kvcache-ai#4767)
  [CI] remove stale Torch version binding after PG JIT (kvcache-ai#4760)
  [TransferEngine] Add buffer-protocol and page-gathered batch transfer APIs (kvcache-ai#4763)
  ...
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.

4 participants