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 4a62b4b9295892f1ec85ac7ba1d4077fe5bbd678 Mon Sep 17 00:00:00 2001 From: Duncan Mackenzie Date: Fri, 4 Sep 2026 12:57:46 -0700 Subject: [PATCH 4/4] Add missing cross-links and fix tone in the design-patterns catalog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-links (from the Tier B review's "missing cross-links" list): - best-practices/multi-tenant-patterns.mdx now links back to design-patterns/fairness.mdx and priority-task-queues.mdx from its "Single Task Queue with Fairness" section — the reverse direction already existed (fairness.mdx links to multi-tenant-patterns.mdx) but not this one. - child-workflows.mdx -> /parent-close-policy and the four per-SDK child-workflows how-to guides. - signal-with-start.mdx, request-response-via-updates.mdx -> /sending-messages and /handling-messages. - pick-first.mdx -> /develop/go/workflows/selectors (same underlying Go Selector construct, same sample repo). - parallel-execution.mdx -> /troubleshooting/blob-size-limit-error and /workflow-execution/limits. - fixed-wall-time-retries.mdx -> /encyclopedia/detecting-activity-failures (defines ScheduleToCloseTimeout/StartToCloseTimeout). - fast-slow-retries.mdx, retry-metrics.mdx, resumable-activity.mdx -> /encyclopedia/retry-policies, matching their sibling pages. - activity-dependency-injection.mdx -> the Go/TypeScript Activity DI how-tos and best-practices/worker.mdx. - updatable-timer.mdx -> /develop/typescript/workflows/timers, which documents the same UpdatableTimer class via the temporal-time-utils package. - long-running-activity.mdx, polling.mdx -> /encyclopedia/detecting-activity-failures#activity-heartbeat. - delayed-callback.mdx -> /activity-execution#asynchronous-activity-completion. - delayed-start.mdx, continue-as-new.mdx -> /schedule, and linked the existing unlinked "Temporal Schedules" bullet in delayed-start.mdx. All new anchor links verified against the built HTML output directly (yarn check-links flags them as "fragment-redirect" false positives — it doesn't parse the unquoted id= attributes Docusaurus emits). Tone fixes: - resumable-activity.mdx and fast-slow-retries.mdx: removed a narrative-climax line ("the Workflow never died") and a self-congratulatory flourish ("exactly what a Temporal Workflow is"), replaced with plain factual statements. - downstream-rate-limiting.mdx: cut a Problem-section sentence that only restated the one before it. - worker-specific-taskqueue.mdx, activity-dependency-injection.mdx: reformatted "When to use" (and, for the former, "Benefits and trade-offs") from run-on sentences stacking 4-5 unrelated items into bulleted Good-fit/Poor-fit lists, matching every sibling page's template. - continue-as-new.mdx, long-running-activity.mdx, entity-workflow.mdx, polling.mdx, delayed-start.mdx, updatable-timer.mdx: rewrote the templated "you often need X1, X2, X3, X4, and X5 ... without it you must A1, A2, A3, and A4" Problem-section opener into plain prose, preserving every fact. --- docs/best-practices/multi-tenant-patterns.mdx | 2 + .../activity-dependency-injection.mdx | 18 +++++++- docs/design-patterns/child-workflows.mdx | 5 +++ docs/design-patterns/continue-as-new.mdx | 6 +-- docs/design-patterns/delayed-callback.mdx | 4 ++ docs/design-patterns/delayed-start.mdx | 6 +-- .../downstream-rate-limiting.mdx | 2 +- docs/design-patterns/entity-workflow.mdx | 2 +- docs/design-patterns/fast-slow-retries.mdx | 3 +- .../fixed-wall-time-retries.mdx | 1 + .../design-patterns/long-running-activity.mdx | 8 +++- docs/design-patterns/parallel-execution.mdx | 5 +++ docs/design-patterns/pick-first.mdx | 4 ++ docs/design-patterns/polling.mdx | 8 +++- .../request-response-via-updates.mdx | 5 +++ docs/design-patterns/resumable-activity.mdx | 6 ++- docs/design-patterns/retry-metrics.mdx | 4 ++ docs/design-patterns/signal-with-start.mdx | 5 +++ docs/design-patterns/updatable-timer.mdx | 8 +++- .../worker-specific-taskqueue.mdx | 41 +++++++++++++------ 20 files changed, 112 insertions(+), 31 deletions(-) diff --git a/docs/best-practices/multi-tenant-patterns.mdx b/docs/best-practices/multi-tenant-patterns.mdx index 417808b4b9..5432c36813 100644 --- a/docs/best-practices/multi-tenant-patterns.mdx +++ b/docs/best-practices/multi-tenant-patterns.mdx @@ -80,6 +80,8 @@ This pattern works well when you have many tenants with different service tiers + + ### 3. Shared Workflow Task Queues, separate Activity Task Queues diff --git a/docs/design-patterns/activity-dependency-injection.mdx b/docs/design-patterns/activity-dependency-injection.mdx index c8a84de570..ec15dc41da 100644 --- a/docs/design-patterns/activity-dependency-injection.mdx +++ b/docs/design-patterns/activity-dependency-injection.mdx @@ -743,9 +743,17 @@ Because the breaker counts failures, size the Activity retry policy accordingly. ## When to use -This pattern is a good fit when your Activities access external services such as databases, message queues, or third-party APIs. It is appropriate when you want to initialize expensive resources once per Worker process, when you need to test Activity logic without connecting to real services, or when you operate in multiple environments (development, staging, production) that require different dependency configurations. +**Good fit:** -This pattern is not necessary for Activities that are pure functions with no external dependencies, or for Activities that only use Temporal-provided context like heartbeating and logging. +- Activities access external services such as databases, message queues, or third-party APIs +- You want to initialize expensive resources once per Worker process +- You need to test Activity logic without connecting to real services +- You operate in multiple environments (development, staging, production) that require different dependency configurations + +**Poor fit:** + +- Activities are pure functions with no external dependencies +- Activities only use Temporal-provided context, such as heartbeating and logging ## Benefits and trade-offs @@ -776,6 +784,12 @@ The trade-off is that all Activity executions on a given Worker share the same d - **[Entity Workflow](/design-patterns/entity-workflow)**: Long-lived Workflows that manage stateful entities, often using Activities with injected dependencies. - **[Worker-Specific Task Queues](/design-patterns/worker-specific-taskqueue)**: Routing Activities to specific Workers, which can have different injected dependencies. +### References + +- [Activities (Go)](/develop/go/activities/basics): Struct-based Activities sharing a DB pool, client connection, or other process-level resources. +- [Activities (TypeScript)](/develop/typescript/activities/basics): The factory-function pattern for sharing dependencies between Activities. +- [Worker deployment and performance](/best-practices/worker): A reference-app example of registering an Activity struct with injected configuration. + ### Sample code ### Go diff --git a/docs/design-patterns/child-workflows.mdx b/docs/design-patterns/child-workflows.mdx index af096e6eef..ada99b0752 100644 --- a/docs/design-patterns/child-workflows.mdx +++ b/docs/design-patterns/child-workflows.mdx @@ -781,6 +781,11 @@ Starting a Child Workflow has more overhead than starting an Activity. - **[Continue-As-New](/design-patterns/continue-as-new)**: Child Workflows can use Continue-As-New independently. - **[Saga Pattern](/design-patterns/saga-pattern)**: Children as compensatable transactions. +### References + +- [Parent Close Policy](/parent-close-policy): Canonical reference for `TERMINATE`, `ABANDON`, and `REQUEST_CANCEL`, including the default and how each behaves during a Continue-As-New. +- [Child Workflows (Go)](/develop/go/workflows/child-workflows) · [Child Workflows (Java)](/develop/java/workflows/child-workflows) · [Child Workflows (Python)](/develop/python/workflows/child-workflows) · [Child Workflows (TypeScript)](/develop/typescript/workflows/child-workflows): Official per-SDK how-to guides. + ### Sample code **Java:** diff --git a/docs/design-patterns/continue-as-new.mdx b/docs/design-patterns/continue-as-new.mdx index 85337100a5..ea7f6d1d95 100644 --- a/docs/design-patterns/continue-as-new.mdx +++ b/docs/design-patterns/continue-as-new.mdx @@ -19,9 +19,9 @@ 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 51,200 event history limit, and maintain Workflow state across logical restarts. +Long-running Workflows — periodic tasks that run indefinitely, infinite loops spanning months or years, or Workflows processing an unbounded stream of data — accumulate Event History with every iteration. Left unchecked, that history eventually hits the 51,200-event limit, and the Workflow still needs to keep its state across whatever comes next. -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. +Without Continue-As-New, the alternatives are all worse: manually stop and restart Workflows and lose continuity, build external orchestration to manage the Workflow's lifecycle, or accept degraded performance as history grows — and risk failure once it hits the limit. ## Solution @@ -397,7 +397,7 @@ You cannot undo Continue-As-New once triggered. - **Version carefully.** Ensure new code can handle state from old executions. - **Monitor history size.** Track event count and continue before hitting limits. - **Use typed APIs.** In Java, prefer `newContinueAsNewStub()` over untyped `continueAsNew()`. In TypeScript, use the generic `continueAsNew()` for type safety. -- **Consider cron.** For fixed Schedules, use Temporal Schedules instead. +- **Consider cron.** For fixed Schedules, use [Temporal Schedules](/schedule) instead. - **Test state transfer.** Verify state correctly passes between executions. ## Common pitfalls diff --git a/docs/design-patterns/delayed-callback.mdx b/docs/design-patterns/delayed-callback.mdx index d7b030bcb2..9e26dacd27 100644 --- a/docs/design-patterns/delayed-callback.mdx +++ b/docs/design-patterns/delayed-callback.mdx @@ -756,3 +756,7 @@ 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 + +### References + +- [Asynchronous Activity Completion](/activity-execution#asynchronous-activity-completion) — canonical reference for Pattern 3's task-token mechanism, including when to prefer it over Signals diff --git a/docs/design-patterns/delayed-start.mdx b/docs/design-patterns/delayed-start.mdx index 8bfc973af7..80699a2263 100644 --- a/docs/design-patterns/delayed-start.mdx +++ b/docs/design-patterns/delayed-start.mdx @@ -18,9 +18,9 @@ The Workflow execution is registered in Temporal right away, but the first Workf ## Problem -In business processes, you often need Workflows that start execution at a future time, are created immediately for tracking but execute later, avoid external scheduling systems or cron jobs for one-time delays, and maintain Workflow identity and queryability before execution begins. +Some business processes need a Workflow that exists and is queryable immediately, but doesn't actually start executing until a specific time in the future — a one-time delay, not a recurring schedule. -Without delayed start, you must use external schedulers to trigger Workflow creation later, start Workflows immediately and sleep as the first operation (which wastes resources), implement complex queueing systems for deferred execution, or use Temporal Schedules for one-time delays (which is more than you need). +Without delayed start, the alternatives are all a worse fit: trigger Workflow creation from an external scheduler, start the Workflow immediately and make `sleep()` its first operation (wasting a Workflow slot for no reason), build a custom queueing system for deferred execution, or reach for [Temporal Schedules](/schedule) — built for recurring executions, more machinery than a single delay needs. ## Solution @@ -448,7 +448,7 @@ Regular Signals sent during the delay are not delivered until the first Workflow ### Patterns -- **Temporal Schedules**: For recurring Workflow execution. +- **[Temporal Schedules](/schedule)**: For recurring Workflow execution. - **[Updatable Timer](/design-patterns/updatable-timer)**: For dynamically adjustable delays within Workflows. - **[Signal with Start](/design-patterns/signal-with-start)**: Interacting with Workflows before execution. diff --git a/docs/design-patterns/downstream-rate-limiting.mdx b/docs/design-patterns/downstream-rate-limiting.mdx index 13a4ff1efe..bd4dd39833 100644 --- a/docs/design-patterns/downstream-rate-limiting.mdx +++ b/docs/design-patterns/downstream-rate-limiting.mdx @@ -23,7 +23,7 @@ The Temporal matching service enforces this limit before dispatching tasks, so t ## Problem -Many downstream systems — LLM providers, payment processors, third-party REST APIs — enforce requests-per-second limits. Some systems cannot handle more than a defined level of requests per second. +Many downstream systems — LLM providers, payment processors, third-party REST APIs — enforce requests-per-second limits. When many Temporal Workflows schedule Activities concurrently, the resulting burst can saturate those limits, causing request failures, cascading retries, and increased latency for all callers. Without centralized throttling, each Activity implementation must manage backpressure independently, which scatters policy across the codebase and provides no enforcement at the Temporal scheduling layer. diff --git a/docs/design-patterns/entity-workflow.mdx b/docs/design-patterns/entity-workflow.mdx index 56ef844093..14aee1415d 100644 --- a/docs/design-patterns/entity-workflow.mdx +++ b/docs/design-patterns/entity-workflow.mdx @@ -18,7 +18,7 @@ Each entity gets its own Workflow instance identified by the entity ID, handling ## Problem -Many business domains have entities that exist for extended periods, undergo multiple state transitions over their lifetime, need to maintain consistent state across operations, require audit trails of all changes, and must handle concurrent operations safely. +Many business domains have entities — accounts, orders, subscriptions — that exist for extended periods and go through many state transitions over their lifetime. Modeling one well means keeping its state consistent across operations, recording an audit trail of every change, and handling concurrent operations safely. Traditional approaches struggle with these requirements: diff --git a/docs/design-patterns/fast-slow-retries.mdx b/docs/design-patterns/fast-slow-retries.mdx index 4f8f3cd3b5..7cf1d96d4b 100644 --- a/docs/design-patterns/fast-slow-retries.mdx +++ b/docs/design-patterns/fast-slow-retries.mdx @@ -38,7 +38,7 @@ Use the Workflow itself as a retry orchestrator across two phases: **Phase 2 — Slow retries**: When the fast retry policy is exhausted, catch the `ActivityError` in the Workflow and execute the Activity again with a long `InitialInterval` and unlimited `MaximumAttempts`. The Temporal Service owns the slow retry management; the Workflow blocks until the Activity eventually succeeds. -This design is invisible in conventional retry libraries because it requires the retry orchestrator to be a durable, resumable process — exactly what a Temporal Workflow is. +Conventional retry libraries can't implement this two-phase design, because the retry orchestrator needs to survive across the entire fast-then-slow window — hours or days — without staying resident in a process. A Temporal Workflow persists its state between retries, so it can hold that phase transition durably. ```mermaid flowchart TD @@ -310,5 +310,6 @@ If the business process has a maximum wait time, add a `ScheduleToCloseTimeout` ### References +- [Temporal Retry Policies](/encyclopedia/retry-policies) - [Understanding Workflow Retries and Failures](https://community.temporal.io/t/understanding-workflow-retries-and-failures/122) - [Failure Handling in Practice](https://temporal.io/blog/failure-handling-in-practice) diff --git a/docs/design-patterns/fixed-wall-time-retries.mdx b/docs/design-patterns/fixed-wall-time-retries.mdx index f52d51424c..56595f697c 100644 --- a/docs/design-patterns/fixed-wall-time-retries.mdx +++ b/docs/design-patterns/fixed-wall-time-retries.mdx @@ -323,5 +323,6 @@ const { authorizeTransaction } = wf.proxyActivities({ ### References +- [Detecting Activity Failures](/encyclopedia/detecting-activity-failures): Canonical reference for `ScheduleToCloseTimeout` and `StartToCloseTimeout`, the mechanism this pattern is built on. - [Activity Timeouts](https://temporal.io/blog/activity-timeouts) - [Temporal Retry Policies](/encyclopedia/retry-policies) diff --git a/docs/design-patterns/long-running-activity.mdx b/docs/design-patterns/long-running-activity.mdx index 831e0e280e..a2afb5e2e3 100644 --- a/docs/design-patterns/long-running-activity.mdx +++ b/docs/design-patterns/long-running-activity.mdx @@ -18,9 +18,9 @@ Heartbeats inform Temporal that the Activity is still alive and allow storing pr ## Problem -In long-running operations, you often need Activities that process large datasets or perform time-consuming operations (minutes to hours), report progress to avoid appearing stuck or timing out, resume from the last checkpoint after Worker crashes or restarts, handle cancellation requests gracefully and clean up resources, and avoid reprocessing already-completed work. +Activities that process large datasets or run for minutes to hours need a way to report progress — so they don't appear stuck or time out — and to resume from a checkpoint rather than restart from scratch after a Worker crash. They also need to handle cancellation gracefully and avoid redoing work that already finished. -Without heartbeats, you must set very long Activity timeouts that delay failure detection, reprocess entire batches from the beginning on failures, accept no visibility into Activity progress, risk zombie Activities that appear alive but are stuck, and implement custom checkpointing and recovery logic. +Without heartbeats, none of that is possible. You're left setting very long Activity timeouts that delay failure detection, reprocessing entire batches from the beginning on any failure, and building custom checkpointing and recovery logic yourself — with no visibility into whether an Activity is actually making progress or just a zombie that looks alive. ## Solution @@ -607,6 +607,10 @@ Heartbeat details have size limits, so you should avoid large objects. - **[Saga Pattern](/design-patterns/saga-pattern)**: Compensating transactions with long-running steps. - **[Polling](/design-patterns/polling)**: Heartbeating Activity for frequent polling. +### References + +- [Activity Heartbeat](/encyclopedia/detecting-activity-failures#activity-heartbeat): Canonical reference for heartbeats, heartbeat timeouts, and throttling. + ### Sample code ### Java diff --git a/docs/design-patterns/parallel-execution.mdx b/docs/design-patterns/parallel-execution.mdx index b10bc7d70b..8b08efb931 100644 --- a/docs/design-patterns/parallel-execution.mdx +++ b/docs/design-patterns/parallel-execution.mdx @@ -529,6 +529,11 @@ You may overwhelm external services without throttling, and storing many Futures - **[Child Workflows](/design-patterns/child-workflows)**: For complex parallel operations with their own state. - **[Saga Pattern](/design-patterns/saga-pattern)**: Parallel operations with compensation. +### References + +- [Blob Size Limit Error](/troubleshooting/blob-size-limit-error): Diagnosing and fixing the 4 MB gRPC message limit this pattern's pitfalls mention. +- [Workflow Execution limits](/workflow-execution/limits): Canonical reference for the 2,000-pending-operations limit, which applies to Activities, Child Workflows, Signals, and Cancellation requests together, not just Activities. + ### Sample code **Java:** diff --git a/docs/design-patterns/pick-first.mdx b/docs/design-patterns/pick-first.mdx index c8e8f40ed1..86967f3286 100644 --- a/docs/design-patterns/pick-first.mdx +++ b/docs/design-patterns/pick-first.mdx @@ -555,6 +555,10 @@ Only the first result is used; others are discarded. - **[Parallel Execution](/design-patterns/parallel-execution)**: Execute in parallel and combine all results. +### References + +- [Selectors (Go)](/develop/go/workflows/selectors): The Go SDK's `Selector` construct for racing multiple Futures and Channels, with the same "cancel the losers" technique. + ### Sample code - [Go Sample](https://github.com/temporalio/samples-go/tree/main/pickfirst) — Complete implementation with Worker and starter. diff --git a/docs/design-patterns/polling.mdx b/docs/design-patterns/polling.mdx index d3a06ad781..84fba5cdf0 100644 --- a/docs/design-patterns/polling.mdx +++ b/docs/design-patterns/polling.mdx @@ -18,9 +18,9 @@ It enables Workflows to wait for asynchronous operations in third-party services ## Problem -In distributed systems, you often need Workflows that wait for external jobs to complete, poll REST APIs that do not provide webhooks, check the status of long-running operations in third-party systems, handle varying poll frequencies, and avoid overwhelming external services with requests. +Workflows often need to wait on an external system that has no way to push a notification back: a REST API with no webhook, a long-running job in a third-party system, a status check that has to be repeated until it changes. Doing that well means picking the right poll frequency for the situation without overwhelming the external service. -Without proper polling strategies, you must implement complex retry logic manually, risk unbounded Workflow history growth, choose between responsiveness and resource efficiency, and handle heartbeating and timeout management yourself. +Without a deliberate polling strategy, you're left building retry logic by hand, choosing between responsiveness and resource efficiency with no way to have both, and managing heartbeating and timeouts yourself — while Workflow history grows with every poll. ## Solution @@ -724,6 +724,10 @@ Periodic sequence is the most flexible but adds complexity through Child Workflo - **[Long-Running Activity](/design-patterns/long-running-activity)**: Reporting progress in long Activities. - **[Continue-As-New](/design-patterns/continue-as-new)**: Managing unbounded Workflow history. +### References + +- [Activity Heartbeat](/encyclopedia/detecting-activity-failures#activity-heartbeat): Canonical reference for heartbeats, heartbeat timeouts, and throttling — the mechanism the frequent-polling variant relies on. + ### Sample code ### Java diff --git a/docs/design-patterns/request-response-via-updates.mdx b/docs/design-patterns/request-response-via-updates.mdx index c7b0b8c96b..e405f5d132 100644 --- a/docs/design-patterns/request-response-via-updates.mdx +++ b/docs/design-patterns/request-response-via-updates.mdx @@ -308,6 +308,11 @@ Trade-offs: - **[Entity Workflow](/design-patterns/entity-workflow)**: Long-running Workflows representing business entities. - **[Early Return](/design-patterns/early-return)**: Returning intermediate results before Workflow completion. +### References + +- [Sending Messages](/sending-messages): Canonical reference for Updates as a delivery mechanism, alongside Signals and Queries. +- [Handling Messages](/handling-messages): Canonical reference for writing Update handlers, including validators and idempotency. + ### Sample code - [Safe Message Handlers (Python)](https://github.com/temporalio/samples-python/tree/main/message_passing/safe_message_handlers) — Concurrent Update handling with validation. diff --git a/docs/design-patterns/resumable-activity.mdx b/docs/design-patterns/resumable-activity.mdx index 02b77b2c5a..26cba2d40a 100644 --- a/docs/design-patterns/resumable-activity.mdx +++ b/docs/design-patterns/resumable-activity.mdx @@ -83,7 +83,7 @@ The following describes each step: 7. The Workflow transitions to `AWAITING_APPROVAL` and parks again, waiting for the client to approve the transfer. 8. The client sends an `approve` Signal. The Workflow completes and returns the result. -The key insight: **the Workflow never died**. It survived bad input, waited indefinitely without polling, accepted an external correction, and completed cleanly. Its entire state — status, corrected account, approval decision — is durable in Temporal throughout. +The Workflow stays alive throughout: it waits indefinitely without polling, accepts an external correction, and completes without restarting. Its state — status, corrected account number, approval decision — is durable in Temporal the entire time. ## Implementation @@ -569,3 +569,7 @@ stateDiagram-v2 ### Guides - [Recover business processes without restarting](/guides/recover-without-restart): A `recoverableStep` implementation of this pattern in a six-step loan pipeline, with Search Attribute-based routing so operators can find and fix blocked cases. + +### References + +- [Temporal Retry Policies](/encyclopedia/retry-policies) diff --git a/docs/design-patterns/retry-metrics.mdx b/docs/design-patterns/retry-metrics.mdx index 6b2dc78d82..5bc8589646 100644 --- a/docs/design-patterns/retry-metrics.mdx +++ b/docs/design-patterns/retry-metrics.mdx @@ -377,3 +377,7 @@ if (ctx.info.attempt > ALERT_THRESHOLD) { - [Fast/Slow Retries](/design-patterns/fast-slow-retries): Combine by emitting this metric inside the slow-phase Activity to alert when patient waiting has gone on too long. - [Fixed Count of Retries](/design-patterns/fixed-count-retries): Cap attempts at a fixed number instead of alerting at a threshold. - [Error Handling & Retry Patterns](/design-patterns/error-handling-patterns): Overview and decision tree for all retry patterns. + +### References + +- [Temporal Retry Policies](/encyclopedia/retry-policies) diff --git a/docs/design-patterns/signal-with-start.mdx b/docs/design-patterns/signal-with-start.mdx index f00de183f2..aba56c2257 100644 --- a/docs/design-patterns/signal-with-start.mdx +++ b/docs/design-patterns/signal-with-start.mdx @@ -312,6 +312,11 @@ Both ALLOW_DUPLICATE and ALLOW_DUPLICATE_FAILED_ONLY work well with Signal with - **[Request-Response via Updates](/design-patterns/request-response-via-updates)**: When you need synchronous responses instead of fire-and-forget. - **[Early Return](/design-patterns/early-return)**: Update-with-Start for request-response with lazy initialization. +### References + +- [Sending Messages](/sending-messages): Canonical reference for Signal-with-Start and Update-with-Start as delivery mechanisms. +- [Handling Messages](/handling-messages): Canonical reference for writing Signal and Update handlers, including idempotency and validation. + ### Sample code **Python** diff --git a/docs/design-patterns/updatable-timer.mdx b/docs/design-patterns/updatable-timer.mdx index ea29ef6e1c..bf8492da4d 100644 --- a/docs/design-patterns/updatable-timer.mdx +++ b/docs/design-patterns/updatable-timer.mdx @@ -18,9 +18,9 @@ It enables Workflows to wait for deadlines that can be extended or shortened bas ## Problem -In business processes, you often need Workflows that wait for a deadline (approval timeout, SLA expiration, grace period), allow the deadline to be extended or shortened dynamically, react immediately when the deadline changes, and continue waiting with the new deadline without restarting. +A Workflow waiting on a deadline — an approval timeout, an SLA expiration, a grace period — often needs that deadline to move: extended, shortened, or reset in reaction to something that happens while it's waiting, without restarting the wait from scratch. -Without an updatable timer, you must use fixed timeouts that cannot be adjusted, cancel and restart Workflows to change deadlines, poll frequently to check for deadline changes, or implement complex state machines to handle timing updates. +Without an updatable timer, a fixed timeout can't be adjusted once it's set. The alternatives are all worse: cancel and restart the Workflow to change the deadline, poll frequently to check whether it changed, or build a state machine to manage the timing updates yourself. ## Solution @@ -609,6 +609,10 @@ You must calculate absolute timestamps rather than relative durations. - **[Signal with Start](/design-patterns/signal-with-start)**: Receiving external events to modify behavior. - **[Approval Pattern](/design-patterns/approval)**: Approval Workflows with adjustable deadlines. +### References + +- [Timers (TypeScript)](/develop/typescript/workflows/timers): Covers the same `UpdatableTimer` class, built on the [`temporal-time-utils`](https://www.npmjs.com/package/temporal-time-utils) package. + ### Sample code - [Java](https://github.com/temporalio/samples-java/tree/main/core/src/main/java/io/temporal/samples/updatabletimer) -- Complete implementation with starter and updater. diff --git a/docs/design-patterns/worker-specific-taskqueue.mdx b/docs/design-patterns/worker-specific-taskqueue.mdx index c74cf05548..ceb2ea82a7 100644 --- a/docs/design-patterns/worker-specific-taskqueue.mdx +++ b/docs/design-patterns/worker-specific-taskqueue.mdx @@ -733,23 +733,38 @@ Both Workers receive the same Activity implementation, but only the host-specifi ## When to use -The Worker-Specific Task Queues pattern is a good fit for file processing Workflows (download, process, upload on the same host), database connection pooling (maintain a connection across Activities), GPU-bound operations (route to Workers with specific hardware), session-based external API calls, and temporary resource management (cache, temp files, locks). +**Good fit:** -It is not a good fit for stateless Activities that can run anywhere, Activities that use shared storage (S3, databases), high-availability requirements (host failure blocks the Workflow), or Workflows without local state dependencies. +- File processing Workflows (download, process, upload on the same host) +- Database connection pooling (maintain a connection across Activities) +- GPU-bound operations (route to Workers with specific hardware) +- Session-based external API calls +- Temporary resource management (cache, temp files, locks) + +**Poor fit:** + +- Stateless Activities that can run anywhere +- Activities that use shared storage (S3, databases) +- High-availability requirements (a host failure blocks the Workflow) +- Workflows without local state dependencies ## Benefits and trade-offs -Activities access local files and state without network overhead. -You do not need distributed file systems or state management. -Data transfer between Workers is eliminated. -The first Activity can run on any Worker; only subsequent ones are pinned. -Task Queue routing is recorded in Workflow history, ensuring deterministic behavior. - -The trade-offs to consider are that if the specific Worker crashes, Activities cannot proceed until the ScheduleToStartTimeout expires. -Host-specific queues may have uneven load distribution. -You must manage multiple Task Queues per Worker. -You must set ScheduleToStartTimeout to handle Worker unavailability. -You need to handle cleanup if the Workflow fails mid-process. +**Benefits:** + +- Activities access local files and state without network overhead +- No distributed file systems or state management needed +- Data transfer between Workers is eliminated +- The first Activity can run on any Worker; only subsequent ones are pinned +- Task Queue routing is recorded in Workflow history, ensuring deterministic behavior + +**Trade-offs:** + +- If the specific Worker crashes, Activities cannot proceed until the `ScheduleToStartTimeout` expires +- Host-specific queues may have uneven load distribution +- You must manage multiple Task Queues per Worker +- You must set `ScheduleToStartTimeout` to handle Worker unavailability +- You need to handle cleanup if the Workflow fails mid-process ## Comparison with alternatives