Add first-writer-wins claims to data store runtime and DataObject#27286
Add first-writer-wins claims to data store runtime and DataObject#27286kian-thompson wants to merge 5 commits into
Conversation
Introduces the public API surface for first-writer-wins claims: - ClaimResult = "Success" | "AlreadyClaimed" - IFluidDataStoreRuntime.trySetClaim/getClaim/hasClaim/claims (all optional) - IFluidDataStorePolicies.enableDataStoreClaims opt-in flag All members are @legacy @beta. Implementation follows in @fluidframework/datastore. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds first-writer-wins claim support to FluidDataStoreRuntime, gated by the new IFluidDataStorePolicies.enableDataStoreClaims flag. - Adds DataStoreMessageType.Claim op type and IClaimMessage envelope. - New runtime state: sequencedClaims, wonClaims, pendingClaims, with async rehydration from a .claims summary blob. - trySetClaim awaits load, then submits a Claim op (or writes directly when detached). Local op processing classifies the writer as winner/loser. - Inbound Claim ops arriving before the snapshot load completes are buffered and drained in arrival order. - Summary persistence via getAttachSummary() and summarize() writes the .claims blob; getOutboundRoutes walks claim values for handle routes so GC sees handles inside claims. - Handle values inside claims are encoded/decoded the same way as in summary blobs (encodeHandleForSerialization / RemoteFluidObjectHandle). - dispose() resolves any outstanding pending deferreds with AlreadyClaimed. - Adds 13 unit tests covering single-client, two-client races, repeat-from- winner/loser, detached set + attach, GC routes, reconnect, feature flag off, staging mode, snapshot rehydration, summary content, and dispose. The DataStoreMessageType enum gained a new value, which breaks the current_as_old typetest; this is recorded in package.json typeValidation.broken. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Forwards to the underlying IFluidDataStoreRuntime claims API. Throws a UsageError if the runtime does not implement claims (e.g., the enableDataStoreClaims policy was not enabled when the data store was created). Updates the DataObject class doc-comment with guidance on when to use a claim vs. writing to root. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Hi! Thank you for opening this PR. Want me to review it? Based on the diff (1171 lines, 16 files), I've queued these reviewers:
How this works
|
|
This definitely feels like it's in the direction I would expect (right granularity and availability, right scenario support, etc.). Very much prefer this direction over #27291. My main feedback would be about encapsulation and reuse of existing concepts. What you're building here is effectively a DDS - an improved and simplified version of ConsensusRegisterCollection. I'd be interested to see this actually built as a new standalone DDS, which we can then leverage in the DataStoreRuntime to provide the functionality. This would help keep the runtime from growing in direct responsibility, avoids expanding the op type vocabulary, probably simplifies your load flow and op buffering requirements, etc. It probably also sets you up well to leverage existing DDS testing framework, and avoids being a special case for features that intersect with the op stream (e.g. staging mode, rollback, etc.) Such a DDS could start out as |
Change trySetClaim from async Promise<ClaimResult> to a synchronous
method returning a discriminated-union IClaimAttempt:
| { status: 'Success' | 'AlreadyClaimed' }
| { status: 'Pending'; result: Promise<ClaimResult> }
Terminal cases (already-loaded + known outcome, or detached) return
synchronously with no promise. Cases where the outcome is not yet
known locally (claims still loading, or op submitted but not yet
sequenced — including attached+disconnected) return Pending with a
result promise that resolves when the outcome is determined.
Dispose now rejects pending claim deferreds with UsageError instead
of misleadingly resolving them to AlreadyClaimed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🔗 No broken links found! ✅ Your attention to detail is admirable. linkcheck output |
Description
Adds a new "claim" primitive to
IFluidDataStoreRuntimeandDataObjectthat lets a data store publish first-writer-wins key/value entries. A claim is conceptually a small piece of immutable per-data-store state where the first client to write a given key wins; concurrent writers from other clients observe"AlreadyClaimed"and can branch their logic accordingly.Use a claim (rather than writing to
DataObject.root) when you specifically need first-writer-wins semantics — for example, when multiple clients race to designate themselves as the owner of a particular role within the data store and only one should succeed.New API surface (all
@legacy@beta)ClaimResult = "Success" | "AlreadyClaimed".IFluidDataStoreRuntime.trySetClaim(key, value),getClaim(key),hasClaim(key), andclaims(a read-only iterator).IFluidDataStorePolicies.enableDataStoreClaimsopt-in flag (defaults to off; set totrueon the data store runtime to enable the API).DataObject.trySetClaim,getClaim,hasClaimconvenience helpers that forward to the runtime.Implementation notes
IFluidHandleinstances; these are encoded the same way as handles in summary blobs and contribute outbound routes to garbage collection..claimssummary blob on the data store and rehydrated on subsequent loads.DataStoreMessageType.Claimop type. Older clients that don't understand it silently ignore it — authors enabling the flag should ensure all collaborators run a runtime that recognizes the op."AlreadyClaimed".Testing
New
claims.spec.tssuite covers: single-client success, two-client race, repeated calls from winner/loser, detached set + persistence, GC route collection from embedded handles, reconnect/resubmit, feature-flag-off, snapshot rehydration, staging-mode rejection, summary blob, and dispose behavior.