-
Notifications
You must be signed in to change notification settings - Fork 56
feat: add custom runId support for idempotent workflow creation #86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: add custom runId support for idempotent workflow creation #86
Conversation
Add optional runId field to StartOptions and CreateWorkflowRunRequest to enable idempotent workflow creation. When a custom runId is provided, the system returns the existing workflow run instead of creating a duplicate. Changes: - Add runId?: string to StartOptions interface - Add runId?: string to CreateWorkflowRunRequest interface - Implement idempotent runs.create() in world-local backend - Implement idempotent runs.create() in world-postgres backend with race condition handling - Add runId support to world-testing server and utility functions - Add 3 new integration tests for custom runId feature - Update documentation with examples and use cases Use cases: - Webhook deduplication when providers send duplicate events - Business entity uniqueness (e.g., one workflow per order ID) - Retry-safe workflow triggers in distributed systems The postgres implementation handles race conditions by using onConflictDoNothing and fetching the existing run if the insert returns nothing, ensuring true idempotency even under concurrent requests. Tests: All 339 tests passing (including 3 new integration tests) Closes vercel#85
🦋 Changeset detectedLatest commit: 690e1d5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 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 |
|
@mikearnaldi is attempting to deploy a commit to the Vercel Labs Team on Vercel. A member of the Team first needs to authorize it. |
Wrap writeJSON() in try-catch to handle 409 conflicts when concurrent requests attempt to create a workflow with the same custom runId. When a conflict occurs, fetch and return the existing run to maintain idempotency (matching PostgreSQL implementation pattern).
|
Additional note, for this to work in Vercel the API that handles runs creation |
| runs: { | ||
| async create(data) { | ||
| const runId = `wrun_${monotonicUlid()}`; | ||
| const runId = data.runId ?? `wrun_${monotonicUlid()}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thoughts on
const runId = data.runId ?
`wrun_${data.runId}`
: `wrun_${monotonicUlid()}`;so the prefix is consistent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for contributing! We definitely want to enable idempotency as soon as possible. This PR looks good overall, but I think we'd run into some problems without a few more checks/changes.
World-local requires IDs to be ULIDs because it extracts dates from them, and world-vercel relies on ID being time-sortable (in the backend). We can change world-local to not require ULIDs or prefixes, but this is harder for world-vercel (and our responsibility).
My take is this PR should be extended to:
- Change world-local to not rely on prefixes or ULIDs, truly allowing custom IDs. Add tests for this.
- Change world-vercel to validate that any custom ID follows the
<prefix>_<ulid>schema. Then we can relax this requirement later (once we start supporting arbitrary custom IDs on Vercel backend). Could also export a helper from core/runtime to create IDs with that schema.
That'd be enough for me personally to merge this. Let me know what you think
|
^ Updated my response to reflect some better next steps and outline requirements for what I'd like to see done to unblock this |
|
I don't think we should change the core |
Summary
This PR adds support for custom
runIdin thestart()function to enable idempotent workflow creation. This solves the problem described in #85 where duplicate triggers (e.g., webhooks, events) could create multiple workflow runs for the same logical operation.Changes
Core Implementation
runIdtoCreateWorkflowRunRequestinterfacerunIdtoStartOptionsinterfaceruns.create()that returns existing run ifrunIdalready existsruns.create()with race condition handling usingonConflictDoNothing+ fallback fetchTest Infrastructure
runIdsupport to invoke endpointrunIdparameter toinvoke()helperDocumentation
runIdUse Cases
This feature enables several important patterns:
Example Usage
Testing
All existing tests pass (339 tests across 25 packages), plus 3 new integration tests specifically for this feature.
Closes #85