Updatable Timer solution mechanism by language
Each SDK provides a different mechanism: Java uses `Workflow.await(Duration, condition)` which returns false when the duration expires or true when the condition is met. TypeScript uses `wf.condition(fn, timeout)` which returns false on timeout or true when the function returns true. Python uses `workflow.wait_condition(fn, timeout=duration)` which returns normally on condition met or raises asyncio.TimeoutError on timeout. Go uses `workflow.NewTimer()` combined with `workflow.NewSelector()` to race a timer against a Signal channel.
Updatable Timer core pattern logic
The core of the pattern is a reusable timer helper that loops on a blocking wait, recalculating the sleep duration each time the wake-up time is updated. When a Signal updates the wake-up time, the condition becomes true, the Workflow recalculates the sleep duration, and blocks again with the new deadline.
Updatable Timer when to use
The Updatable Timer pattern is a good fit for approval Workflows with deadline extensions, SLA management with grace periods, time-based escalations that can be postponed, auction bidding with extended closing times, and payment grace periods that can be adjusted. It is not a good fit for fixed timeouts that never change (use a fixed sleep), immediate cancellation (use cancellation scopes), or complex scheduling (use Temporal Schedules).
Updatable Timer benefits
The pattern allows you to adjust deadlines without restarting Workflows. Changes take effect instantly. The timer helper is reusable across multiple Workflows. All timing is based on Workflow time, ensuring replay consistency. You can Query the current deadline at any time.
Updatable Timer trade-offs
The trade-offs to consider are that the pattern requires an external process to send update Signals. Each timer instance manages one deadline. Previous deadlines are not tracked (add tracking if needed). You must calculate absolute timestamps rather than relative durations.
Updatable Timer best practice: use absolute timestamps
Store wake-up time as an absolute value (epoch millis in Java/TypeScript, epoch seconds in Python, time.Time in Go), not relative durations.
Updatable Timer best practice: validate updates
Ensure new deadlines are in the future.
Updatable Timer best practice: add Queries
Expose the current deadline via Query methods.
Updatable Timer best practice: handle edge cases
Check if the timer already expired before updating.
Activity Dependency Injection pattern
Activity Dependency Injection injects external dependencies such as clients, connections, and configuration into Activities at Worker startup, keeping Workflow code deterministic and Activities testable. Use this pattern when Activities depend on external resources.
Worker Configuration Patterns overview
Worker Configuration Patterns cover how to set up Workers, route work to them, and give Activities the external dependencies they need while keeping Workflow code deterministic and testable.
Request-Response via Updates pattern use case
Request-Response via Updates sends a request into a running Workflow and receives a validated result on the same call, using an Update handler. Use this pattern when you need a result back from the Workflow with validation.
Event Accumulator pattern use case
Event Accumulator collects a stream of incoming Signals into a buffer and processes them together as a batch, rather than one at a time. Use this pattern when you receive many events and want to process them in batches.
Signal with Start pattern use case
Signal with Start starts a Workflow and delivers a Signal in a single atomic operation. If the Workflow already runs, it receives the Signal directly. Use this pattern when you want to send a message without checking whether the Workflow is running.
Workflow messaging patterns overview
Workflow messaging patterns cover how external callers communicate with running Workflows by starting them on demand, sending data in, reading results back, and collecting streams of events. These patterns build on Temporal's Signals and Updates.
Breaking up larger functionality into multiple activities
Larger pieces of functionality should be broken up into multiple activities. This makes it easier to do failure recovery, have short timeouts, and be idempotent.
When not to use Worker-Specific Task Queues pattern
The Worker-Specific Task Queues pattern 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.
Worker-Specific Task Queues pattern overview
The Worker-Specific Task Queues pattern enables routing Activities to specific Worker hosts when Activities must execute on the same machine. This is essential for Workflows where subsequent Activities depend on local state, files, or resources created by previous Activities on a particular host.
Best practice: limit concurrent Workflows in Worker-Specific Task Queues
Limit concurrent Workflows per Worker to prevent resource exhaustion. This prevents a single Worker from being overwhelmed when multiple Workflows route Activities to its host-specific queue.
Two-tier Task Queue architecture
The solution uses a two-tier Task Queue architecture: a default shared Task Queue for initial Activities, and dynamically-named host-specific Task Queues for Activities that must run on the same Worker. The first Activity returns its host-specific Task Queue name, and subsequent Activities use that queue.
scheduleToStartTimeout is critical for host-specific queues
The scheduleToStartTimeout (or setScheduleToStartTimeout() in Java) must be set for host-specific queues. If the specific Worker is unavailable, the Activity Task cannot sit in the host-specific queue indefinitely. This timeout is non-retryable: when it expires, the Activity fails rather than retrying, because a retry would only place the Task back on the same queue. The Workflow catches that failure and retries the entire sequence so the work can restart on a different host.
Worker-Specific Task Queues pattern implementation sequence
The pattern execution follows these steps: (1) The Workflow dispatches the download Activity on the default Task Queue and any available Worker picks it up. (2) The Worker downloads the file and returns both the local file path and its host-specific Task Queue name. (3) The Workflow creates new Activity options targeting the Worker's host-specific Task Queue. (4) Subsequent Activities (process and upload) execute on the same Worker, where the file is already on disk. (5) Other Workers never see the host-specific Activities.
taskQueue option in Worker-Specific pattern
The taskQueue option (or setTaskQueue() in Java) routes subsequent Activities to the specific Worker that downloaded the file. This option is used when creating new Activity options after receiving the host-specific queue name from the initial Activity.
Worker setup for Worker-Specific Task Queues
Each Worker registers with both the default Task Queue and its own host-specific Task Queue. The default Worker handles Workflows and initial Activities. The host-specific Worker handles only Activities that require Worker affinity. Both Workers receive the same Activity implementation, but only the host-specific Worker receives Activities routed to its queue. The host-specific queue name should be unique, typically using hostname, IP, or UUID to ensure uniqueness.
When to use Worker-Specific Task Queues pattern
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).
Benefits of Worker-Specific Task Queues pattern
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.
Trade-offs of Worker-Specific Task Queues pattern
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.
Worker-Specific Task Queues pattern common pitfall: missing ScheduleToStartTimeout
Without ScheduleToStartTimeout on host-specific queues, if the target Worker is down, the Activity waits indefinitely. Always set ScheduleToStartTimeout so the Workflow can detect unavailability and retry on a different host.
Worker-Specific Task Queues pattern pitfall: not registering Worker on both queues
Each Worker must listen on both the default shared Task Queue (for Workflows and initial Activities) and its own host-specific queue. Forgetting the host-specific queue means routed Activities are never picked up.
Worker-Specific Task Queues pattern pitfall: assuming host-specific Worker is always available
The pinned Worker can crash or be restarted. Design the Workflow to retry the entire sequence on a different host when the ScheduleToStartTimeout expires.
Worker-Specific Task Queues pattern pitfall: leaking temporary files on failure
If the Workflow fails after downloading but before uploading, temporary files remain on disk. Use cleanup logic (defer, try-finally, or cancellation scopes) to remove local resources.
Worker-Specific Task Queues pattern pitfall: using when shared storage suffices
If all Workers can access the same storage (S3, NFS), Worker-specific routing adds unnecessary complexity and reduces availability. Use shared storage solutions instead when they are available.
Best practice: retry entire sequence in Worker-Specific Task Queues
Wrap the sequence in retry logic to restart on a different host if needed. This allows the Workflow to recover from Worker failures by retrying the entire activity sequence on another available Worker.
Best practice: set ScheduleToStartTimeout for host-specific queues
Always configure ScheduleToStartTimeout for host-specific queues to handle Worker failures. This timeout ensures the Workflow can detect when the pinned Worker is unavailable and retry on a different host.
Best practice: implement cleanup in Worker-Specific Task Queues
Use try-finally or cancellation scopes to clean up local resources. This ensures temporary files and other resources created during Activities are properly removed even if the Workflow fails mid-process.
Best practice: use unique queue names in Worker-Specific Task Queues
Use hostname, IP, or UUID to ensure unique Task Queue names. This prevents conflicts when multiple Workers run on different hosts and ensures each Worker has a distinctly identifiable queue.
Best practice: monitor queue depth for host-specific queues
Alert on growing host-specific queue backlogs. This helps identify when a pinned Worker is experiencing issues or is overwhelmed with work.
Best practice: drain gracefully in Worker-Specific Task Queues
Drain host-specific queues before stopping Workers. This ensures any in-flight Activities complete before the Worker shuts down.
Best practice: add health checks for Worker-Specific Task Queues
Verify Worker health before accepting work on host-specific queues. This helps ensure that Activities are only routed to Workers that are actually available and functioning properly.
Standalone Activity use cases
Standalone Activities can be used for durable job processing use cases such as sending an email, processing a webhook, syncing data, or executing a single function reliably with built-in retries and timeouts.
Three types of clients that talk to Temporal Server
Temporal Server receives requests from the command-line interface (CLI), the web-based user interface (Web UI), and Temporal Clients embedded into applications.
Temporal client capabilities
A Temporal Client is embedded into application code and provides APIs to communicate with a Temporal Service. Clients can start a Workflow Execution, signal a Workflow Execution to send asynchronous messages, query a Workflow Execution to retrieve current state, list Workflow Executions, get the result of a Workflow Execution, and manage Workflows by canceling or terminating Executions.
Temporal Server composition
The Temporal Server consists of a Frontend Service and multiple backend services (History, Matching, and Worker services), plus a required external database component. Optional components include Elasticsearch for advanced search visibility and Grafana for operational dashboards.
Frontend Service role
The Frontend Service is the unified entry point that Temporal Clients communicate with. It handles rate limiting, authorization, validation, and routes requests to appropriate backend services. Clients never communicate directly with backend services or Workers.
Matching Service responsibilities
The Matching Service manages coordination with other services and manages Task Queues or Task Queue partitions. It dispatches Tasks to their respective queues before Workers pick them up, coordinates Worker polling to determine how many Tasks should be sent, and finds appropriate Workers polling the queue to hand off Tasks when a Workflow Task or Activity needs to be done.
Worker Service function
The Worker Service handles all background functionality that keeps the Temporal Service running smoothly, including internal system Workflows like maintenance jobs, cleanup, replication, archival, and visibility indexing. This is distinct from application Workers and developers do not directly interact with this layer.
Developer responsibilities in Temporal
Developers are responsible for writing the Activity Definition, Workflow Definition, and code to configure and start Workers that coordinate with a Temporal Service to execute Workflow and Activity code. The Temporal Service is responsible for orchestrating execution.
Persistence layer database options
The persistence layer stores Workflow state, Event History, Task Queues, and metadata. Database options include Cassandra, MySQL, PostgreSQL, and SQLite. This persistence enables recovery and replay of Workflows.
Use Child Workflows to partition problems into smaller chunks
Because Child Workflow Executions have their own Event Histories, they are often used to partition large workloads into smaller chunks. For example, a single Workflow Execution does not have enough space in its Event History to spawn 100,000 Activity Executions. But a Parent Workflow Execution can spawn 1,000 Child Workflow Executions that each spawn 1,000 Activity Executions to achieve a total of 1,000,000 Activity Executions.
Child Workflow Execution definition
A Child Workflow Execution is a Workflow Execution that is spawned from within another Workflow in the same Namespace. A Workflow Execution can be both a Parent and a Child Workflow Execution because any Workflow can spawn another Workflow.
Parent must await spawning Child Workflow
A Parent Workflow Execution must await on the Child Workflow Execution to spawn. The Parent can optionally await on the result of the Child Workflow Execution.
Child Workflows do not carry over with Continue-As-New
Child Workflows do not carry over when the Parent uses Continue-As-New. If a Parent Workflow Execution uses Continue-As-New, any ongoing Child Workflow Executions will not be retained in the new continued instance of the Parent.
Parent Close Policy propagation to Children
When a Parent Workflow Execution reaches a Closed status, the Temporal Service propagates Cancellation Requests or Terminations to Child Workflow Executions depending on the Child's Parent Close Policy.
Child Workflow with Continue-As-New treated as single execution
If a Child Workflow Execution uses Continue-As-New, from the Parent Workflow Execution's perspective the entire chain of Runs is treated as a single execution.
Use Child Workflows for separate services
Because a Child Workflow Execution can be processed by a completely separate set of Workers than the Parent Workflow Execution, it can act as an entirely separate service. However, a Parent Workflow Execution and a Child Workflow Execution do not share any local state. As all Workflow Executions, they can communicate only via asynchronous Signals.
Parent Workflow Event History size limits for Child spawning
A single Parent should not spawn more than 1,000 Child Workflow Executions. A Parent Workflow Execution Event History contains Events that correspond to the status of the Child Workflow Execution, so there is a practical limit on how many can be spawned before the Parent's Event History becomes oversized.
Child Workflows for representing single resources
A Child Workflow Execution can create a one to one mapping with a resource. It can be used to manage the resource using its ID to guarantee uniqueness. For example, a Workflow that manages host upgrades could spawn a Child Workflow Execution per host with the hostname as a Workflow ID and use them to ensure that all operations on the host are serialized.
Child Workflows for periodic logic execution
A Child Workflow can be used to execute some periodic logic without overwhelming the Parent Workflow Event History. The Parent Workflow starts a Child Workflow which executes periodic logic calling Continue-As-New as many times as needed, then completes. From the Parent point of view, it is just a single Child Workflow invocation.
Child Workflow API access and deterministic constraints
A Child Workflow has access to all Workflow APIs but is subject to the same deterministic constraints as other Workflows. An Activity has the inverse—no access to Workflow APIs but no Workflow constraints.
Child Workflow Parent Close Policy ABANDON behavior
A Child Workflow Execution can continue on if its Parent is canceled with a Parent Close Policy of ABANDON. An Activity Execution is always canceled when its Workflow Execution is canceled.