Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/best-practices/multi-tenant-patterns.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ This pattern works well when you have many tenants with different service tiers

<RelatedReadContainer>
<RelatedReadItem path="/develop/task-queue-priority-fairness#task-queue-fairness" text="Task Queue Fairness Reference" archetype="feature-guide" />
<RelatedReadItem path="/design-patterns/fairness" text="Fairness pattern" />
<RelatedReadItem path="/design-patterns/priority-task-queues" text="Priority Task Queues pattern" />
</RelatedReadContainer>

### 3. Shared Workflow Task Queues, separate Activity Task Queues
Expand Down
18 changes: 16 additions & 2 deletions docs/design-patterns/activity-dependency-injection.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions docs/design-patterns/child-workflows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:**
Expand Down
6 changes: 3 additions & 3 deletions docs/design-patterns/continue-as-new.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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<typeof myWorkflow>()` 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
Expand Down
4 changes: 4 additions & 0 deletions docs/design-patterns/delayed-callback.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 3 additions & 3 deletions docs/design-patterns/delayed-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion docs/design-patterns/downstream-rate-limiting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion docs/design-patterns/entity-workflow.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
3 changes: 2 additions & 1 deletion docs/design-patterns/fast-slow-retries.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
1 change: 1 addition & 0 deletions docs/design-patterns/fixed-wall-time-retries.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -323,5 +323,6 @@ const { authorizeTransaction } = wf.proxyActivities<typeof activities>({

### 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)
8 changes: 6 additions & 2 deletions docs/design-patterns/long-running-activity.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions docs/design-patterns/parallel-execution.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:**
Expand Down
4 changes: 4 additions & 0 deletions docs/design-patterns/pick-first.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 6 additions & 2 deletions docs/design-patterns/polling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions docs/design-patterns/request-response-via-updates.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 5 additions & 1 deletion docs/design-patterns/resumable-activity.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
4 changes: 4 additions & 0 deletions docs/design-patterns/retry-metrics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
5 changes: 5 additions & 0 deletions docs/design-patterns/signal-with-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down
Loading