From 6872eb5e0b5833a99c5dbc5a3b72507c8fc1fc28 Mon Sep 17 00:00:00 2001 From: Duncan Mackenzie Date: Fri, 4 Sep 2026 09:22:53 -0700 Subject: [PATCH 1/4] Fix design-patterns frontmatter, drift, and a stale limit - Add the missing Event Accumulator card to the design-patterns index (present in sidebars.js but absent from the landing page). - Rewrite 13 meta descriptions that fell outside the site's 120-155 character target, and sync the matching PatternCards blurbs on the top-level index and sub-category index pages so they don't drift from the frontmatter they were copied from. - Fix continue-as-new.mdx's "50,000 event history limit" to the documented 51,200 (docs/encyclopedia/workflow/workflow-execution/limits.mdx). - Remove a dangling "In the future - Org-to-Org Nexus, stay tuned" bullet from delayed-callback.mdx (future promise, no actual link). --- docs/design-patterns/continue-as-new.mdx | 2 +- docs/design-patterns/delayed-callback.mdx | 3 +- docs/design-patterns/delayed-retry.mdx | 2 +- .../downstream-rate-limiting.mdx | 2 +- docs/design-patterns/eager-workflow-start.mdx | 2 +- .../early-return-local-activities.mdx | 2 +- .../entity-lifecycle-patterns.mdx | 2 +- docs/design-patterns/entity-workflow.mdx | 2 +- .../error-handling-patterns.mdx | 6 ++-- .../external-interaction-patterns.mdx | 4 +-- docs/design-patterns/fast-slow-retries.mdx | 2 +- docs/design-patterns/index.mdx | 28 +++++++++++-------- docs/design-patterns/local-activities.mdx | 2 +- .../performance-latency-patterns.mdx | 6 ++-- docs/design-patterns/pick-first.mdx | 2 +- .../qos-throughput-patterns.mdx | 2 +- .../request-response-via-updates.mdx | 2 +- docs/design-patterns/retry-metrics.mdx | 2 +- .../task-orchestration-patterns.mdx | 4 +-- .../workflow-messaging-patterns.mdx | 2 +- 20 files changed, 42 insertions(+), 37 deletions(-) diff --git a/docs/design-patterns/continue-as-new.mdx b/docs/design-patterns/continue-as-new.mdx index 7fc4fd7958..d9397d0a34 100644 --- a/docs/design-patterns/continue-as-new.mdx +++ b/docs/design-patterns/continue-as-new.mdx @@ -16,7 +16,7 @@ By archiving old event history and starting fresh, Continue-As-New also reduces ## Problem -In long-running Workflows, you often need to execute periodic tasks indefinitely, process unbounded streams of data without accumulating history, implement infinite loops that run for months or years, avoid hitting the 50,000 event history limit, and maintain Workflow state across logical restarts. +In long-running Workflows, you often need to execute periodic tasks indefinitely, process unbounded streams of data without accumulating history, implement infinite loops that run for months or years, avoid hitting the 51,200 event history limit, and maintain Workflow state across logical restarts. Without Continue-As-New, you must manually stop and restart Workflows (losing continuity), risk hitting history limits and Workflow failures, implement external orchestration to manage Workflow lifecycle, and accept degraded performance as history grows large. diff --git a/docs/design-patterns/delayed-callback.mdx b/docs/design-patterns/delayed-callback.mdx index 8ccb1356af..eae108e562 100644 --- a/docs/design-patterns/delayed-callback.mdx +++ b/docs/design-patterns/delayed-callback.mdx @@ -2,7 +2,7 @@ id: delayed-callback title: "Delayed Callback (Webhooks)" sidebar_label: "Delayed Callback" -description: "Integrates webhooks durably: receive inbound webhooks via Signals, fire delayed outbound callbacks with durable timers, and complete Activities asynchronously via task tokens." +description: "Webhooks become durable: inbound calls arrive as Signals, outbound calls fire after a durable sleep, and Activities complete later via task tokens." --- import Tabs from '@theme/Tabs'; @@ -753,4 +753,3 @@ func CompleteJob(ctx context.Context, c client.Client, jobID string, result stri - [Polling External Services](/design-patterns/polling) — alternative to callbacks when the external system does not support webhooks - [Delayed Start](/design-patterns/delayed-start) — defer Workflow execution to a future time without `workflow.sleep()` - [Long-Running Activity](/design-patterns/long-running-activity) — heartbeating pattern for activities that run for extended periods -- **In the future** - Org-to-Org Nexus, stay tuned. diff --git a/docs/design-patterns/delayed-retry.mdx b/docs/design-patterns/delayed-retry.mdx index a9575bdbd1..815a9ada53 100644 --- a/docs/design-patterns/delayed-retry.mdx +++ b/docs/design-patterns/delayed-retry.mdx @@ -2,7 +2,7 @@ id: delayed-retry title: "Delayed Retry" sidebar_label: "Delayed Retry" -description: "Override the next retry interval for a specific failure using nextRetryDelay on ApplicationFailure. Use when an error carries information about how long to wait before retrying." +description: "Override one failure's retry interval with nextRetryDelay on ApplicationFailure, matching the wait time the error reports." --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/downstream-rate-limiting.mdx b/docs/design-patterns/downstream-rate-limiting.mdx index 0a2d00ee19..9bec5a6ed1 100644 --- a/docs/design-patterns/downstream-rate-limiting.mdx +++ b/docs/design-patterns/downstream-rate-limiting.mdx @@ -2,7 +2,7 @@ id: downstream-rate-limiting title: "Downstream Rate Limiting" sidebar_label: "Downstream Rate Limiting" -description: "Caps Activity execution rate against a downstream service by routing throttled Activities to a dedicated Task Queue backed by Workers configured with a throughput limit." +description: "Rate-limits calls to a downstream service by routing throttled Activities to a dedicated Task Queue with a server-enforced throughput cap." --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/eager-workflow-start.mdx b/docs/design-patterns/eager-workflow-start.mdx index 88cf4cbac5..0d2f60e521 100644 --- a/docs/design-patterns/eager-workflow-start.mdx +++ b/docs/design-patterns/eager-workflow-start.mdx @@ -2,7 +2,7 @@ id: eager-workflow-start title: "Eager Workflow Start" sidebar_label: "Eager Workflow Start" -description: "Dispatch the first Workflow Task directly to a co-located Worker, bypassing the Temporal Matching Service. Requires the starter and Worker to share the same process and client connection." +description: "Eager Workflow Start sends the first Workflow Task directly to a co-located Worker, skipping the Matching Service to cut startup latency." --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/early-return-local-activities.mdx b/docs/design-patterns/early-return-local-activities.mdx index 6f70dc4a62..2a777dcb05 100644 --- a/docs/design-patterns/early-return-local-activities.mdx +++ b/docs/design-patterns/early-return-local-activities.mdx @@ -2,7 +2,7 @@ id: early-return-local-activities title: "Early Return + Local Activities" sidebar_label: "Early Return + Local Activities" -description: "Extends Early Return by running Phase 1 Activities as Local Activities. The client receives its response after Phase 1 completes entirely in-process, achieving the lowest possible first-response latency." +description: "Extends Early Return by running Phase 1 as Local Activities, so the client's first response comes from in-process work, not a server call." --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/entity-lifecycle-patterns.mdx b/docs/design-patterns/entity-lifecycle-patterns.mdx index 3531ce53f9..0968684d59 100644 --- a/docs/design-patterns/entity-lifecycle-patterns.mdx +++ b/docs/design-patterns/entity-lifecycle-patterns.mdx @@ -16,7 +16,7 @@ These patterns model long-lived business entities as Workflows and keep those Wo href: "/design-patterns/entity-workflow", icon: "entity-workflow-icon.svg", title: "Entity Workflow", - description: "Models a long-lived business entity as a single Workflow that persists for the entity's entire lifetime, handling every state transition through Signals and Updates.", + description: "Models a long-lived business entity — a user account, device, or order — as a single Workflow, with Signals and Updates driving every state transition.", }, { href: "/design-patterns/continue-as-new", diff --git a/docs/design-patterns/entity-workflow.mdx b/docs/design-patterns/entity-workflow.mdx index b00bfe6d65..b2e3528438 100644 --- a/docs/design-patterns/entity-workflow.mdx +++ b/docs/design-patterns/entity-workflow.mdx @@ -2,7 +2,7 @@ id: entity-workflow title: "Entity Workflow Pattern" sidebar_label: "Entity Workflow" -description: "Models long-lived business entities as individual Workflows that persist for the entity's entire lifetime, handling all state transitions through Signals and Updates." +description: "A long-lived business entity — a user account, device, or order — gets one Workflow per instance, with Signals and Updates driving every state transition." --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/error-handling-patterns.mdx b/docs/design-patterns/error-handling-patterns.mdx index 26b75bb4af..54a454f9e8 100644 --- a/docs/design-patterns/error-handling-patterns.mdx +++ b/docs/design-patterns/error-handling-patterns.mdx @@ -34,19 +34,19 @@ These patterns control how Temporal retries Activities, surfaces persistent fail href: "/design-patterns/delayed-retry", icon: "delayed-retry-icon.svg", title: "Delayed Retry", - description: "Override the next retry interval for a specific failure using nextRetryDelay on ApplicationFailure. Use when an error carries information about how long to wait before retrying.", + description: "Override one failure's retry interval with nextRetryDelay on ApplicationFailure, matching the wait time the error reports.", }, { href: "/design-patterns/fast-slow-retries", icon: "fast-slow-retries-icon.svg", title: "Fast/Slow Retries", - description: "Retries aggressively with a short interval first, then shifts to a long interval when fast retries are exhausted, keeping the Workflow alive until the downstream system recovers.", + description: "Retries fast with a short interval first, then shifts to a slow, unlimited interval so the Workflow outlasts an extended downstream outage.", }, { href: "/design-patterns/retry-metrics", icon: "retry-metrics-icon.svg", title: "Retry Alerting via Metrics", - description: "Emits a custom metric from inside the Activity when the attempt count crosses a threshold, surfacing silent persistent failures to on-call teams before an SLA breach.", + description: "Emits a metric from the Activity when attempts cross a threshold, so on-call teams see persistent failures before an SLA breach.", }, { href: "/design-patterns/resumable-activity", diff --git a/docs/design-patterns/external-interaction-patterns.mdx b/docs/design-patterns/external-interaction-patterns.mdx index 7c073be6b1..075c592d04 100644 --- a/docs/design-patterns/external-interaction-patterns.mdx +++ b/docs/design-patterns/external-interaction-patterns.mdx @@ -2,7 +2,7 @@ id: external-interaction-patterns title: "External Interaction Patterns" sidebar_label: "External Interaction Patterns" -description: "Pattern selection guide for waiting on or interacting with systems and actors outside the Workflow." +description: "Compares five patterns for waiting on external systems and human decisions: polling, heartbeating Activities, delayed start, webhooks, and approval." --- import PatternCards from '@site/src/components/PatternCards'; @@ -40,7 +40,7 @@ These patterns cover how a Workflow waits on or interacts with the world outside href: "/design-patterns/delayed-callback", icon: "webhooks-icon.svg", title: "Delayed Callback (Webhooks)", - description: "Integrates webhooks durably: receive inbound webhooks via Signals, fire delayed outbound callbacks with durable timers, and complete Activities asynchronously via task tokens.", + description: "Webhooks become durable: inbound calls arrive as Signals, outbound calls fire after a durable sleep, and Activities complete later via task tokens.", }, ]} /> diff --git a/docs/design-patterns/fast-slow-retries.mdx b/docs/design-patterns/fast-slow-retries.mdx index 47e8b08f27..f021761cbd 100644 --- a/docs/design-patterns/fast-slow-retries.mdx +++ b/docs/design-patterns/fast-slow-retries.mdx @@ -2,7 +2,7 @@ id: fast-slow-retries title: "Fast/Slow Retries" sidebar_label: "Fast/Slow Retries" -description: "Try aggressively with a short interval first, then shift to a long interval when fast retries are exhausted, keeping the Workflow alive until the downstream system recovers." +description: "Retry fast with a short interval first, then shift to a slow, unlimited interval so the Workflow outlasts an extended downstream outage." --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/index.mdx b/docs/design-patterns/index.mdx index dfb6f71118..829d52446f 100644 --- a/docs/design-patterns/index.mdx +++ b/docs/design-patterns/index.mdx @@ -30,7 +30,7 @@ Having these patterns in your toolbox helps you solve recurring problems in a ba href: "/design-patterns/pick-first", icon: "pick-first-icon.svg", title: "Pick First (Race)", - description: "Starts multiple Activities in parallel and uses the first result, cancelling the rest.", + description: "Races multiple Activities in parallel, returns the first completed result, and cancels the remaining Activities using SDK cancellation scopes.", }, ]} /> @@ -47,7 +47,13 @@ Having these patterns in your toolbox helps you solve recurring problems in a ba href: "/design-patterns/request-response-via-updates", icon: "request-response-icon.svg", title: "Request-Response via Updates", - description: "Synchronous request-response with validation. Updates modify state and return results directly.", + description: "Uses a Workflow Update to validate a request, modify Workflow state, and return a typed result to the caller synchronously, with strong consistency.", + }, + { + href: "/design-patterns/event-accumulator", + icon: "event-accumulator-icon.svg", + title: "Event Accumulator", + description: "Durably collect and deduplicate signals from multiple senders, then process the batch after a sliding inactivity timeout.", }, ]} /> @@ -58,7 +64,7 @@ Having these patterns in your toolbox helps you solve recurring problems in a ba href: "/design-patterns/entity-workflow", icon: "entity-workflow-icon.svg", title: "Entity Workflow", - description: "Models long-lived business entities as individual Workflows that persist for the entity's entire lifetime, handling all state transitions through Signals and Updates.", + description: "A long-lived business entity — a user account, device, or order — gets one Workflow per instance, with Signals and Updates driving every state transition.", }, { href: "/design-patterns/continue-as-new", @@ -99,7 +105,7 @@ Having these patterns in your toolbox helps you solve recurring problems in a ba href: "/design-patterns/delayed-callback", icon: "webhooks-icon.svg", title: "Delayed Callback (Webhooks)", - description: "Integrates webhooks durably: receive inbound webhooks via Signals, fire delayed outbound callbacks with durable timers, and complete Activities asynchronously via task tokens.", + description: "Webhooks become durable: inbound calls arrive as Signals, outbound calls fire after a durable sleep, and Activities complete later via task tokens.", }, { href: "/design-patterns/approval", @@ -151,19 +157,19 @@ Having these patterns in your toolbox helps you solve recurring problems in a ba href: "/design-patterns/delayed-retry", icon: "delayed-retry-icon.svg", title: "Delayed Retry", - description: "Override the next retry interval for a specific failure using nextRetryDelay on ApplicationFailure. Use when an error carries information about how long to wait before retrying.", + description: "Override one failure's retry interval with nextRetryDelay on ApplicationFailure, matching the wait time the error reports.", }, { href: "/design-patterns/fast-slow-retries", icon: "fast-slow-retries-icon.svg", title: "Fast/Slow Retries", - description: "Try aggressively with a short interval first, then shift to a long interval when fast retries are exhausted, keeping the Workflow alive until the downstream system recovers.", + description: "Retry fast with a short interval first, then shift to a slow, unlimited interval so the Workflow outlasts an extended downstream outage.", }, { href: "/design-patterns/retry-metrics", icon: "retry-metrics-icon.svg", title: "Retry Alerting via Metrics", - description: "Emit a custom metric from inside the Activity when the attempt count crosses a threshold, surfacing silent persistent failures to on-call teams before an SLA breach.", + description: "Emit a metric from the Activity when attempts cross a threshold, so on-call teams see persistent failures before an SLA breach.", }, { href: "/design-patterns/resumable-activity", @@ -209,7 +215,7 @@ Having these patterns in your toolbox helps you solve recurring problems in a ba href: "/design-patterns/downstream-rate-limiting", icon: "downstream-rate-limiting-icon.svg", title: "Downstream Rate Limiting", - description: "Caps Activity execution rate against a downstream service by routing throttled Activities to a dedicated Task Queue backed by Workers configured with a throughput limit.", + description: "Rate-limits calls to a downstream service by routing throttled Activities to a dedicated Task Queue with a server-enforced throughput cap.", }, { href: "/design-patterns/priority-task-queues", @@ -232,19 +238,19 @@ Having these patterns in your toolbox helps you solve recurring problems in a ba href: "/design-patterns/local-activities", icon: "local-activities-icon.svg", title: "Local Activities", - description: "Run Activity functions in-process inside the Workflow Task, eliminating all server scheduling round-trips. Best for short, idempotent Activities on a latency-sensitive path.", + description: "Local Activities run inside the Worker process, skipping server round-trips for short, idempotent Activities on a latency-sensitive path.", }, { href: "/design-patterns/early-return-local-activities", icon: "early-return-local-activities-icon.svg", title: "Early Return + Local Activities", - description: "Extends Early Return by running Phase 1 Activities as Local Activities. The client receives its response after Phase 1 completes entirely in-process, achieving the lowest possible first-response latency.", + description: "Extends Early Return by running Phase 1 as Local Activities, so the client's first response comes from in-process work, not a server call.", }, { href: "/design-patterns/eager-workflow-start", icon: "eager-workflow-start-icon.svg", title: "Eager Workflow Start", - description: "Dispatch the first Workflow Task directly to a co-located Worker, bypassing the Temporal Matching Service. Requires the starter and Worker to share the same process and client connection.", + description: "Eager Workflow Start sends the first Workflow Task directly to a co-located Worker, skipping the Matching Service to cut startup latency.", }, ]} /> diff --git a/docs/design-patterns/local-activities.mdx b/docs/design-patterns/local-activities.mdx index 1733e63e33..95962cef96 100644 --- a/docs/design-patterns/local-activities.mdx +++ b/docs/design-patterns/local-activities.mdx @@ -2,7 +2,7 @@ id: local-activities title: "Local Activities" sidebar_label: "Local Activities" -description: "Run Activity functions in-process inside the Workflow Task, eliminating all server scheduling round-trips. Best for short, idempotent Activities on a latency-sensitive path." +description: "Local Activities run inside the Worker process, skipping server round-trips for short, idempotent Activities on a latency-sensitive path." --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/performance-latency-patterns.mdx b/docs/design-patterns/performance-latency-patterns.mdx index 31b600a245..a466c162eb 100644 --- a/docs/design-patterns/performance-latency-patterns.mdx +++ b/docs/design-patterns/performance-latency-patterns.mdx @@ -56,19 +56,19 @@ Eager Workflow Start is not available in the TypeScript SDK, but the latency gap href: "/design-patterns/local-activities", icon: "local-activities-icon.svg", title: "Local Activities", - description: "Run Activity functions in-process inside the Workflow Task, eliminating all server scheduling round-trips. Best for short, idempotent Activities on a latency-sensitive path.", + description: "Local Activities run inside the Worker process, skipping server round-trips for short, idempotent Activities on a latency-sensitive path.", }, { href: "/design-patterns/early-return-local-activities", icon: "early-return-local-activities-icon.svg", title: "Early Return + Local Activities", - description: "Extends Early Return by running Phase 1 Activities as Local Activities. The client receives its response after Phase 1 completes entirely in-process, achieving the lowest possible first-response latency.", + description: "Extends Early Return by running Phase 1 as Local Activities, so the client's first response comes from in-process work, not a server call.", }, { href: "/design-patterns/eager-workflow-start", icon: "eager-workflow-start-icon.svg", title: "Eager Workflow Start", - description: "Dispatch the first Workflow Task directly to a co-located Worker, bypassing the Temporal Matching Service. Requires the starter and Worker to share the same process and client connection.", + description: "Eager Workflow Start sends the first Workflow Task directly to a co-located Worker, skipping the Matching Service to cut startup latency.", }, ]} /> diff --git a/docs/design-patterns/pick-first.mdx b/docs/design-patterns/pick-first.mdx index b4ad5e1930..32aeb173e2 100644 --- a/docs/design-patterns/pick-first.mdx +++ b/docs/design-patterns/pick-first.mdx @@ -2,7 +2,7 @@ id: pick-first title: "Pick First Pattern" sidebar_label: "Pick First (Race)" -description: "Starts multiple Activities in parallel and uses the first result, cancelling the rest." +description: "Races multiple Activities in parallel, returns the first completed result, and cancels the remaining Activities using SDK cancellation scopes." --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/qos-throughput-patterns.mdx b/docs/design-patterns/qos-throughput-patterns.mdx index 1842483a41..321d68a3c7 100644 --- a/docs/design-patterns/qos-throughput-patterns.mdx +++ b/docs/design-patterns/qos-throughput-patterns.mdx @@ -16,7 +16,7 @@ These patterns control how fast work executes, protect downstream services from href: "/design-patterns/downstream-rate-limiting", icon: "downstream-rate-limiting-icon.svg", title: "Downstream Rate Limiting", - description: "Caps the Activity execution rate against a downstream service by routing throttled Activities to a dedicated Task Queue whose Workers enforce a throughput limit.", + description: "Rate-limits calls to a downstream service by routing throttled Activities to a dedicated Task Queue with a server-enforced throughput cap.", }, { href: "/design-patterns/priority-task-queues", diff --git a/docs/design-patterns/request-response-via-updates.mdx b/docs/design-patterns/request-response-via-updates.mdx index ae397aa5ab..aee7d59c4e 100644 --- a/docs/design-patterns/request-response-via-updates.mdx +++ b/docs/design-patterns/request-response-via-updates.mdx @@ -2,7 +2,7 @@ id: request-response-via-updates title: "Request-Response via Updates" sidebar_label: "Request-Response via Updates" -description: "Synchronous request-response with validation. Updates modify state and return results directly." +description: "Uses a Workflow Update to validate a request, modify Workflow state, and return a typed result to the caller synchronously, with strong consistency." --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/retry-metrics.mdx b/docs/design-patterns/retry-metrics.mdx index 7ee3236cfe..824cd79475 100644 --- a/docs/design-patterns/retry-metrics.mdx +++ b/docs/design-patterns/retry-metrics.mdx @@ -2,7 +2,7 @@ id: retry-metrics title: "Retry Alerting via Metrics" sidebar_label: "Retry Alerting via Metrics" -description: "Emit a custom metric from inside the Activity when the attempt count crosses a threshold, surfacing silent persistent failures to on-call teams before an SLA breach." +description: "Emit a metric from the Activity when attempts cross a threshold, so on-call teams see persistent failures before an SLA breach." --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/task-orchestration-patterns.mdx b/docs/design-patterns/task-orchestration-patterns.mdx index d0395a5f6b..9adffe2eba 100644 --- a/docs/design-patterns/task-orchestration-patterns.mdx +++ b/docs/design-patterns/task-orchestration-patterns.mdx @@ -2,7 +2,7 @@ id: task-orchestration-patterns title: "Task Orchestration Patterns" sidebar_label: "Task Orchestration Patterns" -description: "Pattern selection guide for composing and coordinating multiple units of work within a Workflow." +description: "Compares Child Workflows, Parallel Execution, and Pick First (Race) for decomposing, running concurrently, and racing work inside a single Workflow." --- import PatternCards from '@site/src/components/PatternCards'; @@ -28,7 +28,7 @@ These patterns compose and coordinate multiple units of work within a Workflow href: "/design-patterns/pick-first", icon: "pick-first-icon.svg", title: "Pick First (Race)", - description: "Starts multiple Activities in parallel, takes the first result to arrive, and cancels the rest.", + description: "Races multiple Activities in parallel, takes the first result to arrive, and cancels the rest.", }, ]} /> diff --git a/docs/design-patterns/workflow-messaging-patterns.mdx b/docs/design-patterns/workflow-messaging-patterns.mdx index 4a4fcdfcf6..1ce2144204 100644 --- a/docs/design-patterns/workflow-messaging-patterns.mdx +++ b/docs/design-patterns/workflow-messaging-patterns.mdx @@ -22,7 +22,7 @@ These patterns cover how external callers communicate with running Workflows — href: "/design-patterns/request-response-via-updates", icon: "request-response-icon.svg", title: "Request-Response via Updates", - description: "Sends a request into a running Workflow and receives a validated result on the same call, using an Update handler.", + description: "Sends a request into a running Workflow through an Update, which validates it, modifies state, and returns a typed result on the same call.", }, { href: "/design-patterns/event-accumulator", From d6cc888011da8db1f07a9f498153b2ec950d28e5 Mon Sep 17 00:00:00 2001 From: Duncan Mackenzie Date: Fri, 4 Sep 2026 09:29:37 -0700 Subject: [PATCH 2/4] Document the Design Patterns section and tag its pages - Add a "Design patterns" entry to readme/INFORMATION-ARCHITECTURE.md describing its audience, template, and how it differs from Guides and Best Practices. The section (added in #4746) had no IA entry. - Add frontmatter tags to all 46 pages in docs/design-patterns/, which had none (unlike ~90% of docs/ pages). Every page gets a shared "Design Patterns" tag; leaf pages also get one topic tag reused from the site's existing vocabulary (Activities, Workflows, Signals, Updates, Errors, Child Workflows, Task Queues, Workers, Timers, Metrics, Failures). - Cross-link docs/evaluate/use-cases-design-patterns.mdx (which predates this catalog) to the canonical Saga, Approval, and Long-Running Activity pattern pages it was duplicating without linking to. --- .../activity-dependency-injection.mdx | 3 +++ docs/design-patterns/approval.mdx | 3 +++ docs/design-patterns/batch-iterator.mdx | 3 +++ .../batch-processing-patterns.mdx | 2 ++ docs/design-patterns/child-workflows.mdx | 3 +++ docs/design-patterns/continue-as-new.mdx | 3 +++ docs/design-patterns/delayed-callback.mdx | 3 +++ docs/design-patterns/delayed-retry.mdx | 3 +++ docs/design-patterns/delayed-start.mdx | 3 +++ .../distributed-transaction-patterns.mdx | 2 ++ .../downstream-rate-limiting.mdx | 3 +++ docs/design-patterns/eager-workflow-start.mdx | 3 +++ .../early-return-local-activities.mdx | 3 +++ docs/design-patterns/early-return.mdx | 3 +++ .../entity-lifecycle-patterns.mdx | 2 ++ docs/design-patterns/entity-workflow.mdx | 3 +++ .../error-handling-patterns.mdx | 2 ++ docs/design-patterns/event-accumulator.mdx | 3 +++ .../external-interaction-patterns.mdx | 2 ++ docs/design-patterns/fairness.mdx | 3 +++ .../fanout-child-workflows.mdx | 3 +++ docs/design-patterns/fast-slow-retries.mdx | 3 +++ docs/design-patterns/fixed-count-retries.mdx | 3 +++ .../fixed-wall-time-retries.mdx | 3 +++ docs/design-patterns/index.mdx | 2 ++ docs/design-patterns/local-activities.mdx | 3 +++ .../design-patterns/long-running-activity.mdx | 3 +++ docs/design-patterns/mapreduce-tree.mdx | 3 +++ docs/design-patterns/non-retryable-errors.mdx | 3 +++ docs/design-patterns/parallel-execution.mdx | 3 +++ .../performance-latency-patterns.mdx | 2 ++ docs/design-patterns/pick-first.mdx | 3 +++ docs/design-patterns/polling.mdx | 3 +++ docs/design-patterns/priority-task-queues.mdx | 3 +++ .../qos-throughput-patterns.mdx | 2 ++ .../request-response-via-updates.mdx | 3 +++ docs/design-patterns/resumable-activity.mdx | 3 +++ docs/design-patterns/retry-metrics.mdx | 3 +++ docs/design-patterns/saga-pattern.mdx | 3 +++ docs/design-patterns/signal-with-start.mdx | 3 +++ docs/design-patterns/sliding-window.mdx | 3 +++ .../task-orchestration-patterns.mdx | 2 ++ docs/design-patterns/updatable-timer.mdx | 3 +++ .../worker-configuration-patterns.mdx | 2 ++ .../worker-specific-taskqueue.mdx | 3 +++ .../workflow-messaging-patterns.mdx | 2 ++ docs/evaluate/use-cases-design-patterns.mdx | 8 +++++-- readme/INFORMATION-ARCHITECTURE.md | 22 +++++++++++++++++++ 48 files changed, 155 insertions(+), 2 deletions(-) diff --git a/docs/design-patterns/activity-dependency-injection.mdx b/docs/design-patterns/activity-dependency-injection.mdx index d3c99242fd..c8a84de570 100644 --- a/docs/design-patterns/activity-dependency-injection.mdx +++ b/docs/design-patterns/activity-dependency-injection.mdx @@ -3,6 +3,9 @@ id: activity-dependency-injection title: "Activity Dependency Injection" sidebar_label: "Activity Dependency Injection" description: "Injects external dependencies into Activities at Worker startup, keeping Workflow code deterministic and Activities testable." +tags: + - Design Patterns + - Workers --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/approval.mdx b/docs/design-patterns/approval.mdx index bafcafc9d1..b16a69a22a 100644 --- a/docs/design-patterns/approval.mdx +++ b/docs/design-patterns/approval.mdx @@ -3,6 +3,9 @@ id: approval title: "Approval Pattern" sidebar_label: "Approval" description: "Human-in-the-loop Workflows that block until external approval decisions are made. Uses Signals to capture approval data with metadata." +tags: + - Design Patterns + - Signals --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/batch-iterator.mdx b/docs/design-patterns/batch-iterator.mdx index 05f174fbe8..f1579dbca0 100644 --- a/docs/design-patterns/batch-iterator.mdx +++ b/docs/design-patterns/batch-iterator.mdx @@ -3,6 +3,9 @@ id: batch-iterator title: "Batch Iterator" sidebar_label: "Batch Iterator" description: "Pages through unbounded datasets using Continue-As-New to prevent history overflow while maintaining exactly-once processing guarantees." +tags: + - Design Patterns + - Workflows --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/batch-processing-patterns.mdx b/docs/design-patterns/batch-processing-patterns.mdx index 88d2c7246e..cd6ef1dbc0 100644 --- a/docs/design-patterns/batch-processing-patterns.mdx +++ b/docs/design-patterns/batch-processing-patterns.mdx @@ -3,6 +3,8 @@ id: batch-processing-patterns title: "Batch Processing Patterns" sidebar_label: "Batch Processing Patterns" description: "Compare Fan-Out, Batch Iterator, Sliding Window, and MapReduce Tree patterns for processing large record sets reliably at scale." +tags: + - Design Patterns --- import PatternCards from '@site/src/components/PatternCards'; diff --git a/docs/design-patterns/child-workflows.mdx b/docs/design-patterns/child-workflows.mdx index c134451c25..4e898b182e 100644 --- a/docs/design-patterns/child-workflows.mdx +++ b/docs/design-patterns/child-workflows.mdx @@ -3,6 +3,9 @@ id: child-workflows title: "Child Workflows Pattern" sidebar_label: "Child Workflows" description: "Decomposes complex Workflows into smaller, reusable units. Each child has an independent Workflow ID, history, and lifecycle." +tags: + - Design Patterns + - Child Workflows --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/continue-as-new.mdx b/docs/design-patterns/continue-as-new.mdx index d9397d0a34..85337100a5 100644 --- a/docs/design-patterns/continue-as-new.mdx +++ b/docs/design-patterns/continue-as-new.mdx @@ -3,6 +3,9 @@ id: continue-as-new title: "Continue-As-New Pattern" sidebar_label: "Continue-As-New" description: "Prevents unbounded history growth by completing the current execution and starting a new one with fresh history." +tags: + - Design Patterns + - Workflows --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/delayed-callback.mdx b/docs/design-patterns/delayed-callback.mdx index eae108e562..d7b030bcb2 100644 --- a/docs/design-patterns/delayed-callback.mdx +++ b/docs/design-patterns/delayed-callback.mdx @@ -3,6 +3,9 @@ id: delayed-callback title: "Delayed Callback (Webhooks)" sidebar_label: "Delayed Callback" description: "Webhooks become durable: inbound calls arrive as Signals, outbound calls fire after a durable sleep, and Activities complete later via task tokens." +tags: + - Design Patterns + - Signals --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/delayed-retry.mdx b/docs/design-patterns/delayed-retry.mdx index 815a9ada53..82554dad7b 100644 --- a/docs/design-patterns/delayed-retry.mdx +++ b/docs/design-patterns/delayed-retry.mdx @@ -3,6 +3,9 @@ id: delayed-retry title: "Delayed Retry" sidebar_label: "Delayed Retry" description: "Override one failure's retry interval with nextRetryDelay on ApplicationFailure, matching the wait time the error reports." +tags: + - Design Patterns + - Errors --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/delayed-start.mdx b/docs/design-patterns/delayed-start.mdx index 9dc89a5a39..8bfc973af7 100644 --- a/docs/design-patterns/delayed-start.mdx +++ b/docs/design-patterns/delayed-start.mdx @@ -3,6 +3,9 @@ id: delayed-start title: "Delayed Start Pattern" sidebar_label: "Delayed Start" description: "Creates Workflows immediately but defers execution until a specified delay expires. Fits one-time scheduled operations and grace periods." +tags: + - Design Patterns + - Workflows --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/distributed-transaction-patterns.mdx b/docs/design-patterns/distributed-transaction-patterns.mdx index ac319bb60d..92204107ae 100644 --- a/docs/design-patterns/distributed-transaction-patterns.mdx +++ b/docs/design-patterns/distributed-transaction-patterns.mdx @@ -3,6 +3,8 @@ id: distributed-transaction-patterns title: "Distributed Transaction Patterns" sidebar_label: "Distributed Transaction Patterns" description: "Pattern selection guide for distributed transactions, with a decision tree for choosing between Saga and Early Return." +tags: + - Design Patterns --- import PatternCards from '@site/src/components/PatternCards'; diff --git a/docs/design-patterns/downstream-rate-limiting.mdx b/docs/design-patterns/downstream-rate-limiting.mdx index 9bec5a6ed1..128e1ad154 100644 --- a/docs/design-patterns/downstream-rate-limiting.mdx +++ b/docs/design-patterns/downstream-rate-limiting.mdx @@ -3,6 +3,9 @@ id: downstream-rate-limiting title: "Downstream Rate Limiting" sidebar_label: "Downstream Rate Limiting" description: "Rate-limits calls to a downstream service by routing throttled Activities to a dedicated Task Queue with a server-enforced throughput cap." +tags: + - Design Patterns + - Task Queues --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/eager-workflow-start.mdx b/docs/design-patterns/eager-workflow-start.mdx index 0d2f60e521..fdcd216f4c 100644 --- a/docs/design-patterns/eager-workflow-start.mdx +++ b/docs/design-patterns/eager-workflow-start.mdx @@ -3,6 +3,9 @@ id: eager-workflow-start title: "Eager Workflow Start" sidebar_label: "Eager Workflow Start" description: "Eager Workflow Start sends the first Workflow Task directly to a co-located Worker, skipping the Matching Service to cut startup latency." +tags: + - Design Patterns + - Workers --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/early-return-local-activities.mdx b/docs/design-patterns/early-return-local-activities.mdx index 2a777dcb05..d79f8a36ac 100644 --- a/docs/design-patterns/early-return-local-activities.mdx +++ b/docs/design-patterns/early-return-local-activities.mdx @@ -3,6 +3,9 @@ id: early-return-local-activities title: "Early Return + Local Activities" sidebar_label: "Early Return + Local Activities" description: "Extends Early Return by running Phase 1 as Local Activities, so the client's first response comes from in-process work, not a server call." +tags: + - Design Patterns + - Activities --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/early-return.mdx b/docs/design-patterns/early-return.mdx index 7afbeafa2f..ee48673c95 100644 --- a/docs/design-patterns/early-return.mdx +++ b/docs/design-patterns/early-return.mdx @@ -3,6 +3,9 @@ id: early-return title: "Early Return (Update with Start)" sidebar_label: "Early Return" description: "Synchronous initialization with asynchronous completion. Returns results immediately while processing continues in the background." +tags: + - Design Patterns + - Updates --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/entity-lifecycle-patterns.mdx b/docs/design-patterns/entity-lifecycle-patterns.mdx index 0968684d59..0689bb50cc 100644 --- a/docs/design-patterns/entity-lifecycle-patterns.mdx +++ b/docs/design-patterns/entity-lifecycle-patterns.mdx @@ -3,6 +3,8 @@ id: entity-lifecycle-patterns title: "Entity & Lifecycle Patterns" sidebar_label: "Entity & Lifecycle Patterns" description: "Pattern selection guide for modeling long-lived stateful entities and managing Workflow history growth over time." +tags: + - Design Patterns --- import PatternCards from '@site/src/components/PatternCards'; diff --git a/docs/design-patterns/entity-workflow.mdx b/docs/design-patterns/entity-workflow.mdx index b2e3528438..56ef844093 100644 --- a/docs/design-patterns/entity-workflow.mdx +++ b/docs/design-patterns/entity-workflow.mdx @@ -3,6 +3,9 @@ id: entity-workflow title: "Entity Workflow Pattern" sidebar_label: "Entity Workflow" description: "A long-lived business entity — a user account, device, or order — gets one Workflow per instance, with Signals and Updates driving every state transition." +tags: + - Design Patterns + - Workflows --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/error-handling-patterns.mdx b/docs/design-patterns/error-handling-patterns.mdx index 54a454f9e8..f4ea46e3ab 100644 --- a/docs/design-patterns/error-handling-patterns.mdx +++ b/docs/design-patterns/error-handling-patterns.mdx @@ -3,6 +3,8 @@ id: error-handling-patterns title: "Error Handling & Retry Patterns" sidebar_label: "Error Handling & Retry Patterns" description: "Pattern selection guide and decision tree for choosing the right retry strategy based on your error type, cost constraints, and recovery requirements." +tags: + - Design Patterns --- import PatternCards from '@site/src/components/PatternCards'; diff --git a/docs/design-patterns/event-accumulator.mdx b/docs/design-patterns/event-accumulator.mdx index 716f51f9f1..73fcfcfd0a 100644 --- a/docs/design-patterns/event-accumulator.mdx +++ b/docs/design-patterns/event-accumulator.mdx @@ -3,6 +3,9 @@ id: event-accumulator title: "Event Accumulator Pattern" sidebar_label: "Event Accumulator" description: "Durably collect and deduplicate signals from multiple senders, then process the batch after a sliding inactivity timeout." +tags: + - Design Patterns + - Signals --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/external-interaction-patterns.mdx b/docs/design-patterns/external-interaction-patterns.mdx index 075c592d04..b961c0884b 100644 --- a/docs/design-patterns/external-interaction-patterns.mdx +++ b/docs/design-patterns/external-interaction-patterns.mdx @@ -3,6 +3,8 @@ id: external-interaction-patterns title: "External Interaction Patterns" sidebar_label: "External Interaction Patterns" description: "Compares five patterns for waiting on external systems and human decisions: polling, heartbeating Activities, delayed start, webhooks, and approval." +tags: + - Design Patterns --- import PatternCards from '@site/src/components/PatternCards'; diff --git a/docs/design-patterns/fairness.mdx b/docs/design-patterns/fairness.mdx index a416f1189f..395738febb 100644 --- a/docs/design-patterns/fairness.mdx +++ b/docs/design-patterns/fairness.mdx @@ -3,6 +3,9 @@ id: fairness title: "Fairness" sidebar_label: "Fairness" description: "Distributes Worker capacity evenly across tenants or users so that a burst from one caller does not starve the others." +tags: + - Design Patterns + - Task Queues --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/fanout-child-workflows.mdx b/docs/design-patterns/fanout-child-workflows.mdx index 9c0bbcc15a..57732881e9 100644 --- a/docs/design-patterns/fanout-child-workflows.mdx +++ b/docs/design-patterns/fanout-child-workflows.mdx @@ -3,6 +3,9 @@ id: fanout-child-workflows title: "Fan-Out with Child Workflows" sidebar_label: "Fan-Out with Child Workflows" description: "Distributes a large record set across parallel Child Workflows for concurrent processing with automatic scaling." +tags: + - Design Patterns + - Child Workflows --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/fast-slow-retries.mdx b/docs/design-patterns/fast-slow-retries.mdx index f021761cbd..4f8f3cd3b5 100644 --- a/docs/design-patterns/fast-slow-retries.mdx +++ b/docs/design-patterns/fast-slow-retries.mdx @@ -3,6 +3,9 @@ id: fast-slow-retries title: "Fast/Slow Retries" sidebar_label: "Fast/Slow Retries" description: "Retry fast with a short interval first, then shift to a slow, unlimited interval so the Workflow outlasts an extended downstream outage." +tags: + - Design Patterns + - Errors --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/fixed-count-retries.mdx b/docs/design-patterns/fixed-count-retries.mdx index ac89cab276..2fe30b8b28 100644 --- a/docs/design-patterns/fixed-count-retries.mdx +++ b/docs/design-patterns/fixed-count-retries.mdx @@ -3,6 +3,9 @@ id: fixed-count-retries title: "Fixed Count of Retries" sidebar_label: "Fixed Count of Retries" description: "Cap the number of Activity retry attempts to control cost when each attempt consumes a paid or limited resource." +tags: + - Design Patterns + - Errors --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/fixed-wall-time-retries.mdx b/docs/design-patterns/fixed-wall-time-retries.mdx index 5be09a272b..f52d51424c 100644 --- a/docs/design-patterns/fixed-wall-time-retries.mdx +++ b/docs/design-patterns/fixed-wall-time-retries.mdx @@ -3,6 +3,9 @@ id: fixed-wall-time-retries title: "Fixed Wall-Time Retries" sidebar_label: "Fixed Wall-Time Retries" description: "Bound the total elapsed time across all retry attempts to enforce a business SLA, regardless of how many individual attempts occur." +tags: + - Design Patterns + - Errors --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/index.mdx b/docs/design-patterns/index.mdx index 829d52446f..fc808d609e 100644 --- a/docs/design-patterns/index.mdx +++ b/docs/design-patterns/index.mdx @@ -3,6 +3,8 @@ id: index title: "Temporal Design Patterns" sidebar_label: "Overview" description: "A catalog of common, reusable, and proven design patterns for Temporal Workflows, organized by problem domain." +tags: + - Design Patterns slug: /design-patterns --- diff --git a/docs/design-patterns/local-activities.mdx b/docs/design-patterns/local-activities.mdx index 95962cef96..c94a3c3543 100644 --- a/docs/design-patterns/local-activities.mdx +++ b/docs/design-patterns/local-activities.mdx @@ -3,6 +3,9 @@ id: local-activities title: "Local Activities" sidebar_label: "Local Activities" description: "Local Activities run inside the Worker process, skipping server round-trips for short, idempotent Activities on a latency-sensitive path." +tags: + - Design Patterns + - Activities --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/long-running-activity.mdx b/docs/design-patterns/long-running-activity.mdx index c5b441049e..831e0e280e 100644 --- a/docs/design-patterns/long-running-activity.mdx +++ b/docs/design-patterns/long-running-activity.mdx @@ -3,6 +3,9 @@ id: long-running-activity title: "Long-Running Activity - Tracking Progress and Handling Cancellation with Heartbeats" sidebar_label: "Long Running Activity" description: "Long-running Activities report progress via heartbeats and enable resumption after failures with cancellation support." +tags: + - Design Patterns + - Activities --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/mapreduce-tree.mdx b/docs/design-patterns/mapreduce-tree.mdx index 43282d7001..a53711a183 100644 --- a/docs/design-patterns/mapreduce-tree.mdx +++ b/docs/design-patterns/mapreduce-tree.mdx @@ -3,6 +3,9 @@ id: mapreduce-tree title: "MapReduce Tree" sidebar_label: "MapReduce Tree" description: "Recursively splits a dataset into a binary tree of Child Workflows, processes leaves in parallel, then aggregates results back up the tree." +tags: + - Design Patterns + - Child Workflows --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/non-retryable-errors.mdx b/docs/design-patterns/non-retryable-errors.mdx index e3574500e7..d6dd9a7ff9 100644 --- a/docs/design-patterns/non-retryable-errors.mdx +++ b/docs/design-patterns/non-retryable-errors.mdx @@ -3,6 +3,9 @@ id: non-retryable-errors title: "Non-Retryable Errors" sidebar_label: "Non-Retryable Errors" description: "Mark error types that will never succeed — such as validation failures or missing records — so Temporal fails fast instead of retrying indefinitely." +tags: + - Design Patterns + - Errors --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/parallel-execution.mdx b/docs/design-patterns/parallel-execution.mdx index 4675e07ddc..b10bc7d70b 100644 --- a/docs/design-patterns/parallel-execution.mdx +++ b/docs/design-patterns/parallel-execution.mdx @@ -3,6 +3,9 @@ id: parallel-execution title: "Parallel Execution" sidebar_label: "Parallel Execution" description: "Executes multiple Activities concurrently for maximum throughput with error handling and controlled parallelism." +tags: + - Design Patterns + - Activities --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/performance-latency-patterns.mdx b/docs/design-patterns/performance-latency-patterns.mdx index a466c162eb..32913539e9 100644 --- a/docs/design-patterns/performance-latency-patterns.mdx +++ b/docs/design-patterns/performance-latency-patterns.mdx @@ -3,6 +3,8 @@ id: performance-latency-patterns title: "Performance & Latency Patterns" sidebar_label: "Performance & Latency Patterns" description: "Pattern selection guide for reducing Workflow latency, with a comparison of the round-trips each pattern removes and their combined effect." +tags: + - Design Patterns --- import PatternCards from '@site/src/components/PatternCards'; diff --git a/docs/design-patterns/pick-first.mdx b/docs/design-patterns/pick-first.mdx index 32aeb173e2..c8e8f40ed1 100644 --- a/docs/design-patterns/pick-first.mdx +++ b/docs/design-patterns/pick-first.mdx @@ -3,6 +3,9 @@ id: pick-first title: "Pick First Pattern" sidebar_label: "Pick First (Race)" description: "Races multiple Activities in parallel, returns the first completed result, and cancels the remaining Activities using SDK cancellation scopes." +tags: + - Design Patterns + - Activities --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/polling.mdx b/docs/design-patterns/polling.mdx index 293ccd7d1e..d3a06ad781 100644 --- a/docs/design-patterns/polling.mdx +++ b/docs/design-patterns/polling.mdx @@ -3,6 +3,9 @@ id: polling title: "Polling External Services" sidebar_label: "Polling External Services" description: "Strategies for polling external resources with varying frequencies: frequent, infrequent, and periodic patterns." +tags: + - Design Patterns + - Activities --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/priority-task-queues.mdx b/docs/design-patterns/priority-task-queues.mdx index ee3ce3c02e..92a76b7cc7 100644 --- a/docs/design-patterns/priority-task-queues.mdx +++ b/docs/design-patterns/priority-task-queues.mdx @@ -3,6 +3,9 @@ id: priority-task-queues title: "Priority Task Queues" sidebar_label: "Priority Task Queues" description: "Assigns a priority level to Workflows and Activities so that time-sensitive work executes ahead of lower-priority work within a single Task Queue." +tags: + - Design Patterns + - Task Queues --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/qos-throughput-patterns.mdx b/docs/design-patterns/qos-throughput-patterns.mdx index 321d68a3c7..b584df627d 100644 --- a/docs/design-patterns/qos-throughput-patterns.mdx +++ b/docs/design-patterns/qos-throughput-patterns.mdx @@ -3,6 +3,8 @@ id: qos-throughput-patterns title: "QoS & Throughput Patterns" sidebar_label: "QoS & Throughput Patterns" description: "Pattern selection guide for controlling execution rate, protecting downstream services from overload, and ensuring fair capacity distribution across tenants." +tags: + - Design Patterns --- import PatternCards from '@site/src/components/PatternCards'; diff --git a/docs/design-patterns/request-response-via-updates.mdx b/docs/design-patterns/request-response-via-updates.mdx index aee7d59c4e..c7b0b8c96b 100644 --- a/docs/design-patterns/request-response-via-updates.mdx +++ b/docs/design-patterns/request-response-via-updates.mdx @@ -3,6 +3,9 @@ id: request-response-via-updates title: "Request-Response via Updates" sidebar_label: "Request-Response via Updates" description: "Uses a Workflow Update to validate a request, modify Workflow state, and return a typed result to the caller synchronously, with strong consistency." +tags: + - Design Patterns + - Updates --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/resumable-activity.mdx b/docs/design-patterns/resumable-activity.mdx index b7fd512199..02b77b2c5a 100644 --- a/docs/design-patterns/resumable-activity.mdx +++ b/docs/design-patterns/resumable-activity.mdx @@ -3,6 +3,9 @@ id: resumable-activity title: "Resumable Activity (AKA Pause On Failure)" sidebar_label: "Resumable Activity" description: "Park the Workflow after retries are exhausted and wait for a human to signal a correction, then resume execution from where it left off." +tags: + - Design Patterns + - Failures --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/retry-metrics.mdx b/docs/design-patterns/retry-metrics.mdx index 824cd79475..6b2dc78d82 100644 --- a/docs/design-patterns/retry-metrics.mdx +++ b/docs/design-patterns/retry-metrics.mdx @@ -3,6 +3,9 @@ id: retry-metrics title: "Retry Alerting via Metrics" sidebar_label: "Retry Alerting via Metrics" description: "Emit a metric from the Activity when attempts cross a threshold, so on-call teams see persistent failures before an SLA breach." +tags: + - Design Patterns + - Metrics --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/saga-pattern.mdx b/docs/design-patterns/saga-pattern.mdx index 390f1daf18..625fe55c34 100644 --- a/docs/design-patterns/saga-pattern.mdx +++ b/docs/design-patterns/saga-pattern.mdx @@ -3,6 +3,9 @@ id: saga-pattern title: "Saga Pattern" sidebar_label: "Saga Pattern" description: "Manages distributed transactions with compensating actions. Each step has a compensation that undoes its effects if subsequent steps fail." +tags: + - Design Patterns + - Activities --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/signal-with-start.mdx b/docs/design-patterns/signal-with-start.mdx index 79659187c1..f00de183f2 100644 --- a/docs/design-patterns/signal-with-start.mdx +++ b/docs/design-patterns/signal-with-start.mdx @@ -3,6 +3,9 @@ id: signal-with-start title: "Signal with Start Pattern" sidebar_label: "Signal with Start" description: "Starts a Workflow when Signaling it if it does not already exist. If already running, it receives the Signal directly." +tags: + - Design Patterns + - Signals --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/sliding-window.mdx b/docs/design-patterns/sliding-window.mdx index 6e283924ff..f56ac28787 100644 --- a/docs/design-patterns/sliding-window.mdx +++ b/docs/design-patterns/sliding-window.mdx @@ -3,6 +3,9 @@ id: sliding-window title: "Sliding Window" sidebar_label: "Sliding Window" description: "Maintains a fixed number of concurrently active Child Workflows, starting a new one each time an existing one completes." +tags: + - Design Patterns + - Child Workflows --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/task-orchestration-patterns.mdx b/docs/design-patterns/task-orchestration-patterns.mdx index 9adffe2eba..58bc5a265b 100644 --- a/docs/design-patterns/task-orchestration-patterns.mdx +++ b/docs/design-patterns/task-orchestration-patterns.mdx @@ -3,6 +3,8 @@ id: task-orchestration-patterns title: "Task Orchestration Patterns" sidebar_label: "Task Orchestration Patterns" description: "Compares Child Workflows, Parallel Execution, and Pick First (Race) for decomposing, running concurrently, and racing work inside a single Workflow." +tags: + - Design Patterns --- import PatternCards from '@site/src/components/PatternCards'; diff --git a/docs/design-patterns/updatable-timer.mdx b/docs/design-patterns/updatable-timer.mdx index 44a61ec27a..ea29ef6e1c 100644 --- a/docs/design-patterns/updatable-timer.mdx +++ b/docs/design-patterns/updatable-timer.mdx @@ -3,6 +3,9 @@ id: updatable-timer title: "Updatable / Debounced Timer Pattern" sidebar_label: "Updatable Timer" description: "Dynamically adjustable timers that respond to Signals or Updates. Extend, shorten, or cancel timers based on external events." +tags: + - Design Patterns + - Timers --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/worker-configuration-patterns.mdx b/docs/design-patterns/worker-configuration-patterns.mdx index 95e430366e..2f85be9740 100644 --- a/docs/design-patterns/worker-configuration-patterns.mdx +++ b/docs/design-patterns/worker-configuration-patterns.mdx @@ -3,6 +3,8 @@ id: worker-configuration-patterns title: "Worker Configuration Patterns" sidebar_label: "Worker Configuration Patterns" description: "Pattern selection guide for configuring how Workers are set up, how work is routed, and how Activities access external dependencies." +tags: + - Design Patterns --- import PatternCards from '@site/src/components/PatternCards'; diff --git a/docs/design-patterns/worker-specific-taskqueue.mdx b/docs/design-patterns/worker-specific-taskqueue.mdx index f23bd7f3e9..c74cf05548 100644 --- a/docs/design-patterns/worker-specific-taskqueue.mdx +++ b/docs/design-patterns/worker-specific-taskqueue.mdx @@ -3,6 +3,9 @@ id: worker-specific-taskqueue title: "Worker-Specific Task Queues Pattern" sidebar_label: "Worker-Specific Task Queues" description: "Routes Activities to specific Workers using unique Task Queues for Worker affinity and host-specific processing." +tags: + - Design Patterns + - Task Queues --- import Tabs from '@theme/Tabs'; diff --git a/docs/design-patterns/workflow-messaging-patterns.mdx b/docs/design-patterns/workflow-messaging-patterns.mdx index 1ce2144204..64f285a165 100644 --- a/docs/design-patterns/workflow-messaging-patterns.mdx +++ b/docs/design-patterns/workflow-messaging-patterns.mdx @@ -3,6 +3,8 @@ id: workflow-messaging-patterns title: "Workflow Messaging Patterns" sidebar_label: "Workflow Messaging Patterns" description: "Pattern selection guide for sending data into running Workflows and receiving responses or triggering behavior changes." +tags: + - Design Patterns --- import PatternCards from '@site/src/components/PatternCards'; diff --git a/docs/evaluate/use-cases-design-patterns.mdx b/docs/evaluate/use-cases-design-patterns.mdx index c4e016d168..e3e819935a 100644 --- a/docs/evaluate/use-cases-design-patterns.mdx +++ b/docs/evaluate/use-cases-design-patterns.mdx @@ -8,7 +8,7 @@ tags: - design-patterns --- -This page provides an overview of how leading organizations leverage Temporal to solve real-world problems, general use cases, and architectural design patterns. +This page provides an overview of how leading organizations leverage Temporal to solve real-world problems, general use cases, and architectural design patterns. For a full catalog of reusable, code-level Workflow and Activity patterns, see [Temporal Design Patterns](/design-patterns). ## Use Cases of Temporal in Production @@ -74,6 +74,8 @@ They can use schedules and timers to prompt for user input. **Code Sample**: [Candidate acceptance example prompting for a response](https://learn.temporal.io/examples/go/background-checks/candidate-acceptance) +For a reusable implementation of this pattern, see [Approval](/design-patterns/approval). + ### Polyglot Systems Modern development teams often work with different programming languages based on their expertise and project requirements. Temporal supports this through built-in multi-language capabilities, allowing teams to continue using their preferred languages while working together. @@ -89,7 +91,7 @@ It processes one message at a time, ensuring each message is processed only once This approach addresses issues that can arise with long message processing times, which in other systems might cause consumer failover (typically with a default 5-minute message poll timeout) and potentially result in duplicate message processing by multiple consumers. Temporal's ability to handle extended task durations makes it well-suited for such scenarios. -The [heartbeat](/encyclopedia/detecting-activity-failures#activity-heartbeat) feature allows you to know that an activity is still working, providing insight into the progress of long-running processes. +The [heartbeat](/encyclopedia/detecting-activity-failures#activity-heartbeat) feature allows you to know that an activity is still working, providing insight into the progress of long-running processes. See the [Long-Running Activity](/design-patterns/long-running-activity) pattern for a reusable implementation with progress reporting and cancellation. **Example**: [eCommerce example](https://learn.temporal.io/tutorials/go/build-an-ecommerce-app/). @@ -103,6 +105,8 @@ The Saga pattern is a design pattern used to manage and handle failures in compl If a step in the Workflow fails, the Saga pattern compensates for this failure by executing specific actions to undo the previous steps. This ensures that even in the event of a failure, the system can revert to a consistent state. +For an implementation walkthrough with compensation ordering and code samples, see the [Saga Pattern](/design-patterns/saga-pattern) reference. + **Examples:** - [Build a trip booking application in Python](https://learn.temporal.io/tutorials/python/trip-booking-app/). diff --git a/readme/INFORMATION-ARCHITECTURE.md b/readme/INFORMATION-ARCHITECTURE.md index 02af70c320..bf3ffa68b2 100644 --- a/readme/INFORMATION-ARCHITECTURE.md +++ b/readme/INFORMATION-ARCHITECTURE.md @@ -52,6 +52,28 @@ This document describes the purpose, audience, and content type for each top-lev - **Content type:** Mostly reference, and a few how-tos - **Description:** Covers installation, configuration, use with Temporal Cloud and the full command tree. Each command page lists subcommands, flags, and representative examples. +## Design patterns + +- **Audience:** Developers who already know what they're building and need a proven, reusable technique for a specific + mechanical problem — fan out work across Child Workflows, rate-limit a downstream call, pick the right retry shape + for an error type. +- **Content type:** Reference-style pattern catalog. Each leaf page follows a fixed template: Overview, Problem, + Solution, Implementation (with runnable code across multiple SDKs), When to use, Benefits and trade-offs, Comparison + with alternatives, Best practices, Common pitfalls, Related. Each sub-category has its own short "pattern selection + guide" index page. +- **Description:** A catalog of Temporal-specific design patterns (`/design-patterns`), grouped by problem domain: task + orchestration, workflow messaging, entity lifecycle, external interaction, distributed transactions, error handling + and retry, batch processing, QoS and throughput, performance and latency, and worker configuration. + + Distinct from Guides: a Guide is a full, use-case-specific implementation walkthrough; a Design Pattern is a + shorter, reusable building block that a Guide (or a reader's own application) builds on top of. Guides link out to + the relevant pattern page rather than re-explaining the underlying mechanism; pattern pages should link back to any + Guide that showcases a deeper, real-world implementation. + + Distinct from Best Practices: Best Practices are operational and organizational recommendations (namespace + management, cost governance, worker tuning) for running Temporal at scale. Design Patterns are code-level + implementation techniques for a Workflow or Activity. + ## References - **Audience:** Developers, operators, and architects looking up specific technical details. From 87c7e82b6add842f20751358775b0955145e5742 Mon Sep 17 00:00:00 2001 From: Duncan Mackenzie Date: Fri, 4 Sep 2026 09:38:26 -0700 Subject: [PATCH 3/4] Fix technical inaccuracies found in the design-patterns catalog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All claims below verified against the temporalio/temporal, sdk-go, sdk-python, and sdk-typescript source on GitHub, not just against other doc pages. - eager-workflow-start.mdx claimed TypeScript doesn't support Eager Workflow Start. It does (WorkflowOptions.requestEagerStart, wired through NativeConnection and the gRPC start request in sdk-typescript) — added a TypeScript tab and corrected every claim that excluded it. Also added the missing .NET SDK (RequestEagerStart), and fixed the self-hosted guidance: system.enableEagerWorkflowStart defaults to true (confirmed in temporal's dynamicconfig/constants.go) rather than needing to be turned on, so the pitfall is an operator having disabled it, not one having forgotten to enable it. Cross-linked to the canonical /develop/worker-performance#eager-workflow-start page. - local-activities.mdx's timeout pitfall omitted Workflow Task heartbeating, the SDK's actual mitigation (sdk-go's ratioToForceCompleteWorkflowTaskComplete = 0.8, i.e. the ~80% figure the encyclopedia page already cites). Added it, plus the missing cross-link to /local-activity. - non-retryable-errors.mdx didn't mention that wrapping a non-retryable ApplicationFailure in a plain language error loses the flag. Confirmed in sdk-go: ErrorToFailure does a concrete type switch on the outermost error only, so a fmt.Errorf-wrapped ApplicationError falls through to a default retryable failure. - downstream-rate-limiting.mdx didn't mention that Eager Activity execution can bypass the rate-limited Task Queue. Added it, and confirmed the exact per-SDK difference: sdk-python requires disable_eager_activity_execution=True explicitly, while sdk-go's worker.go auto-disables eager activities whenever TaskQueueActivitiesPerSecond is set. - delayed-retry.mdx was missing the Python and Go tabs every sibling page has. Added them using the real ApplicationError/next_retry_delay (Python) and NewApplicationErrorWithOptions/NextRetryDelay (Go) APIs, matching this repo's own SDK reference pages. - docs/develop/worker-tuning-reference.mdx used MaxConcurrentActivityTaskExecutionSize / MaxConcurrentLocalActivityTaskExecutionSize. Neither field has "Task" in it in sdk-go, sdk-java, or sdk-typescript — fixed to MaxConcurrentActivityExecutionSize / MaxConcurrentLocalActivityExecutionSize. (The design-patterns page using these names was already correct.) - Reconciled the Child Workflow fan-out guidance: the encyclopedia's recommended cap of 1,000 Child Workflow Executions per parent wasn't surfaced in child-workflows.mdx, fanout-child-workflows.mdx, sliding-window.mdx, or mapreduce-tree.mdx, and batch-processing-patterns.mdx's "~4M records" Fan-Out capacity figure didn't account for it (it multiplied the hard 2,000-child limit by 2,000 activities/child instead). Added the 1,000 figure to all four pitfalls sections and revised the capacity estimate to ~500K, consistent with this page's own "aim for 500 Activities per child" guidance. Also fixed a "50,000 event history limit" mention in fanout-child-workflows.mdx to the documented 51,200. --- .../batch-processing-patterns.mdx | 4 +- docs/design-patterns/child-workflows.mdx | 2 +- docs/design-patterns/delayed-retry.mdx | 153 ++++++++++++++++++ .../downstream-rate-limiting.mdx | 1 + docs/design-patterns/eager-workflow-start.mdx | 79 ++++++--- .../fanout-child-workflows.mdx | 4 +- docs/design-patterns/local-activities.mdx | 6 +- docs/design-patterns/mapreduce-tree.mdx | 1 + docs/design-patterns/non-retryable-errors.mdx | 1 + docs/design-patterns/sliding-window.mdx | 2 +- docs/develop/worker-tuning-reference.mdx | 8 +- 11 files changed, 229 insertions(+), 32 deletions(-) diff --git a/docs/design-patterns/batch-processing-patterns.mdx b/docs/design-patterns/batch-processing-patterns.mdx index cd6ef1dbc0..eff83c256f 100644 --- a/docs/design-patterns/batch-processing-patterns.mdx +++ b/docs/design-patterns/batch-processing-patterns.mdx @@ -16,7 +16,7 @@ These patterns process large volumes of records reliably, at scale, and without | Pattern | Record set size | Parallelism model | Workflow-based rate control | |---|---|---|---| | [Basic Workflow](#basic-workflow-single-tier-fan-out) | Small (up to a few hundred records) | Sequential or parallel activities in one Workflow | No | -| [Fan-Out with Child Workflows](/design-patterns/fanout-child-workflows) | Up to ~4M records | Fixed concurrency (one child per chunk) | No | +| [Fan-Out with Child Workflows](/design-patterns/fanout-child-workflows) | Up to ~500K records | Fixed concurrency (one child per chunk) | No | | [Batch Iterator](/design-patterns/batch-iterator) | Unlimited | Limited (activities per page) | Yes — fixed page rate | | [Sliding Window](/design-patterns/sliding-window) | Unlimited | Bounded window of concurrent children | Yes — configurable window | | [MapReduce Tree](/design-patterns/mapreduce-tree) | Unlimited | Fully parallel recursive tree | No — maximum speed | @@ -28,7 +28,7 @@ These patterns process large volumes of records reliably, at scale, and without href: "/design-patterns/fanout-child-workflows", icon: "fanout-child-workflows-icon.svg", title: "Fan-Out with Child Workflows", - description: "Splits a record set into fixed-size chunks and assigns each to an independent child Workflow. Direct to reason about; best for record sets up to ~4M items.", + description: "Splits a record set into fixed-size chunks and assigns each to an independent child Workflow. Direct to reason about; best for record sets up to ~500K items.", }, { href: "/design-patterns/batch-iterator", diff --git a/docs/design-patterns/child-workflows.mdx b/docs/design-patterns/child-workflows.mdx index 4e898b182e..af096e6eef 100644 --- a/docs/design-patterns/child-workflows.mdx +++ b/docs/design-patterns/child-workflows.mdx @@ -767,7 +767,7 @@ Starting a Child Workflow has more overhead than starting an Activity. ## Common pitfalls - **Treating Child Workflows like Activities.** Child Workflows are for orchestration, not for executing external code. If you only need to call an API or run a function, use an Activity instead. -- **Spawning unbounded children in a loop.** Starting thousands of Child Workflows without batching can overwhelm the Temporal Service and bloat the parent's event history. Use fixed-size batches or a sliding window. +- **Spawning unbounded children in a loop.** Starting thousands of Child Workflows without batching can overwhelm the Temporal Service and bloat the parent's event history. Temporal enforces a hard limit of 2,000 pending (in-flight) children per parent, but the [recommended cap](/child-workflows#when-to-use-child-workflows) is lower: a single parent should not spawn more than 1,000 Child Workflow Executions in total, since each one adds more history to the parent than an Activity would. Use fixed-size batches or a sliding window. - **Ignoring the Parent Close Policy.** The default policy is TERMINATE, which kills children when the parent closes. If children must outlive the parent, set the policy to ABANDON explicitly. - **Using synchronous calls when async is needed.** Calling a Child Workflow synchronously blocks the parent until the child completes. For long-running children, use the async API (`Async.function()` in Java, `startChild()` in TypeScript, `start_child_workflow()` in Python, or collect Futures without calling `.Get()` in Go) to avoid stalling the parent. - **Omitting Workflow IDs.** Without explicit Workflow IDs, you lose the ability to deduplicate or look up Child Workflows by a meaningful identifier. Generate deterministic IDs based on business keys. diff --git a/docs/design-patterns/delayed-retry.mdx b/docs/design-patterns/delayed-retry.mdx index 82554dad7b..9f6d2126f4 100644 --- a/docs/design-patterns/delayed-retry.mdx +++ b/docs/design-patterns/delayed-retry.mdx @@ -69,6 +69,61 @@ Extract the wait duration from the error or response and pass it to `Application The RetryPolicy's `MaximumAttempts` and `ScheduleToCloseTimeout` still apply — only the interval for the next retry is overridden. + + +```python +# activities.py +from datetime import timedelta +from temporalio import activity +from temporalio.exceptions import ApplicationError + +@activity.defn +async def call_api(endpoint: str) -> str: + response = await http_client.get(endpoint) + + if response.status_code == 429: + retry_after = response.headers.get("Retry-After") + if retry_after is not None: + raise ApplicationError( + f"Rate limited — retrying after {retry_after}s", + type="RateLimitError", + next_retry_delay=timedelta(seconds=int(retry_after)), + ) + raise ApplicationError( + "Rate limited — retrying per RetryPolicy", type="RateLimitError" + ) + + return response.text +``` + + + + +```go +// rate_limited_activity.go +func CallApi(ctx context.Context, endpoint string) (string, error) { + response, err := httpClient.Get(endpoint) + if err != nil { + return "", err + } + + if response.StatusCode == 429 { + if retryAfter := response.Header.Get("Retry-After"); retryAfter != "" { + seconds, _ := strconv.Atoi(retryAfter) + return "", temporal.NewApplicationErrorWithOptions( + fmt.Sprintf("Rate limited — retrying after %ds", seconds), + "RateLimitError", + temporal.ApplicationErrorOptions{NextRetryDelay: time.Duration(seconds) * time.Second}, + ) + } + return "", temporal.NewApplicationError("Rate limited — retrying per RetryPolicy", "RateLimitError") + } + + return response.Body, nil +} +``` + + ```java @@ -135,6 +190,54 @@ export async function callApi(endpoint: string): Promise { You can also set the delay dynamically based on the attempt number — for example, to implement a custom backoff that differs from exponential, or to add a known base delay on top of the standard backoff. + + +```python +# activities.py +from datetime import timedelta +from temporalio import activity +from temporalio.exceptions import ApplicationError + +@activity.defn +async def process(input: str) -> str: + attempt = activity.info().attempt + + try: + return await downstream_service.call(input) + except ServiceUnavailableError as e: + # Custom delay: 3 seconds × attempt number (3s, 6s, 9s, …) + raise ApplicationError( + f"Service unavailable on attempt {attempt}", + type="ServiceUnavailable", + next_retry_delay=timedelta(seconds=3 * attempt), + ) from e +``` + + + + +```go +// backoff_activity.go +func Process(ctx context.Context, input string) (string, error) { + attempt := activity.GetInfo(ctx).Attempt + + result, err := downstreamService.Call(input) + if err != nil { + // Custom delay: 3 seconds × attempt number (3s, 6s, 9s, …) + return "", temporal.NewApplicationErrorWithOptions( + fmt.Sprintf("Service unavailable on attempt %d", attempt), + "ServiceUnavailable", + temporal.ApplicationErrorOptions{ + Cause: err, + NextRetryDelay: 3 * time.Second * time.Duration(attempt), + }, + ) + } + return result, nil +} +``` + + ```java @@ -196,6 +299,56 @@ The Workflow sets a normal `RetryPolicy`. The `nextRetryDelay` set in the Activity overrides the interval only for the retry following that specific failure — subsequent attempts fall back to the RetryPolicy schedule if `nextRetryDelay` is not set again. + + +```python +# workflows.py +from datetime import timedelta +from temporalio import workflow +from temporalio.common import RetryPolicy + +with workflow.unsafe.imports_passed_through(): + from activities import call_api + +@workflow.defn +class ApiWorkflow: + @workflow.run + async def run(self, endpoint: str) -> str: + return await workflow.execute_activity( + call_api, + endpoint, + start_to_close_timeout=timedelta(seconds=10), + retry_policy=RetryPolicy( + initial_interval=timedelta(seconds=1), + backoff_coefficient=2.0, + maximum_attempts=10, + ), + ) +``` + + + + +```go +// api_workflow.go +func ApiWorkflow(ctx workflow.Context, endpoint string) (string, error) { + ao := workflow.ActivityOptions{ + StartToCloseTimeout: 10 * time.Second, + RetryPolicy: &temporal.RetryPolicy{ + InitialInterval: time.Second, + BackoffCoefficient: 2.0, + MaximumAttempts: 10, + }, + } + ctx = workflow.WithActivityOptions(ctx, ao) + + var result string + err := workflow.ExecuteActivity(ctx, CallApi, endpoint).Get(ctx, &result) + return result, err +} +``` + + ```java diff --git a/docs/design-patterns/downstream-rate-limiting.mdx b/docs/design-patterns/downstream-rate-limiting.mdx index 128e1ad154..13a4ff1efe 100644 --- a/docs/design-patterns/downstream-rate-limiting.mdx +++ b/docs/design-patterns/downstream-rate-limiting.mdx @@ -283,6 +283,7 @@ The concurrency slots (`MaxConcurrentActivityExecutionSize`, `MaxConcurrentWorkf - **Confusing throughput limits with concurrency limits.** `MaxTaskQueueActivitiesPerSecond` controls starts per second; `MaxConcurrentActivityExecutionSize` controls simultaneous executions. Long-running Activities that hold slots for minutes may exhaust concurrency before the RPS cap applies. - **Setting the cap far below actual demand.** A cap much lower than actual submission rate causes the queue to grow unboundedly. Monitor queue depth and raise the cap or add more Workers when throughput requirements grow. - **Expecting a perfectly even per-second rate.** The limit is enforced across the queue's partitions, default four. The server maintains the configured rate as an average over time but can dispatch a short burst above it, up to roughly the rate divided across partitions. If the downstream service rejects any momentary overshoot, set the cap below the hard limit to leave headroom, or reduce the partition count for the queue. +- **Eager Activity execution bypassing the rate-limited queue.** [Eager Activity Start](/develop/worker-performance#eager-activity-start) lets the server hand an Activity straight back to the Worker that just completed the scheduling Workflow Task, skipping the Task Queue and its rate limit entirely. In Python, disable it explicitly with `disable_eager_activity_execution=True` on the `Worker`. The Go SDK disables it automatically whenever `TaskQueueActivitiesPerSecond` is set, so no separate flag is needed there — but confirm the equivalent for your SDK before relying on the queue-level cap alone. ## Related diff --git a/docs/design-patterns/eager-workflow-start.mdx b/docs/design-patterns/eager-workflow-start.mdx index fdcd216f4c..773047cde4 100644 --- a/docs/design-patterns/eager-workflow-start.mdx +++ b/docs/design-patterns/eager-workflow-start.mdx @@ -12,7 +12,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; :::info[TLDR] -**Bypass the Temporal Matching Service by dispatching the first Workflow Task directly to a co-located Worker.** The Worker and the client that starts the Workflow must share the same process and server connection. Eager Workflow Start eliminates the Matching Service round-trip, saving approximately 30–50 ms per Workflow start. When combined with Local Activities, this pattern achieves ~265 ms total-workflow latency (vs ~850 ms baseline). The TypeScript SDK does not support Eager Workflow Start. +**Bypass the Temporal Matching Service by dispatching the first Workflow Task directly to a co-located Worker.** The Worker and the client that starts the Workflow must share the same process and server connection. Eager Workflow Start eliminates the Matching Service round-trip, saving approximately 30–50 ms per Workflow start. When combined with Local Activities, this pattern achieves ~265 ms total-workflow latency (vs ~850 ms baseline). Supported by the Go, Java, Python, TypeScript, and .NET SDKs. ::: ## Overview @@ -57,17 +57,9 @@ For applications where the starter and Worker share the same deployment unit—s ## Solution -Start a Worker in the same process as the workflow starter, using the same client connection. Set `EnableEagerStart: true` (Go), `setDisableEagerExecution(false)` (Java), or `request_eager_start=True` (Python) on the `StartWorkflowOptions`. The SDK signals to the server that a local Worker is available, and the server returns the first Workflow Task inline. +Start a Worker in the same process as the workflow starter, using the same client connection. Set `EnableEagerStart: true` (Go), `setDisableEagerExecution(false)` (Java), `request_eager_start=True` (Python), `requestEagerStart: true` (TypeScript, on a `NativeConnection` shared between Worker and Client), or `RequestEagerStart` (.NET) on the workflow start options. The SDK signals to the server that a local Worker is available, and the server returns the first Workflow Task inline. -:::warning[Feature flag for self-hosted Temporal] -On self-hosted Temporal Server, Eager Workflow Start may require enabling a dynamic config flag: - -``` ---dynamic-config-value system.enableEagerWorkflowStart=true -``` - -Temporal Cloud and recent versions of the open-source server may enable this by default. Check your server's release notes or documentation to confirm. -::: +Eager Workflow Start is enabled by default in Temporal Cloud and in self-hosted Temporal Server 1.29.0 and later — no additional server configuration or access request is needed. On self-hosted Temporal Server, an operator can disable it with the dynamic config flag `system.enableEagerWorkflowStart` set to `false`; if you don't observe the expected latency improvement, confirm that flag hasn't been turned off. See [Eager Workflow Start](/develop/worker-performance#eager-workflow-start) for the canonical server-side reference. @@ -179,11 +171,53 @@ public class Starter { } ``` + + + +```typescript +// starter.ts — starts the Worker in the same process, then executes the Workflow eagerly +import { NativeConnection, Worker } from '@temporalio/worker'; +import { Client } from '@temporalio/client'; +import { transactionWorkflow } from './workflows'; +import { TASK_QUEUE, TransactionRequest } from './shared'; + +async function run() { + // The Client and the Worker must share this NativeConnection for eager dispatch to work. + const connection = await NativeConnection.connect({ address: 'localhost:7233' }); + + const worker = await Worker.create({ + connection, + taskQueue: TASK_QUEUE, + workflowsPath: require.resolve('./workflows'), + activities: { validateTransaction, settleTransaction }, + }); + + const client = new Client({ connection }); + + await worker.runUntil(async () => { + const handle = await client.workflow.start(transactionWorkflow, { + args: [{ amount: 100.0, currency: 'USD' } satisfies TransactionRequest], + workflowId: 'eager-workflow-start-demo', + taskQueue: TASK_QUEUE, + requestEagerStart: true, // Dispatch first WorkflowTask inline + }); + + const result = await handle.result(); + console.log(`Transaction complete: ID=${result.id} Status=${result.status}`); + }); +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); +``` + -:::info[TypeScript SDK] -The TypeScript SDK does not currently support Eager Workflow Start. Use [Local Activities](/design-patterns/local-activities) or [Early Return + Local Activities](/design-patterns/early-return-local-activities) for latency-sensitive TypeScript workflows. +:::info[.NET SDK] +The .NET SDK also supports Eager Workflow Start: set `RequestEagerStart = true` on `WorkflowOptions` when starting the Workflow, with the Worker and Client sharing the same connection. ::: ## When to use @@ -192,12 +226,11 @@ The TypeScript SDK does not currently support Eager Workflow Start. Use [Local A - The workflow starter and Worker run in the same deployment unit (for example, a single service that both handles API requests and runs Workers) - You need the absolute minimum total-workflow latency and are already using Local Activities -- The language is Go, Java, or Python +- Any of the Go, Java, Python, TypeScript, or .NET SDKs **Poor fit:** - Workers are deployed independently from starters (the eager request falls back to normal dispatch, which is harmless but provides no benefit) -- You are using the TypeScript SDK - First-response latency matters more than total latency—combine with [Early Return](/design-patterns/early-return) or [Early Return + Local Activities](/design-patterns/early-return-local-activities) for that use case ## Benefits and trade-offs @@ -207,24 +240,24 @@ The TypeScript SDK does not currently support Eager Workflow Start. Use [Local A | Matching Service round-trip | Yes (~30–50 ms) | No (eliminated) | | Worker co-location required | No | Yes (same process + client) | | Fallback behavior | N/A | Graceful fallback to normal dispatch | -| TypeScript SDK support | Yes | No | -| Configuration required | None | `EnableEagerStart`/`request_eager_start`/`setDisableEagerExecution(false)` | -| Self-hosted server flag | N/A | May need `system.enableEagerWorkflowStart=true` | +| SDK support | All | Go, Java, Python, TypeScript, .NET | +| Configuration required | None | `EnableEagerStart`/`request_eager_start`/`setDisableEagerExecution(false)`/`requestEagerStart`/`RequestEagerStart` | +| Self-hosted server flag | N/A | On by default (Server 1.29.0+); disable via `system.enableEagerWorkflowStart=false` | ## Best practices - **Combine with Local Activities.** Eager Workflow Start eliminates the Matching overhead on the first Workflow Task; Local Activities eliminate server round-trips within each Workflow Task. Together they provide the greatest total latency reduction. - **Use a non-blocking Worker start.** Start the Worker before executing the Workflow so it has an available slot. In Go, use `w.Start()` and defer `w.Stop()`. In Python, use `async with Worker(...)`. In Java, call `factory.start()` before creating the workflow stub. - **Do not rely on eager dispatch always firing.** The server falls back to normal dispatch if no local slot is available (for example, the Worker is at capacity). Design the Workflow to work correctly in both cases. -- **Share the same client and connection.** The Worker and the workflow starter must use the same `WorkflowClient` instance (Java), `client.Client` (Go), or `Client` (Python). A Worker using a different connection cannot receive eager tasks from another client. +- **Share the same client and connection.** The Worker and the workflow starter must use the same `WorkflowClient` instance (Java), `client.Client` (Go), `Client` (Python), or `NativeConnection` (TypeScript). A Worker using a different connection cannot receive eager tasks from another client. - **Be mindful of resource sharing in co-located deployments.** When a Worker runs in the same process as a request handler, they share CPU, memory, and failure domains. A spike in activity execution can slow request handling, and vice versa. Monitor Worker CPU, Workflow Task execution latency, and task queue depth to ensure Worker load does not affect client-facing latency. ## Common pitfalls - **Starting the Worker after `ExecuteWorkflow`.** If the Worker is not registered and running before the eager start call, no local slot exists and the request falls back to normal dispatch. - **Expecting eager dispatch in distributed deployments.** If the process that calls `ExecuteWorkflow` is not the same process running the Worker, eager dispatch will never succeed. The call still works, but it provides no latency benefit. -- **Missing the feature flag on self-hosted servers.** If the server dynamic config flag is not set, eager dispatch requests are silently ignored and the execution falls back to normal dispatch. Verify the flag is set if you do not observe the expected latency improvement. -- **Using TypeScript.** The TypeScript SDK does not support Eager Workflow Start. Switch to Python, Go, or Java for this optimization. +- **Assuming the self-hosted server flag is off.** Eager Workflow Start ships enabled by default (Server 1.29.0+). If you don't observe the expected latency improvement, check whether an operator explicitly disabled `system.enableEagerWorkflowStart`, rather than assuming it needs to be turned on. +- **Using a Connection instead of a NativeConnection in TypeScript.** The high-level `Client` and the `Worker` must share a `NativeConnection` object, not just the same server address. A `Worker` created from a separate connection cannot receive eager tasks. ## Related @@ -233,3 +266,7 @@ The TypeScript SDK does not currently support Eager Workflow Start. Use [Local A - [Local Activities](/design-patterns/local-activities) — eliminates per-Activity server round-trips; pairs naturally with Eager Workflow Start - [Early Return + Local Activities](/design-patterns/early-return-local-activities) — minimum first-response latency via Update-with-Start plus Local Activities - [Early Return](/design-patterns/early-return) — returns early to the client via Update-with-Start + +### References + +- [Eager Workflow Start](/develop/worker-performance#eager-workflow-start) — canonical server-side reference, including Eager Activity Start diff --git a/docs/design-patterns/fanout-child-workflows.mdx b/docs/design-patterns/fanout-child-workflows.mdx index 57732881e9..6a940e066f 100644 --- a/docs/design-patterns/fanout-child-workflows.mdx +++ b/docs/design-patterns/fanout-child-workflows.mdx @@ -21,7 +21,7 @@ The Fan-Out pattern distributes a large record set across multiple independent c ## Problem -A single Workflow run can have at most 2,000 in-flight Activities (aim for 500) and at most 50,000 history events. Processing millions of records in a single Workflow run is therefore not possible. +A single Workflow run can have at most 2,000 in-flight Activities (aim for 500) and at most 51,200 history events. Processing millions of records in a single Workflow run is therefore not possible. You need a way to partition a large record set, process each partition independently, and coordinate the overall job while keeping each Workflow's history within safe bounds. @@ -316,7 +316,7 @@ public class RecordBatchWorkflowImpl implements RecordBatchWorkflow { ## Common pitfalls -- **Starting too many children at once.** Each child start adds to the parent's history. Temporal enforces a default limit of 2,000 pending (in-flight) child Workflows per parent; keep well under it. See [Temporal guidance](/child-workflows#when-to-use-child-workflows). If you need more children, switch to [MapReduce Tree](/design-patterns/mapreduce-tree) or [Sliding Window](/design-patterns/sliding-window). +- **Starting too many children at once.** Each child start adds to the parent's history. Temporal enforces a default limit of 2,000 pending (in-flight) child Workflows per parent, but the recommended cap is lower still: a single parent should not spawn more than 1,000 Child Workflow Executions, since each one adds more history to the parent than an Activity would. See [Temporal guidance](/child-workflows#when-to-use-child-workflows). If you need more children, switch to [MapReduce Tree](/design-patterns/mapreduce-tree) or [Sliding Window](/design-patterns/sliding-window). - **Passing large lists of IDs.** Workflow inputs are stored in event history. Passing millions of record IDs as a list will blow the history size limit. Use offset + length instead. - **Ignoring child failures.** A failed child does not automatically fail the parent unless you await all results. Always await child handles and handle errors explicitly. diff --git a/docs/design-patterns/local-activities.mdx b/docs/design-patterns/local-activities.mdx index c94a3c3543..bd60a4765a 100644 --- a/docs/design-patterns/local-activities.mdx +++ b/docs/design-patterns/local-activities.mdx @@ -197,7 +197,7 @@ public class Impl implements TransactionWorkflow { ## Common pitfalls -- **Exceeding the Workflow Task timeout.** If a Local Activity takes longer than the Workflow Task timeout (default 10 seconds), the entire task times out and retries—including any Local Activities that already completed in memory during that task. +- **Exceeding the Workflow Task timeout.** If a Local Activity takes longer than the Workflow Task timeout (default 10 seconds) and Workflow Task heartbeating doesn't cover it, the entire task times out and retries—including any Local Activities that already completed in memory during that task. The SDK mitigates this automatically: once a running Local Activity passes about 80% of the Workflow Task timeout, the Worker heartbeats by completing the current Workflow Task and requesting a new one, so a single long Local Activity (or a chain of them) can run past one Workflow Task's timeout without the whole task failing. Heartbeating adds Events to history and delays processing of incoming Signals until the Local Activity finishes — see [Local Activity](/local-activity#workflow-task-heartbeating) for the full mechanism. - **Assuming exactly-once semantics.** Unlike regular Activities, a Local Activity does not get its own persisted history event until the Workflow Task completes. A crashed Worker causes the whole task to re-run. This compounds when Local Activities are chained: if a Worker crashes after the third of five sequential Local Activities, all five re-execute on the next attempt. If you need a durable checkpoint between each step, use regular Activities instead. - **Long retry intervals.** Each retry attempt with back-off creates a server-side timer event. For truly short Activities, use a tight `scheduleToCloseTimeout` and allow immediate retries rather than spaced-out back-off. @@ -209,3 +209,7 @@ public class Impl implements TransactionWorkflow { - [Early Return](/design-patterns/early-return) — returns a response to the caller before the Workflow finishes, independent of Local Activities - [Eager Workflow Start](/design-patterns/eager-workflow-start) — eliminates the server Matching step when starting a Workflow for additional latency reduction - [Long Running Activity](/design-patterns/long-running-activity) — the right choice when Activities need heartbeating and long execution windows + +### References + +- [Local Activity](/local-activity) — canonical concept reference, including durability guarantees and Workflow Task heartbeating diff --git a/docs/design-patterns/mapreduce-tree.mdx b/docs/design-patterns/mapreduce-tree.mdx index a53711a183..590dfb78af 100644 --- a/docs/design-patterns/mapreduce-tree.mdx +++ b/docs/design-patterns/mapreduce-tree.mdx @@ -429,6 +429,7 @@ public class NodeWorkflowImpl implements NodeWorkflow { ## Common pitfalls +- **Too many direct children per Node.** Temporal's [guidance](/child-workflows#when-to-use-child-workflows) is that a single parent should not spawn more than 1,000 Child Workflow Executions. This applies to every Node in the tree, not just the Root — if a Node's own branching factor produces more than that many direct child Workflows, add another tree level rather than widening a single Node. - **Thundering herd.** The MapReduce Tree fans out exponentially. For large record sets, all leaf Activities start nearly simultaneously. Ensure your downstream system can absorb the burst, or switch to [Sliding Window](/design-patterns/sliding-window) for rate limiting. - **Signal storms.** If thousands of leaves all signal a single Node at the same time, the Node's signal queue can become a bottleneck. A two-level tree (Root → Nodes → Leaves) distributes this load; a deeper tree helps even more. - **History bloat in the Root Workflow.** Each child start and signal received adds events to the Root's history. For very large record sets, consider adding an extra tree level to keep the Root from receiving too many direct signals. diff --git a/docs/design-patterns/non-retryable-errors.mdx b/docs/design-patterns/non-retryable-errors.mdx index d6dd9a7ff9..0d5ada0cef 100644 --- a/docs/design-patterns/non-retryable-errors.mdx +++ b/docs/design-patterns/non-retryable-errors.mdx @@ -476,6 +476,7 @@ try { - **Using the error message instead of a type name.** `RetryPolicy.NonRetryableErrorTypes` matches on type names, not message strings. Without a type name, the policy cannot identify the error. - **Swallowing the `ActivityError` without logging.** Non-retryable errors fail fast and silently if you do not catch and log them. Always log the failure before re-raising or returning an error result. - **Confusing non-retryable errors with Workflow failures.** A non-retryable `ActivityError` fails the Activity and delivers the error to the Workflow. The Workflow itself does not fail unless it re-raises the error without catching it. +- **Losing the flag by wrapping the error.** The SDK inspects only the **outermost** error to decide how to represent the failure to the Temporal Service — the Server's retry decision looks only at that top-level failure info, not at the `cause` chain. If your Activity catches a non-retryable `ApplicationFailure` and re-throws it wrapped in a plain language error or exception (for example, Go's `fmt.Errorf("...: %w", err)`), the SDK converts that outer error into a new, retryable failure and the `non_retryable` flag is silently lost. To add context without losing it, wrap the error in another Application Failure that carries the same non-retryable flag — see [The outermost error type determines retryability](/encyclopedia/application-failures#outermost-error-type). ## Related diff --git a/docs/design-patterns/sliding-window.mdx b/docs/design-patterns/sliding-window.mdx index f56ac28787..0e43f8702a 100644 --- a/docs/design-patterns/sliding-window.mdx +++ b/docs/design-patterns/sliding-window.mdx @@ -491,7 +491,7 @@ public interface SlidingWindowWorkflow { - **Preserve the parent Workflow ID across Continue-as-New.** The parent's Workflow ID is stable across `continueAsNew` runs — do not generate a new one. Children read the parent's Workflow ID from their own Workflow metadata (`workflowInfo().parent` in TypeScript, `workflow.info().parent` in Python, `workflow.GetInfo(ctx).ParentWorkflowExecution` in Go, `Workflow.getInfo().getParentWorkflowId()` in Java) rather than receiving it as an argument, then signal by Workflow ID (with no run ID) so they always reach the current run. - **Use `PARENT_CLOSE_POLICY_ABANDON` on child Workflows.** This lets children that were started by a previous run complete normally even after the parent has continued as new. -- **Size the window conservatively at first.** Each in-flight child counts toward the 2,000 unfinished-actions limit for the parent. A window of 50–200 is a reasonable starting point depending on child duration and downstream capacity. +- **Size the window conservatively at first.** Each in-flight child counts toward the 2,000 unfinished-actions limit for the parent, and Temporal's own guidance recommends staying well under that: a single parent should not spawn more than 1,000 Child Workflow Executions in total. A window of 50–200 is a reasonable starting point depending on child duration and downstream capacity. - **Pass only IDs (not full records) to child Workflows.** Workflow inputs are stored in event history. Keep them small. - **Carry minimal state into `continueAsNew`.** Pass `windowSize`, `startIndex`, the live in-flight count (`active`), a running `totalProcessed`, and the record ID list (or a reference to it). Do not accumulate results in the parent — collect them out-of-band if needed. diff --git a/docs/develop/worker-tuning-reference.mdx b/docs/develop/worker-tuning-reference.mdx index 532ca60587..0cb302375a 100644 --- a/docs/develop/worker-tuning-reference.mdx +++ b/docs/develop/worker-tuning-reference.mdx @@ -55,13 +55,13 @@ Compute settings control how many Tasks a Worker can execute concurrently. | Setting | Description | |---------|-------------| | `MaxConcurrentWorkflowTaskExecutionSize` | Maximum concurrent Workflow Tasks | -| `MaxConcurrentActivityTaskExecutionSize` | Maximum concurrent Activity Tasks | -| `MaxConcurrentLocalActivityTaskExecutionSize` | Maximum concurrent Local Activities | +| `MaxConcurrentActivityExecutionSize` | Maximum concurrent Activity Tasks | +| `MaxConcurrentLocalActivityExecutionSize` | Maximum concurrent Local Activities | | `MaxWorkflowThreadCount` / `workflowThreadPoolSize` | Thread pool for Workflow execution | ### Compute defaults by SDK -| SDK | MaxConcurrentWorkflowTaskExecutionSize | MaxConcurrentActivityTaskExecutionSize | MaxConcurrentLocalActivityTaskExecutionSize | MaxWorkflowThreadCount | +| SDK | MaxConcurrentWorkflowTaskExecutionSize | MaxConcurrentActivityExecutionSize | MaxConcurrentLocalActivityExecutionSize | MaxWorkflowThreadCount | |-----|----------------------------------------|----------------------------------------|---------------------------------------------|------------------------| | **Go** | 1,000 | 1,000 | 1,000 | - | | **Java** | 200 | 200 | 200 | 600 | @@ -136,7 +136,7 @@ For the complete metrics reference, see [SDK metrics](/references/sdk-metrics). | Worker configuration option | SDK metric | |-----------------------------|------------| | `MaxConcurrentWorkflowTaskExecutionSize` | [`worker_task_slots_available {worker_type = WorkflowWorker}`](/references/sdk-metrics#worker_task_slots_available) | -| `MaxConcurrentActivityTaskExecutionSize` | [`worker_task_slots_available {worker_type = ActivityWorker}`](/references/sdk-metrics#worker_task_slots_available) | +| `MaxConcurrentActivityExecutionSize` | [`worker_task_slots_available {worker_type = ActivityWorker}`](/references/sdk-metrics#worker_task_slots_available) | | `MaxWorkflowThreadCount` | [`workflow_active_thread_count`](/references/sdk-metrics#workflow_active_thread_count) (Java only) | | CPU-intensive logic | [`workflow_task_execution_latency`](/references/sdk-metrics#workflow_task_execution_latency) | From efd8ab87444999faabd62ef04cf181a5a2b4aa22 Mon Sep 17 00:00:00 2001 From: Duncan Mackenzie Date: Thu, 10 Sep 2026 15:15:46 -0700 Subject: [PATCH 4/4] Fix Eager Workflow Start SDK coverage in the performance guide The performance-latency-patterns.mdx selection guide still said Eager Workflow Start was limited to Go/Java/Python and told TypeScript users outright that it wasn't available - both wrong since eager-workflow-start.mdx was corrected to cover TypeScript and .NET. Update the comparison table's SDK Support column and drop the now-incorrect TypeScript-specific callout and "You are using TypeScript" branch, which steered TypeScript readers away from a pattern they can actually use. Flagged by chatgpt-codex-connector's review on #5246. --- docs/design-patterns/performance-latency-patterns.mdx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/design-patterns/performance-latency-patterns.mdx b/docs/design-patterns/performance-latency-patterns.mdx index 0e951e4204..068b4ec610 100644 --- a/docs/design-patterns/performance-latency-patterns.mdx +++ b/docs/design-patterns/performance-latency-patterns.mdx @@ -41,15 +41,11 @@ The numbers below are approximate benchmarks based on a three-Activity transacti | [Early Return](/design-patterns/early-return) | ~265 ms | ~850 ms | All | | [Local Activities](/design-patterns/local-activities) | ~275 ms | ~275 ms | All | | [Early Return + Local Activities](/design-patterns/early-return-local-activities) | ~160 ms | ~275 ms | All | -| [Eager Workflow Start](/design-patterns/eager-workflow-start) + Local Activities | ~265 ms | ~265 ms | Go, Java, Python | -| Early Return + Local Activities + Eager Start | ~160 ms | ~265 ms | Go, Java, Python | +| [Eager Workflow Start](/design-patterns/eager-workflow-start) + Local Activities | ~265 ms | ~265 ms | Go, Java, Python, TypeScript, .NET | +| Early Return + Local Activities + Eager Start | ~160 ms | ~265 ms | Go, Java, Python, TypeScript, .NET | **First Response** is the time until the client receives an actionable result. **Total Latency** is the time until the Workflow fully completes. -:::tip[TypeScript users] -Eager Workflow Start is not available in the TypeScript SDK, but the latency gap is small (~30–50 ms per Workflow start). [Local Activities](/design-patterns/local-activities) and [Early Return + Local Activities](/design-patterns/early-return-local-activities) are fully supported and achieve competitive results: ~275 ms total latency and ~160 ms first-response latency respectively. -::: - ## Patterns in this section