mirror of
https://github.com/kvcache-ai/Mooncake.git
synced 2026-11-05 01:17:55 +00:00
[GH-ISSUE #4296] [RFC]: [Store] Add object storage as a first-class replica tier #1076
Labels
No labels
ascend
auto-closed
bug
bug
CI/Build
Common
documentation
enhancement
feature request
good first issue
help wanted
high priority
MetaX
nightly-failure
pull-request
PyTorch Backend
question
RFC
Roadmap
stale
Store
Tests
Transfer Engine
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
kvcache-ai/Mooncake#1076
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @particle128 on GitHub (Sep 23, 2026).
Original GitHub issue: https://github.com/kvcache-ai/Mooncake/issues/4296
Changes proposed
[RFC]: [Store] Add object storage as a first-class replica tier
This proposal introduces object storage as a first-class replica tier, reusing the existing OSS adapter. The new types, configuration options, and protocol extensions below are proposed changes.
1. Goals and scope
The current object storage integration follows the FileStorage / LOCAL_DISK path: the MEMORY owner performs uploads, and reads also depend on that owner. This proposal introduces an
OBJECT_STORAGEreplica alongside MEMORY, LOCAL_DISK, DFS, and NOF_SSD:The initial implementation reuses the OSS adapter while keeping the interface provider-neutral. Each Mooncake object maps to one backend object. Object aggregation, cascading MEMORY → SSD → object storage, a unified redesign of all external storage tiers, and general idempotency enhancements to the shared Start/End/Revoke protocol are out of scope.
2. Architecture
3. Metadata and configuration
3.1 Replica descriptor
ObjectStorageDescriptoris added to the existingReplica::Descriptor::descriptor_variant. Replicaidandstatusremain in the shared outer descriptor rather than being duplicated in the object descriptor.Enabling object replicas requires support in the Master and all participating Clients. The initial implementation does not promise mixed-version operation with older releases.
os_replica_numinitially supports 0 or 1, defaulting to 0. A value of 1 requests one object replica according to the Master's policy. It is neither the object service's internal replication factor nor an indication that the replica is already complete.object_keyis a backend object name allocated by the Master, separate from the application's key. The adapter handles prefixes and encoding.object_key. This does not introduce a user-facing multi-version KV API.Using a separate object name isolates successive values of the same application key. For example, if
Put(K)followsRemove(K)before the old background DELETE finishes, using K for both backend objects would let the delayed DELETE remove the new data. The Master maps the application key to anobject_key, so old tasks operate only on the old object. The same isolation applies to Upsert and late uploads.3.2 Cluster-wide backend configuration
Initially, each cluster uses a single object storage backend. The Master publishes non-secret configuration to Clients: provider, endpoint, bucket/namespace, and prefix. Clients fetch and cache it during initialization. The Master uses the same storage target for reclamation; descriptors do not duplicate this configuration.
An object's full location is the cluster-wide endpoint / bucket / prefix plus
object_key.Client-side and Master-side adapters use credentials configured in their respective processes, reusing the existing OSS environment variables without introducing a credential service:
Clients need read/write access; Master-side reclamation needs delete access within the target bucket/prefix. They may use different identities. Secrets are not included in shared configuration or descriptors. The initial implementation retains credential loading at adapter initialization and does not introduce automatic credential refresh.
Backend configuration is fixed at cluster startup. Runtime target switching and multi-backend routing are not supported. Existing objects and upload/deletion tasks must not be reinterpreted against another namespace; descriptors, configuration caches, and tasks must not be reused across rebuilt clusters.
Object access does not require a local SSD mount. Background modes require an owner capable of fetching tasks and uploading objects.
The backend must make fully committed objects readable across Clients and support reads and repeated deletes by
object_key. The adapter handles provider-specific differences.3.3 Master write mode
A new Master option,
object_storage_write_mode, defaults tosyncand can be set through the configuration file or command line:syncasyncoffload_on_evictThe option applies only to writes with
os_replica_num=1; 0 preserves existing behavior. The mode is bound to the write. PutStart returns the required foreground replicas and deferred target, rather than reinterpreting in-flight tasks after a configuration change.Both background modes verify during foreground admission that at least one MEMORY owner can fetch tasks and upload objects. The
syncmode only requires object-write capability on the requesting Client.In either background mode, a successful Put does not guarantee that the data is already in object storage. Losing all valid replicas before the object is committed can still make the data unavailable. Only the object replica is deferred; other requested foreground replicas must still complete.
For these keys, the object policy controls automatic offload scheduling to avoid duplicate dispatch through the existing LOCAL_DISK path. Tasks explicitly target OBJECT_STORAGE and do not register completion as LOCAL_DISK. Existing LOCAL_DISK replicas are unaffected.
4. Write paths
4.1 Synchronous writes
Data transfers for MEMORY and other replicas are omitted from the diagram. The Master creates object-replica metadata and allocates an
object_key; the Client uploads through the existing adapter. Completion is determined per key, including per-key results within a batch. Failure of any required replica must not be reported as success for that key.4.2 Asynchronous replication after a memory write
4.3 Offload on memory eviction
The foreground Put records only the deferred policy. Object and task reservation occurs when eviction is triggered. Existing memory-eviction candidate rules are reused. An existing COMPLETE object for the same value avoids another upload, and an existing task avoids duplicate scheduling.
Source memory must not be released merely to lower the watermark before the object is committed or while read/write tasks still reference it. Memory watermarks must leave upload headroom, with bounds on queue size and protected bytes. If object storage cannot keep up, new Puts may be backpressured or fail; data is not automatically redirected to SSD.
5. Read path
All three write modes share this read path, retaining MEMORY preference. Uncommitted objects are not selected for reads.
Before submitting a new issue...
@github-actions[bot] commented on GitHub (Sep 23, 2026):
Thanks for opening this issue, @particle128!
3041842A maintainer will triage this when possible. To help us respond faster, please include:
Useful links: Documentation · Contributing guide
@fcczzz commented on GitHub (Sep 24, 2026):
@particle128 Thanks for the proposal! Making object-storage replicas readable independently of the original owner would be a useful improvement.
Have you considered extending the existing DFS abstraction instead of introducing a new top-level
OBJECT_STORAGEreplica type? DFS already supports shard and bucket allocators, so perhaps an object-storage allocator could handle unique object-key allocation and deferred deletion, with the backend routing reads and writes through the existing object-storage adapter.That would still require descriptor and I/O-path extensions, but both approaches seem to share the same core model: Master-managed replica metadata with direct Client access to shared storage. It would be helpful to include a comparison in the RFC—are there lifecycle or API requirements that make a separate replica type preferable, or is the main motivation to keep this change scoped?
@hongzhi-gao commented on GitHub (Oct 16, 2026):
Revised review units and phase acceptance criteria
Updated after fcczzz's clarification: A separate
OBJECT_STORAGEreplica type is acceptable. The requirement is to share common replica-management semantics, rather than choose an enum as a substitute for code reuse. The earlier recommendation to require both locations under one replica category is superseded.There are still three user-visible phases. The review split now separates bounded I/O, the catalog/recovery boundary, and synchronous integration; the number of PRs is secondary to a coherent correctness boundary.
Implementation status: #4727 has been narrowed to I/O. #4936 preserves the extracted catalog implementation and component tests for focused review. Its independent logical heads/commit flags are not an agreed recovery authority, and it has not implemented unified Master log/snapshot recovery. The full integration, async and eviction review units remain future work.
Proposed initial deployment scope: First-class OSS requires a supported durable recovery configuration with DFS disabled. The first phase does not promise DFS/OSS coexistence in one cluster or both replicas for one key. Current upstream rejects DFS with snapshot/oplog recovery and cannot restore DFS allocator state; that gap must be resolved before expanding coexistence. Unsupported combinations must be rejected explicitly. This is a proposed integration scope, not a new restriction implemented by the I/O-only PR.
Durable upload/cleanup obligations remain necessary. The preferred ownership proposal extends the existing ordered recovery authority, rather than introducing a second authoritative logical-head directory. The exact interface is for the separate catalog review. Existing DFS recovery and PutEnd are insufficient unchanged. Object visibility, immutable attempt fencing, late-upload cleanup and recovery guarantees must accompany the first complete synchronous feature.
Phase 1 — Complete synchronous object replicas (review units 1–3)
Acceptance criteria:
Phase 2 — Complete asynchronous replication (review unit 4)
Acceptance criteria:
Phase 3 — Complete offload on eviction (review unit 5)
Acceptance criteria:
Delivery constraints
@fcczzz commented on GitHub (Oct 23, 2026):
@hongzhi-gao Hi, thanks for explaining. My main concern is preserving a common abstraction and reusing replica-management logic.
From Mooncake’s perspective, DFS and object storage seem to serve the same role: the Master manages replica locations and states, while Clients access shared storage directly. File-and-offset addressing versus object-key addressing clearly requires different descriptors and I/O implementations, but could those differences be handled through an object allocator and backend extensions?
Uncertain upload outcomes, late completion, and deferred deletion need explicit handling in either design. These requirements alone don’t seem to establish why a separate top-level replica type is preferable. My concern is that adding OBJECT_STORAGE could introduce parallel branches across allocation, completion, read selection, lease protection, reclamation, and recovery, even though much of the lifecycle is shared.
Could the RFC compare the two approaches, identifying which management paths remain shared and where genuinely different behavior is required? In particular, what would be difficult to express through allocator/backend interfaces?
If the main benefit of a separate type is limiting changes to existing DFS code for the initial implementation, that is understandable. It would help to make that tradeoff explicit and explain how we would avoid duplicating the common lifecycle logic.
@hongzhi-gao commented on GitHub (Oct 23, 2026):
Update (2026-10-26): Following fcczzz's clarification, keeping a separate
OBJECT_STORAGEtype is acceptable; my earlier recommendation below to require one shared replica category is superseded. See the current scope/management-boundary reply and updated delivery plan. #4727 is now I/O-only, with catalog review in draft #4936.Thanks, @fcczzz. After tracing the allocator, Master/Client lifecycle, and current recovery paths, I think extending the shared-storage abstraction is the better direction.
I did not find a lifecycle requirement that inherently needs a separate top-level
OBJECT_STORAGEtype. File ranges and object keys need distinct location descriptors and I/O implementations, but allocation bookkeeping, replica completion/visibility, read leases, pins, and logical removal can remain shared. Upload uncertainty and deferred deletion require explicit handling in either design.This needs interface changes: the allocator currently returns only
DistributedFSDescriptor, andFree()cannot report asynchronous reclamation. I propose tagged file/object locations, exact write-attempt tokens, and separate logical retirement and physical reclamation contracts, while preserving shard/bucket-specific allocation and deletion units.I would also revise the catalog split. Durable upload and cleanup records are necessary, but a second authoritative logical-head catalog is not inherently required. Current upstream provides ordered batch logging and materialized snapshots; these need extensions for pending attempts and reclamation records, plus durable acknowledgement before committing object replicas. Existing DFS recovery and PutEnd cannot simply be reused unchanged.
Would you agree with keeping both location variants under the shared-storage replica category, using the existing log/snapshot authority for visibility and recovery, and requiring durable recovery configuration for the first synchronous feature? The remaining lease decision is whether failover uses a conservative read-TTL grace period or persists lease bounds.
This is a source-level design comparison; I have not implemented or runtime-validated the proposed interfaces.
@fcczzz commented on GitHub (Oct 25, 2026):
@hongzhi-gao Hi, thanks for tracing the existing paths and updating the proposal. I'd like to clarify my earlier suggestion: extending DFS was an option to explore, not a requirement to avoid a separate OBJECT_STORAGE type. I'm comfortable proceeding with the separate type.
My main concern is reusing common replica-management logic where the semantics match, so introducing another backend does not require parallel allocation, completion, revoke, lease, removal, and recovery flows across Put, BatchPut, and Upsert. Sharing the same enum would not, by itself, prevent that duplication either.
As we proceed, could you clarify:
I support the phased delivery plan. For #4727, could we keep the object-storage I/O portion in this PR and move the persistent catalog/lifecycle management into a separate PR for focused review?
The catalog tracks upload and cleanup obligations, but also logical head and commit state, which overlaps with the Master's existing metadata and recovery responsibilities. In that separate PR, we should clarify ownership of these facts, what can extend the existing log/snapshot mechanisms, and how recovery handles discrepancies and unfinished operations. Object-specific persistent records may be necessary; the important question is how they fit into the overall recovery contract.
We can start reviewing the I/O portion now, while agreeing on the catalog boundary before subsequent integration builds on it. There is no need to change the replica type solely to accommodate my earlier suggestion.
@hongzhi-gao commented on GitHub (Oct 26, 2026):