Skip to content

feat(map): add first-writer-wins claim API on SharedDirectory#27291

Draft
kian-thompson wants to merge 2 commits into
microsoft:mainfrom
kian-thompson:users/copilot/claims-dds
Draft

feat(map): add first-writer-wins claim API on SharedDirectory#27291
kian-thompson wants to merge 2 commits into
microsoft:mainfrom
kian-thompson:users/copilot/claims-dds

Conversation

@kian-thompson
Copy link
Copy Markdown
Contributor

Description

Adds an opt-in first-writer-wins claim API on ISharedDirectory:

  • trySetClaim(key, value): Promise<ClaimResult> — attempts to claim a root-level key. Resolves to "Success" if this client won, or "AlreadyClaimed" if another client's claim was sequenced first. Repeated calls from the original winner also resolve "Success".
  • isClaimed(key): boolean — returns true if the root-level key is currently claimed.

Once a key is claimed, the value is immutable: set and delete for that key throw UsageError, get returns the claimed value, and clear() does not remove claims. Claims are persisted in the summary and survive load (on rehydrate, no client is treated as the original winner).

The API is gated behind a runtime opt-in (enableDdsClaims: true on the data store runtime options). Calling trySetClaim or isClaimed without the flag throws UsageError. Claims are only available on the root ISharedDirectory; subdirectories intentionally do not expose them.

Implementation notes:

  • New "claim" op type with its own message handler, rollback path, resubmit path, and stashed-op replay.
  • New optional claims field in the IDirectoryNewStorageFormat summary blob (back-compat: absent on older snapshots).
  • Pending claim deferreds are resolved on dispose to avoid hangs.

Reviewer Guidance

The review process is outlined on this wiki page.

  • Open question: while offline, the trySetClaim promise stays pending until reconnect (or dispose). Should we add an AbortSignal parameter or a disconnected-aware result, or just document the behavior? Marking as draft pending this discussion.
  • The applyStashedOp path re-submits a fresh claim op rather than reusing the stashed op directly — feedback welcome on whether that's the right approach.
  • New mocha tests live in packages/dds/map/src/test/mocha/directory.claims.spec.ts.

kian-thompson and others added 2 commits May 12, 2026 00:47
Adds DDS-level first-writer-wins claim semantics on SharedDirectory's
root, gated behind runtime option `enableDdsClaims`.

- New `IClaimable`/`ClaimResult` types (legacy beta).
- New `claim` op kind in `IDirectoryOperation` carrying { key, value };
  no path because claims are root-scoped.
- New optional `claims` field in `IDirectoryNewStorageFormat`;
  back-compat preserved (loaders treat absence as empty).
- Local `set`/`delete` on a claimed root key throws UsageError;
  remote `set`/`delete` ops are dropped on processing.
- `clear()` preserves claimed entries.
- Detached: claim resolves synchronously to Success and persists in
  attach summary.
- Disposing the DDS resolves pending claim deferreds with AlreadyClaimed.
- On reload, no client is recorded as winner; repeat trySetClaim
  resolves to AlreadyClaimed (loser-on-retry per spec).

Includes 12 unit tests covering: feature flag, single-client claim,
two-client race, repeat winner/loser semantics, set/delete throws,
clear preservation, detached round-trip, summary back-compat, and
sequenced rehydration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

Hi! Thank you for opening this PR. Want me to review it?

Based on the diff (593 lines, 9 files), I've queued these reviewers:

  • Correctness — logic errors, race conditions, lifecycle issues
  • Security — vulnerabilities, secret exposure, injection
  • API Compatibility — breaking changes, release tags, type design
  • Performance — algorithmic regressions, memory leaks
  • Testing — coverage gaps, hollow tests

How this works

  • Adjust the reviewer set by ticking/unticking boxes above. Reviewer toggles alone don't trigger anything.

  • Tick Start review below to dispatch the review fleet.

  • After review finishes, tick Start review again to request another run — it auto-resets after each dispatch.

  • This comment updates as new commits land; your reviewer selections are preserved.

  • Start review

@github-actions
Copy link
Copy Markdown
Contributor

🔗 No broken links found! ✅

Your attention to detail is admirable.

linkcheck output


> fluid-framework-docs-site@0.0.0 ci:check-links /home/runner/work/FluidFramework/FluidFramework/docs
> start-server-and-test "npm run serve -- --no-open" 3000 check-links

1: starting server using command "npm run serve -- --no-open"
and when url "[ 'http://127.0.0.1:3000' ]" is responding with HTTP status code 200
running tests using command "npm run check-links"


> fluid-framework-docs-site@0.0.0 serve
> docusaurus serve --no-open

[SUCCESS] Serving "build" directory at: http://localhost:3000/

> fluid-framework-docs-site@0.0.0 check-links
> linkcheck http://localhost:3000 --skip-file skipped-urls.txt

Crawling...

Stats:
  288641 links
    1922 destination URLs
    2172 URLs ignored
       0 warnings
       0 errors


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant