new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Temporal · all subjects

design patterns

58 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Resumable Activity pitfall: not clearing correction state

After applying the correction, set corrected_account = None (or equivalent) before the next Activity attempt. Otherwise, if the corrected activity also fails, the Workflow immediately re-uses the previous correction instead of waiting for a new one.

Resumable Activity pitfall: waiting without timeout

If operators never send the correction Signal, the Workflow waits indefinitely. Add a durable timer if the process must resolve within a time bound.

Resumable Activity implementation steps

Use a bounded RetryPolicy to allow a few automatic retries for transient failures. Catch the exhausted ActivityError in the Workflow and transition to AWAITING_CORRECTION state. Block on workflow.wait_condition (or equivalent). Register a Signal handler that accepts corrected input and unblocks the condition. When the Signal arrives, re-execute the Activity with the corrected input. A second Signal gates final approval before completing.

Resumable Activity states and transitions

The Workflow transitions through these states: PENDING -> TRANSFERRING -> AWAITING_CORRECTION -> TRANSFERRING (after correction signal) -> AWAITING_APPROVAL -> COMPLETED or REJECTED. From AWAITING_CORRECTION, if 5 correction attempts are exceeded, the Workflow transitions to FAILED. The state can be queried at any time via a getStatus query method.

Resumable Activity pitfall: conflating with general retry loop

This pattern is for correcting input data. For retrying the same call against a temporarily unavailable system, use Fast/Slow Retries instead.

Signal with Start common pitfalls

Common pitfalls include: not implementing Signal idempotency (Signals can be delivered more than once due to client retries, causing duplicate processing); unbounded history growth where entity Workflows hit the 50K event or 10K Signal limit without calling Continue-As-New (use isContinueAsNewSuggested() to trigger Continue-As-New); losing pending Signals on Continue-As-New by not draining all pending Signals before calling it and passing unprocessed ones as input to the new execution; expecting a return value from Signals (they are fire-and-forget, use Updates or Update-with-Start instead); race between SignalWithStart and Continue-As-New (Temporal prevents this by rewinding the Workflow to process the Signal first if one arrives while completing).

Signal with Start best practices

Best practices include: derive Workflow ID from stable business identifiers (account ID, user ID); implement Signal idempotency by tracking processed operation IDs to prevent duplicates; use WorkflowInit to initialize state before Signals are delivered (Java, .NET, and Python's __init__); handle unbounded execution using Continue-As-New for long-running entity Workflows; choose ALLOW_DUPLICATE_FAILED_ONLY for entity Workflows; include a unique operation or reference ID in every Signal; check for duplicates at the start of Signal handlers to return early.

Signal with Start vs alternatives comparison

Signal with Start is for entity Workflows with fire-and-forget response type and signal-level idempotency. Update with Start is for request-response patterns with sync return value and update-level idempotency. REJECT_DUPLICATE is for one-time operations with async response type (Workflow ID) and workflow-level idempotency.

Workflow ID Duplicate policies behavior

ALLOW_DUPLICATE (default) allows a new Workflow Execution with the same ID after the previous one has closed (completed, failed, timed out, terminated, or cancelled), but does not affect a currently running Workflow — Signal with Start delivers the Signal to the running execution. ALLOW_DUPLICATE_FAILED_ONLY allows restart only if the previous run failed, preventing accidental restarts of running Workflows. REJECT_DUPLICATE prevents any duplicate starts, useful for one-time operations, not entity Workflows. TERMINATE_IF_RUNNING terminates the running Workflow and starts a new one.

Signal with Start benefits

Signal with Start provides an atomic operation where start and Signal happen with no race conditions. Workflows only exist when needed via lazy creation. The client does not need to check if the Workflow exists. The operation is safe to retry because duplicate starts are handled by the Workflow ID. The pattern is a natural fit for long-lived business entities.

TypeScript Signal with Start implementation

In TypeScript, use client.workflow.signalWithStart() with options object containing: workflowId (derived from entity), taskQueue (string), signal (Signal name string), and signalArgs (array of arguments). Define Signals using defineSignal<[arg types]>(name) and use setHandler() to attach handlers. In the Workflow, use condition(() -> false) to run forever. Track processed items in a Set for idempotency and store items in an array.

Java Signal with Start implementation

In Java, create a WorkflowOptions with setWorkflowId() and setTaskQueue(), then use workflowClient.newSignalWithStartRequest() to create a BatchRequest. Add the Workflow run method and Signal method to the request, then call workflowClient.signalWithStart(request). Define a @WorkflowInterface with @WorkflowMethod for run() and @SignalMethod for Signal handlers. Implement the interface tracking processed items in a HashSet for idempotency and items in an ArrayList, with run() calling Workflow.await(() -> false) to run forever.

Go Signal with Start implementation

In Go, use client.SignalWithStartWorkflow() with parameters: context, workflow ID as string, signal name as string, signal arguments, StartWorkflowOptions struct containing ID and TaskQueue, and the Workflow function. In the Workflow, use workflow.GetSignalChannel() to receive Signals, workflow.Go() to run a concurrent goroutine, and workflow.Await() with a lambda returning false to run forever. Track processed items in a map for idempotency and store items in a slice.

Python Signal with Start implementation

In Python, use client.start_workflow() with parameters: id for the Workflow ID (derived from entity), task_queue for the task queue name, start_signal for the Signal name as a string, and start_signal_args for Signal arguments as a list. In the Workflow, use @workflow.defn decorator, define a run() method that awaits workflow.wait_condition(lambda: False) to run forever, and use @workflow.signal decorator for Signal handlers. The example shows a shopping cart workflow that tracks processed item IDs in a set for idempotency and stores items in a list.

Signal with Start solves client complexity

Without Signal with Start, clients must check if the Workflow exists before Signaling, start the Workflow if it does not exist and then Signal it, handle race conditions when multiple clients try to start the same Workflow, and write complex coordination logic.

Signal with Start use cases

Signal with Start is a good fit for entity Workflows such as accounts, shopping carts, user sessions, and clusters; event-driven architectures like Kafka consumers and message queue processors; Workflows that receive multiple operations over their lifetime; lazy entity creation where Workflows only exist when needed; and fire-and-forget operations where immediate response is not needed.

Signal with Start unsuitable use cases

Signal with Start is not a good fit for one-time operations (use REJECT_DUPLICATE policy instead), request-response patterns requiring synchronous confirmation (use Update with Start), or operations that need immediate return values.

Signal with Start trade-offs

Signal with Start trade-offs are that Signals are fire-and-forget with no immediate confirmation that the Signal was processed. Signal idempotency must be tracked by the Workflow for processed operation IDs. Workflows must handle unbounded execution using Continue-As-New. Signals do not return values — use Queries or Updates for that.

Signal with Start pattern definition

Signal with Start is a pattern that atomically starts a Workflow if it is not running and delivers a Signal in a single operation. If the Workflow is already running, it receives the Signal without starting a new instance. The client does not need to check if the Workflow exists — Temporal handles it automatically.

When to use Priority Task Queues pattern

This pattern is a good fit when your system mixes time-sensitive operations (payment processing, user-facing requests) with background or batch work (reporting, data imports, inventory management), and you want urgent tasks to proceed even during periods of high load. It also works well when you need to mark urgent tasks that should override normal processing—for example, triggering immediate re-runs of failed critical tasks.

When not to use Priority Task Queues pattern

This pattern is not a good fit when all work is effectively equal in urgency, when a continuously replenished high-priority backlog could starve lower-priority work indefinitely, or when you need hard capacity isolation between tiers. For prioritizing work amongst tenants or customers, consider the Fairness pattern instead, which distributes capacity proportionally using weighted fairness keys.

Priority Task Queues benefits

Native priority requires no extra queues, routing logic, or additional Worker pools. A single pool of Workers serves all priority levels, so idle capacity at low-priority levels is automatically used by higher-priority work without any additional configuration.

Priority Task Queues trade-offs

Lower-priority tasks are blocked until all higher-priority tasks have started. In an environment with a continuously replenished high-priority backlog, low-priority tasks may be significantly delayed. The built-in PriorityKey range is 1–5; if more than five distinct levels are needed, the feature cannot accommodate them.

Saga pattern Temporal durability guarantee

Temporal's durable execution guarantees that compensations will execute even after Worker failures, ensuring cleanup happens reliably.

Saga pattern best practices for idempotency

Use idempotency keys for forward Activities by passing a unique identifier (such as a client ID or Workflow ID) to each Activity so retries do not create duplicate side effects. Pass references (IDs, URLs) instead of full data objects in compensation payloads to avoid exceeding the 2 MB payload limit.

Saga compensation timeouts best practice

Set `StartToCloseTimeout` on compensation Activities but avoid `ScheduleToCloseTimeout`. Do not set Workflow-level timeouts — let compensations retry until they succeed.

Saga compensation cancellation in Go

In Go, use `NewDisconnectedContext` to run compensation Activities after Workflow cancellation, since the original context is already cancelled.

Saga compensation error handling

If a compensation Activity fails, log the error and continue executing remaining compensations. In production, alert for manual intervention on persistent compensation failures. Always re-throw the original exception after running compensations so the Workflow reports the correct failure reason.

Saga pattern pitfall: non-idempotent compensations

Compensations may run even when the forward Activity never executed (if registered before execution) or may run multiple times on retry. All compensations must be idempotent, or the Saga may fail.

Saga pattern pitfall: forgotten compensation registration

If a step succeeds but its compensation was never registered, a later failure leaves that step's effects permanently in place.

Saga pattern pitfall: permanently failing compensations

If a compensation Activity fails with a non-retryable error, the Saga cannot fully roll back. Design compensations with generous retry policies.

Saga pattern pitfall: large compensation payloads

Passing large objects through the compensation chain can exceed the 2 MB payload limit. Use references (IDs, URLs) instead of full data.

Saga pattern pitfall: TypeScript ContinueAsNew exception

In TypeScript, `continueAsNew` works by throwing a special exception. A `catch` block that does not re-throw it, or a `finally` block that returns a value, silently prevents Continue-As-New. This must be avoided when using Saga compensation.

Saga pattern use cases

The Saga pattern is a good fit when you need to maintain consistency across multiple services or databases, traditional distributed transactions (two-phase commit) are too slow or unavailable, you can define compensating actions for each step in your business process, eventual consistency is acceptable for your use case, and you need to handle long-running transactions that may span hours or days.

Saga pattern when not to use

The Saga pattern is not a good fit for operations that require strong ACID consistency, single-service transactions that can use a local database transaction, processes where compensations cannot be defined, or operations that must appear atomic to external observers.

Saga pattern benefits and trade-offs

The Saga pattern maintains eventual consistency without distributed locks, each service can use its own database and transaction model, and Temporal's durable execution guarantees that compensations will execute even after Worker failures. The trade-offs are that only eventual consistency is provided with intermediate states visible to other processes, you must design idempotent compensation Activities, compensation logic must be maintained alongside forward logic, and some operations may not have meaningful compensations.

Saga pattern comparison with alternatives

Saga (orchestration) provides eventual consistency with compensating transactions and loose coupling at high scalability. Two-phase commit provides strong ACID consistency with distributed lock/rollback but has tight coupling and low scalability. Saga (choreography) provides eventual consistency with event-driven compensations and very loose coupling at high scalability. Local transaction provides strong ACID consistency with database rollback and no coupling but only works for single service.

Saga pattern overview

The Saga pattern manages distributed transactions across multiple services by coordinating a sequence of local transactions, each with a compensating action that can undo its effects if subsequent steps fail. If any step fails, compensation transactions execute in reverse order to undo the effects of all completed steps.

Saga pattern problem it solves

In distributed systems, you need to maintain data consistency across multiple services or databases without using traditional ACID transactions. When a multi-step business process fails partway through, you must undo the effects of completed steps. Traditional two-phase commit does not scale well and creates tight coupling between services.

Saga pattern solution approach

Implement each step as a local transaction with a corresponding compensation transaction. Register compensations as each step completes, then automatically trigger them when errors occur to ensure cleanup happens reliably. If a step fails, execute compensation transactions in reverse order to undo the effects of all completed steps.

Saga compensation registration timing options

There are two approaches for when to register compensation activities: (1) Register before Activity execution (recommended for safety) — ensures the compensation runs even if the Activity fails after partial completion, and the compensation must be idempotent and handle cases where the forward Activity never executed. (2) Register after Activity execution (appropriate when safe) — only compensates Activities that completed successfully and is appropriate when Activities are truly atomic (all-or-nothing). When in doubt, register compensations before execution and ensure they are idempotent.

Saga pattern Python implementation

In Python, maintain a list of compensation lambdas. Register each compensation before executing its corresponding activity. On error, iterate through compensations in reverse order using `reversed()` to execute them in LIFO order. All compensations must handle idempotency and cases where the forward Activity never executed.

Saga pattern Go implementation

In Go, use a slice of closures to track compensations. Register each compensation before executing its corresponding activity. On error, iterate from the end of the slice backwards to execute compensations in LIFO order. Alternatively, some samples use `defer` to register compensations, which also achieves LIFO order.

Saga pattern Java implementation with Saga API

In Java, use the SDK's `Saga` helper class to track compensations. Create a Saga instance with options (e.g., `setParallelCompensation(false)` for sequential execution). Register each compensation before executing its corresponding activity using `saga.addCompensation()`. On error, call `saga.compensate()` to run all registered compensations in reverse order.

Saga pattern TypeScript implementation

In TypeScript, maintain an array of Compensation functions. Register each compensation before executing its corresponding activity using `unshift()` to maintain LIFO order. On error, manually iterate through the compensations array and await each compensation function. The `unshift()` method ensures compensations are already in reverse order.

Retry Alerting best practice for long-running retries

For any long-running retry scenario, add Retry Alerting via Metrics to surface persistent failures before they breach an SLA.

When to use Fast/Slow Retries

Use Fast/Slow Retries when you want to recover from transient errors quickly but also wait indefinitely for the downstream system to come back.

When to use Fixed Wall-Time Retries

Use Fixed Wall-Time Retries when the process must resolve (one way or another) within a business SLA window such as 24 hours. Configure ScheduleToCloseTimeout for this purpose.

When to use Delayed Retry

Use Delayed Retry when the downstream system has a scheduled maintenance window and you know approximately how long it will be unavailable.

When to use Non-Retryable Errors versus Resumable Activity

If the error is structural — a missing record, invalid input, or authorization failure — and cannot be corrected automatically, ask whether a human can fix it. If yes, use Resumable Activity to park the Workflow and await a correction signal; otherwise use Non-Retryable Errors to fail fast.

When to use Fixed Count of Retries

Use Fixed Count of Retries when each attempt consumes a paid API call, a rate-limited token, or another scarce resource, to cap total consumption.

Resumable Activity pattern

The Resumable Activity pattern parks the Workflow after retries are exhausted and waits for a human to correct the data or approve continuing, then resumes from where it left off. Use this when the error is structural (missing record, invalid input, authorization failure) but a human can fix it.

Retry Alerting via Metrics pattern

The Retry Alerting via Metrics pattern 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.

Fast/Slow Retries pattern

The Fast/Slow Retries pattern 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.

Delayed Retry pattern

The Delayed Retry pattern overrides the next retry interval for a specific failure using nextRetryDelay on ApplicationFailure. Use this when an error carries information about how long to wait before retrying, such as when a downstream system has a scheduled maintenance window.

Non-Retryable Errors pattern

The Non-Retryable Errors pattern marks error types that will never succeed — such as validation failures or missing records — so Temporal fails fast instead of retrying. Use NonRetryableErrorTypes in RetryPolicy to specify these error types.

Fixed Wall-Time Retries pattern

The Fixed Wall-Time Retries pattern bounds the total elapsed time across all retry attempts to enforce a business SLA, regardless of how many attempts occur. Use ScheduleToCloseTimeout to implement this pattern.

Fixed Count of Retries pattern

The Fixed Count of Retries pattern caps the number of Activity retry attempts to control cost when each attempt consumes a paid or limited resource. Use MaximumAttempts in the RetryPolicy to implement this pattern.

Give your agent this brain