feat(plugins): add revision based conditional writes to storage and KV - #2980
feat(plugins): add revision based conditional writes to storage and KV#2980logelog wants to merge 7 commits into
Conversation
🦋 Changeset detectedLatest commit: c4b441b The changes in this PR will be included in the next version bump. This PR includes changesets to release 17 packages
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 |
Scope checkThis 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. |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-moderation
@emdash-cms/registry-verification
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
ascorbic
left a comment
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
|
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. |
5a9f62a to
df96537
Compare
df96537 to
68c55bf
Compare
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.kvin native, Cloudflare and Workerd plugins. A shared counter can use it as follows: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 }, ornullwhen absent. Stored JSONnullstill has a revision.compareAndSet(key, expectedRevision, value)returns{ applied: true, revision }on success or{ applied: false }on conflict. An explicitnullrevision 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
Checklist
AI-generated code disclosure
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.