-
Notifications
You must be signed in to change notification settings - Fork 64
Description
Problem
Currently, when starting a workflow using start(), there is no way to provide a custom runId. The system always auto-generates a unique ID. This creates problems in several common scenarios:
Use Cases Affected
-
Webhook Deduplication: When webhook providers send duplicate events (common with retry logic), each call to
start()creates a new workflow run, even if it's processing the same event. -
Business Entity Uniqueness: Applications often need to ensure only one workflow runs per business entity (e.g., one order processing workflow per order ID). Currently, there's no built-in way to enforce this.
-
Retry-Safe Triggers: In distributed systems, workflow triggers may be retried. Without idempotency support, retries create duplicate workflow runs.
Example Scenario
// Webhook handler receives same event twice due to retry
async function handleOrderWebhook(orderId: string) {
// Problem: Both calls create separate workflow runs!
const run1 = await start(processOrderWorkflow, [orderId]);
const run2 = await start(processOrderWorkflow, [orderId]); // Duplicate!
}Proposed Solution
Add optional runId field to StartOptions:
interface StartOptions {
deploymentId?: string;
runId?: string; // NEW: Enable idempotent workflow creation
}Usage Example
async function handleOrderWebhook(orderId: string) {
const customRunId = `wrun_order_${orderId}`;
// First call creates workflow
const run1 = await start(processOrderWorkflow, [orderId], {
runId: customRunId
});
// Second call returns existing run (idempotent)
const run2 = await start(processOrderWorkflow, [orderId], {
runId: customRunId
});
console.log(run1.runId === run2.runId); // true
}Implementation Requirements
world.runs.create()should accept optionalrunId- If
runIdexists, return existing run instead of throwing error - If
runIdnot provided, auto-generate as before (backward compatible) - All storage backends (world-local, world-postgres, world-vercel) should support this
Benefits
- No breaking changes: Fully backward compatible
- Simpler code: No external deduplication needed
- Safer: Built-in idempotency guarantees
- Standard pattern: Matches idempotency key patterns from Stripe, AWS, etc.
Related
This follows the same pattern already used for step-level idempotency with stepId (see docs).