Skip to content

feat(plugins): add revision based conditional writes to storage and KV - #2980

Open
logelog wants to merge 7 commits into
emdash-cms:mainfrom
logelog:feat/plugin-storage-conditional-writes
Open

feat(plugins): add revision based conditional writes to storage and KV#2980
logelog wants to merge 7 commits into
emdash-cms:mainfrom
logelog:feat/plugin-storage-conditional-writes

Conversation

@logelog

@logelog logelog commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

When two requests read a plugin counter and each save an increment, one increment can be lost: both writes succeed, but the later write replaces the earlier result. This PR adds revision based conditional writes so plugin developers can detect that conflict and retry from the latest value.

The same API is available on declared storage collections and ctx.kv in native, Cloudflare and Workerd plugins. A shared counter can use it as follows:

const key = "state:completedJobs";
const current = await ctx.kv.getVersioned<number>(key);
const result = await ctx.kv.compareAndSet(
	key,
	current?.revision ?? null,
	(current?.value ?? 0) + 1,
);
if (!result.applied) {
	throw new Error("Counter changed; read it again before retrying");
}

If both requests read the same revision, only one replacement applies. The other receives a conflict and can read, recompute and retry with a bounded policy.

  • getVersioned(key) returns { value, revision }, or null when absent. Stored JSON null still has a revision.
  • compareAndSet(key, expectedRevision, value) returns { applied: true, revision } on success or { applied: false } on conflict. An explicit null revision creates only when absent.
  • compareAndDelete(key, expectedRevision) deletes only the matching version and returns { applied: boolean }.

Each operation targets one key in the calling plugin's namespace; collection access also requires a declared collection. Invalid input, permission failures and database failures reject the promise. Existing unconditional writes invalidate earlier revisions. Atomicity covers one key, so multiple records and external actions still require coordination by the plugin.

Migration

Migration 076 adds revisions with default "0" without a row backfill and can resume after interrupted schema changes. Triggers invalidate revisions for older writers, including delete/recreate cycles. Upgrade core and the matching sandbox adapter together, and apply the migration before using these methods.

Discussion: approved storage atomicity direction.

Type of change

  • Bug fix
  • Feature (requires maintainer approval through a Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md.
  • Workspace typechecks pass.
  • Full type-aware lint passes.
  • Targeted tests pass on the affected database and sandbox runtimes.
  • Changed files have been formatted.
  • I have added and updated tests for the changes.
  • User-visible admin strings are wrapped for translation: not applicable; no admin UI changes.
  • I have added and reviewed the user-facing changeset.
  • New features link to the approved discussion above.
  • Screenshots are included for UI changes: not applicable.

AI-generated code disclosure

  • This PR includes AI-generated code. GPT-5.6.

Screenshots / test output

Screenshots: not applicable; no UI changes.

Targeted coverage includes competing writers, stale revisions, namespace isolation, old writers, delete/recreate cycles, migration retries and sandbox adapters.

@changeset-bot

changeset-bot Bot commented Sep 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c4b441b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
emdash Minor
@emdash-cms/cloudflare Minor
@emdash-cms/sandbox-workerd Minor
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/admin Minor
@emdash-cms/auth Minor
@emdash-cms/blocks Minor
create-emdash Minor
@emdash-cms/gutenberg-to-portable-text Minor
@emdash-cms/x402 Minor
@emdash-cms/auth-atproto Patch
@emdash-cms/release-service Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Scope check

This PR changes 1,752 lines across 29 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@github-actions github-actions Bot added the review/needs-review No maintainer or bot review yet label Sep 9, 2026
@pkg-pr-new

pkg-pr-new Bot commented Sep 9, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2980

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2980

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2980

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2980

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2980

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2980

emdash

npm i https://pkg.pr.new/emdash@2980

create-emdash

npm i https://pkg.pr.new/create-emdash@2980

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2980

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2980

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2980

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2980

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2980

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/@emdash-cms/registry-moderation@2980

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2980

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2980

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2980

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2980

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2980

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2980

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2980

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2980

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2980

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2980

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2980

commit: c4b441b

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

@ascorbic ascorbic 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.

Overall I think this is great. Just one comment

}

// Old writers must stamp inserts before the backfill cursor can pass their keys.
for (const table of TABLES) {

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.

Do we need to backfill existing rows? "0" is fine as an initial revision ID, and it'll update for existing unconditional writes. Better to not waste D1 resources unless really needed

@logelog logelog Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I've removed the backfill. Existing records start at "0"; write triggers still invalidate stale revisions, including for older writers and delete/recreate cycles.

@logelog logelog changed the title feat(plugins): add revision-based conditional writes to storage and KV feat(plugins): add atomic conditional writes to storage and KV Sep 10, 2026
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/needs-review No maintainer or bot review yet labels Sep 10, 2026
@logelog
logelog marked this pull request as draft September 10, 2026 15:16
@logelog

logelog commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

I've restored this PR to its original scope: revision based conditional writes for plugin storage and KV, with the backfill removed. The additional work will be proposed separately.

@logelog logelog closed this Sep 10, 2026
@logelog logelog changed the title feat(plugins): add atomic conditional writes to storage and KV feat(plugins): add revision based conditional writes to storage and KV Sep 10, 2026
@logelog logelog reopened this Sep 10, 2026
@logelog
logelog force-pushed the feat/plugin-storage-conditional-writes branch from 5a9f62a to df96537 Compare September 10, 2026 17:19
@logelog
logelog marked this pull request as ready for review September 10, 2026 17:21
@github-actions github-actions Bot removed the review/needs-rereview Author pushed changes since the last review label Sep 10, 2026
@logelog
logelog force-pushed the feat/plugin-storage-conditional-writes branch from df96537 to 68c55bf Compare September 10, 2026 17:30
@github-actions github-actions Bot added the review/needs-rereview Author pushed changes since the last review label Sep 10, 2026
@logelog
logelog requested a review from ascorbic September 10, 2026 19:05
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.

2 participants