Materialize waits as entities to prevent duplicate wait_completed events#1057
Conversation
🦋 Changeset detectedLatest commit: 04f58a3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 18 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 |
🧪 E2E Test Results❌ Some tests failed Summary
❌ Failed Tests🌍 Community Worlds (44 failed)mongodb (1 failed):
turso (43 failed):
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
❌ 🌍 Community Worlds
✅ 📋 Other
|
There was a problem hiding this comment.
Pull request overview
This PR adds graceful handling for 409 Conflict responses when creating wait_completed events, addressing race conditions when multiple concurrent workflow invocations attempt to complete the same wait. The changes build on PR #1055 which added orphaned event detection, and work in conjunction with server-side changes that materialize waits as entities to prevent duplicate completions.
Changes:
- Wraps
wait_completedevent creation in try/catch blocks to handle 409 conflicts gracefully - Applies 409 handling in both automatic wait completion (runtime.ts) and manual wake-up (runs.ts)
- Updates event sourcing documentation to reflect that waits are now materialized entities in storage
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| packages/core/src/runtime/runs.ts | Adds 409 conflict handling in wakeUpRun() to count already-completed waits as successful |
| packages/core/src/runtime/runs.test.ts | New test file with unit tests for 409 handling and error cases in wakeUpRun() |
| packages/core/src/runtime.ts | Wraps automatic wait_completed event creation in try/catch to log and skip on 409 conflicts |
| docs/content/docs/how-it-works/event-sourcing.mdx | Updates documentation to clarify waits are materialized entities with atomic completion guarantees |
| .changeset/wait-complete-guard.md | Adds changeset for patch release describing the fix |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
When multiple concurrent workflow invocations race to complete the same wait, the server returns 409 (conflict) for duplicates. This change handles the 409 gracefully in both runtime.ts (sleep elapsed check) and runs.ts (wakeUpRun), preventing crashes and treating already-completed waits as successful. Also updates event-sourcing docs to reflect that waits are now materialized as entities in storage with atomic completion guarantees. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ranch Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… duplicate wait_completed events Adds Wait type/schema to the shared world package and implements wait entity materialization in both local (filesystem) and postgres world implementations, matching the DynamoDB behavior. wait_created creates a wait entity with status 'waiting', and wait_completed transitions it to 'completed' with guards that reject duplicates (409). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
a084c94 to
a5e093c
Compare
…ation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* origin/main: add closure comment in sleep test helper (#1071)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rride Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
VaguelySerious
left a comment
There was a problem hiding this comment.
LGTM, but can we add (or modify) one e2e test to include Promise.all(sleep(1s) x 10) or similar? That'd probably ensure general race-condition safety for waits into the future
I already did that in the previous PR and it's been merged. workflow/workbench/example/workflows/99_e2e.ts Lines 205 to 211 in 0946dad |
|
Replying to @TooTallNate's code review: Backward CompatibilityGood call — both local and postgres would throw 404 for waits created before this code was deployed. For Vercel production this is handled server-side (the backend backfills the wait entity in
|
The server-side changes (vercel/workflow-server#265) have been merged to main and deployed to production, so we no longer need to point at the preview URL. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* origin/main: Remove "workflow/internal/serialization" export (#1082)
Summary
Adds wait entity materialization to the local and postgres world implementations, matching the server-side DynamoDB behavior in workflow-server PR #265. This prevents duplicate
wait_completedevents by creating wait entities with status tracking and conditional writes.Changes by package
@workflow/worldwaits.tswithWaittype,WaitSchema, andWaitStatusSchemawaitfield toEventResultinterface@workflow/world-localwait_createdcreates a wait entity file withstatus: 'waiting'; throws 409 if already existswait_completedtransitions tostatus: 'completed'; throws 404 if not found, 409 if already completedrun_completed,run_failed,run_cancelled) viadeleteAllWaitsForRunwait_createdblocked on terminal runs (can't create new entities)@workflow/world-postgresworkflow_waitstable withwait_statusenum (migration0007_add_waits_table)wait_createduses INSERT withonConflictDoNothing+ check for 409wait_completeduses conditional UPDATE (WHERE status = 'waiting') with fallback SELECT to distinguish 404 vs 409@workflow/corewait_completedevent creation (concurrent VQS invocations)Server-side counterpart: https://github.com/vercel/workflow-server/pull/265
Test plan
pnpm test --filter world-local)pnpm test --filter world-postgres)WORKFLOW_SERVER_URL_OVERRIDEbefore merging🤖 Generated with Claude Code