-
Notifications
You must be signed in to change notification settings - Fork 158
implement event-sourced architecture #621
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
Draft
pranaygp
wants to merge
22
commits into
main
Choose a base branch
from
pranaygp/perf-phase-3b-atomic-events
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
be89ed6
perf: implement event-sourced architecture
pranaygp 4ad45d9
Apply suggestions from code review
pranaygp bf8af8c
Improve invalid event log handling in step/hook/wait
pranaygp 2b80003
Handle serialized workflow run errors correctly
pranaygp aa4183d
log error in failing test
pranaygp 78be52a
Handle queue idempotency in vercel world
pranaygp c56af7a
hotfix for error propogation
pranaygp e543c26
Fix: Incorrect HTTP status code 409 should be 410 for terminal run st…
vercel[bot] 2e547d2
Fix: The code attempts to pass an unsupported `fatal` property when c…
vercel[bot] efd281f
Fix: Code silently skips updating workflowRun if result.run is undefi…
vercel[bot] 62b2e30
Add hook_conflict event type for duplicate token detection
pranaygp e1b9ad7
Add changeset for hook_conflict events
pranaygp 753e6ed
Add unit tests for hook_conflict handling
pranaygp a6273a1
Improve hook-conflict.mdx error guide
pranaygp b629645
Fix docs validation: add hook-conflict to errors index
pranaygp 598b6aa
Fix world-local tests for hook_conflict event behavior
pranaygp cf0a740
Add docs validation requirement to docs-writer agent
pranaygp 54b489e
Add docs type-checking validation to docs-writer agent
pranaygp b135c24
Add specVersion property to World interface for backwards compatibility
pranaygp b01304e
Add migration for spec_version column in postgres schema
pranaygp d01fd9e
Add drizzle migration journal and snapshot for spec_version column
pranaygp 9d01b15
Regenerate postgres migration using drizzle-kit
pranaygp File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| --- | ||
| "@workflow/core": patch | ||
| "@workflow/world": patch | ||
| "@workflow/world-local": patch | ||
| "@workflow/world-postgres": patch | ||
| "@workflow/world-vercel": patch | ||
|
|
||
| "@workflow/web-shared": patch | ||
| --- | ||
|
|
||
| perf: implement event-sourced architecture for runs, steps, and hooks | ||
|
|
||
| - Add run lifecycle events (run_created, run_started, run_completed, run_failed, run_cancelled) | ||
| - Add step_retrying event for non-fatal step failures that will be retried | ||
| - Remove `fatal` field from step_failed event (step_failed now implies terminal failure) | ||
| - Rename step's `lastKnownError` to `error` for consistency with server | ||
| - Update world implementations to create/update entities from events via events.create() | ||
| - Entities (runs, steps, hooks) are now materializations of the event log | ||
| - This makes the system faster, easier to reason about, and resilient to data inconsistencies | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| "@workflow/world": patch | ||
| "@workflow/world-local": patch | ||
| "@workflow/world-postgres": patch | ||
| "@workflow/core": patch | ||
| "@workflow/errors": patch | ||
| --- | ||
|
|
||
| Add hook_conflict event type for duplicate token detection |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| --- | ||
| "@workflow/world": patch | ||
| "@workflow/world-local": patch | ||
| "@workflow/world-vercel": patch | ||
| "@workflow/cli": patch | ||
| "@workflow/web": patch | ||
| "@workflow/web-shared": patch | ||
| --- | ||
|
|
||
| Remove the unused paused/resumed run events and states | ||
|
|
||
| - Remove `run_paused` and `run_resumed` event types | ||
| - Remove `paused` status from `WorkflowRunStatus` | ||
| - Remove `PauseWorkflowRunParams` and `ResumeWorkflowRunParams` types | ||
| - Remove `pauseWorkflowRun` and `resumeWorkflowRun` functions from world-vercel |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| --- | ||
| "@workflow/world": patch | ||
| "@workflow/world-vercel": patch | ||
| "@workflow/world-local": patch | ||
| "@workflow/world-postgres": patch | ||
| "@workflow/web-shared": patch | ||
| --- | ||
|
|
||
| Add `specVersion` property to World interface for backwards compatibility | ||
|
|
||
| - Added `specVersion` property to the World interface that exposes the npm package version | ||
| - Added `specVersion` to WorkflowRun schema and run_created event data | ||
| - World implementations (world-vercel, world-local, world-postgres) now set specVersion from their package version using genversion | ||
| - Server can use specVersion to route operations based on the world version that created the run | ||
| - Added specVersion display to the observability UI attribute panel |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| --- | ||
| title: hook-conflict | ||
| --- | ||
|
|
||
| This error occurs when you try to create a hook with a token that is already in use by another active workflow run. Hook tokens must be unique across all running workflows in your project. | ||
|
|
||
| ## Error Message | ||
|
|
||
| ``` | ||
| Hook token conflict: Hook with token <token> already exists for this project | ||
| ``` | ||
|
|
||
| ## Why This Happens | ||
|
|
||
| Hooks use tokens to identify incoming webhook payloads. When you create a hook with `createHook({ token: "my-token" })`, the Workflow runtime reserves that token for your workflow run. If another workflow run is already using that token, a conflict occurs. | ||
|
|
||
| This typically happens when: | ||
|
|
||
| 1. **Two workflows start simultaneously** with the same hardcoded token | ||
| 2. **A previous workflow run is still waiting** for a hook when a new run tries to use the same token | ||
|
|
||
| ## Common Causes | ||
|
|
||
| ### Hardcoded Token Values | ||
|
|
||
| {/* @skip-typecheck: incomplete code sample */} | ||
| ```typescript lineNumbers | ||
| // Error - multiple concurrent runs will conflict | ||
| export async function processPayment() { | ||
| "use workflow"; | ||
|
|
||
| const hook = createHook({ token: "payment-hook" }); // [!code highlight] | ||
| // If another run is already waiting on "payment-hook", this will fail | ||
| const payment = await hook; | ||
| } | ||
| ``` | ||
|
|
||
| **Solution:** Use unique tokens that include the run ID or other unique identifiers. | ||
|
|
||
| ```typescript lineNumbers | ||
| export async function processPayment(orderId: string) { | ||
| "use workflow"; | ||
|
|
||
| // Include unique identifier in token | ||
| const hook = createHook({ token: `payment-${orderId}` }); // [!code highlight] | ||
| const payment = await hook; | ||
| } | ||
| ``` | ||
|
|
||
| ### Omitting the Token (Auto-generated) | ||
|
|
||
| The safest approach is to let the Workflow runtime generate a unique token automatically: | ||
|
|
||
| ```typescript lineNumbers | ||
| export async function processPayment() { | ||
| "use workflow"; | ||
|
|
||
| const hook = createHook(); // Auto-generated unique token // [!code highlight] | ||
| console.log(`Send webhook to token: ${hook.token}`); | ||
| const payment = await hook; | ||
| } | ||
| ``` | ||
|
|
||
| ## Handling Hook Conflicts in Your Workflow | ||
|
|
||
| When a hook conflict occurs, awaiting the hook will throw a `WorkflowRuntimeError`. You can catch this error to handle the conflict gracefully: | ||
|
|
||
| ```typescript lineNumbers | ||
| import { WorkflowRuntimeError } from "@workflow/errors"; | ||
|
|
||
| export async function processPayment(orderId: string) { | ||
| "use workflow"; | ||
|
|
||
| const hook = createHook({ token: `payment-${orderId}` }); | ||
|
|
||
| try { | ||
| const payment = await hook; // [!code highlight] | ||
| return { success: true, payment }; | ||
| } catch (error) { | ||
| if (error instanceof WorkflowRuntimeError && error.slug === "hook-conflict") { // [!code highlight] | ||
| // Another workflow is already processing this order | ||
| return { success: false, reason: "duplicate-processing" }; | ||
| } | ||
| throw error; // Re-throw other errors | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| This pattern is useful when you want to detect and handle duplicate processing attempts instead of letting the workflow fail. | ||
|
|
||
| ## When Hook Tokens Are Released | ||
|
|
||
| Hook tokens are automatically released when: | ||
|
|
||
| - The workflow run **completes** (successfully or with an error) | ||
| - The workflow run is **cancelled** | ||
| - The hook is explicitly **disposed** | ||
|
|
||
| After a workflow completes, its hook tokens become available for reuse by other workflows. | ||
|
|
||
| ## Best Practices | ||
|
|
||
| 1. **Use auto-generated tokens** when possible - they are guaranteed to be unique | ||
| 2. **Include unique identifiers** if you need custom tokens (order ID, user ID, etc.) | ||
| 3. **Avoid reusing the same token** across multiple concurrent workflow runs | ||
| 4. **Consider using webhooks** (`createWebhook`) if you need a fixed, predictable URL that can receive multiple payloads | ||
|
|
||
| ## Related | ||
|
|
||
| - [Hooks](/docs/foundations/hooks) - Learn more about using hooks in workflows | ||
| - [createWebhook](/docs/api-reference/workflow/create-webhook) - Alternative for fixed webhook URLs |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
TODO: before the final merge, will need to re-check if the changesets need updates since they might go stale by then. lots of changes happening