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 · Develop · all subjects

best-practices

226 notes in this subject, read out of this brain and free to use. This is page 3 of 4.

ServerError Temporal exception

ServerError is used for exceptions from the Temporal Service itself, like database failures.

ChildWorkflowError Temporal exception

ChildWorkflowError is raised when a Child Workflow Execution fails.

TimeoutError Temporal exception

TimeoutError occurs when an Activity or Workflow exceeds its configured timeout.

ActivityError Temporal exception

ActivityError wraps exceptions raised from Activities. The cause field contains the original error (ApplicationError, TimeoutError, CancelledError, etc.). Catch this in Workflows to handle Activity failures.

ApplicationError Temporal exception

ApplicationError is raised by your code to indicate application-specific failures. This is the only Temporal exception you should raise manually. When you raise an ApplicationError, you can optionally provide a type string and mark it as non_retryable.

TerminatedError Temporal exception

TerminatedError occurs when a Workflow Execution is forcefully terminated.

WorkflowAlreadyStartedError Temporal exception

WorkflowAlreadyStartedError is raised when attempting to start a Workflow with an ID that's already running.

Workflow Task failures vs Workflow Execution failures

Workflow Task failures are bugs that can be fixed with redeployment and retry automatically. Workflow Execution failures are business logic failures that should stop the Workflow and require explicitly raising an ApplicationError.

TemporalError base exception

All Temporal exceptions inherit from TemporalError. Do not extend TemporalError or its children. Use the provided exception types to ensure consistent behavior across process and language boundaries, compatibility with the Temporal Service, and proper serialization via Protocol Buffers.

Protecting sensitive information in failures

The default Failure Converter copies exception messages and stack traces as plain text visible in the Web UI. If your exceptions might contain sensitive information, configure a custom Failure Converter to encrypt this data.

CancelledError Temporal exception

CancelledError results from cancellation of a Workflow, Activity, or Timer. You can catch and ignore this to continue execution despite cancellation.

Transient, intermittent, and permanent failures

Transient failures like brief network hiccups resolve on their own and should be retried immediately. Intermittent failures like rate limiting need increasing delays between retries. Permanent failures like invalid input won't resolve through retries and need different data or code changes.

Python SDK installation and quickstart resources

Detailed installation instructions for the Python SDK are available in the Quickstart guide. After setting up the local Temporal Service, developers should start with Activity basics, Workflow basics, Activity execution, and Run Worker processes. The official Python API Documentation is available at https://python.temporal.io, and code samples are at https://github.com/temporalio/samples-python.

Python SDK community and support resources

The Temporal Python community provides support through the Temporal Python Community Slack, the Python SDK Forum at https://community.temporal.io/tag/python-sdk, a free Temporal 101 course in Python at https://learn.temporal.io/courses/temporal_101/python/, and the Python SDK GitHub repository at https://github.com/temporalio/sdk-python.

Python SDK main documentation sections

The Python SDK developer guide covers the following main topics: Workflows (including basics, child workflows, continue-as-new, cancellation, timeouts, message passing, schedules, timers, versioning, and workflow streams), Activities (basics, execution, standalone activities, timeouts, asynchronous completion, benign exceptions), Workers (worker processes), Temporal Client, Temporal Nexus, Platform features (observability and enriching UI), and Best practices (testing, sandbox, debugging, data handling, sync vs async).

GoogleAdkPlugin ties Temporal and Google ADK together

The GoogleAdkPlugin is the integration point between Temporal and Google ADK. It runs each model call and tool call as a Temporal Activity, configures the payload converter for ADK objects, and makes ADK's runtime deterministic for Workflow replay. The same plugin instance is added to both the Client and the Worker.

TemporalModel runs each model call as an invoke_model Activity

TemporalModel is a model class passed to an ADK Agent instead of a model name string. It runs each model call as an invoke_model Activity, making every turn durable and visible in the Event history. It accepts a model name string like 'gemini-2.5-flash' and optional ActivityConfig for controlling activity behavior.

ActivityConfig names model turns in Event history

When creating a TemporalModel, pass an ActivityConfig with a summary parameter to name each model turn as an Activity in the Event history. This makes it easy to see which agent ran and when in multi-agent workflows. The ActivityConfig can also set start_to_close_timeout for per-agent timeouts.

TemporalMcpToolSet runs MCP server operations as Activities

TemporalMcpToolSet exposes tools from an MCP (Model Context Protocol) server to an ADK Agent. It runs the server's list-tools and call-tool operations as Activities because connecting to an MCP server is external I/O. Register the toolset with the GoogleAdkPlugin through a TemporalMcpToolSetProvider.

Multi-agent workflows with TemporalModel and ADK handoff

Multiple ADK agents can work together in a single Workflow, each with its own TemporalModel. A coordinator agent can delegate to sub_agents using ADK's built-in transfer_to_agent handoff pattern. Each agent can have its own ActivityConfig to control timeouts and naming in the Event history.

TemporalModel supports streaming with streaming_topic parameter

TemporalModel can stream a model's output as it is generated by passing a streaming_topic parameter. The Workflow hosts a WorkflowStream to receive the streamed chunks, and the model call publishes each chunk as an LlmResponse to the topic. The ADK's RunConfig must use streaming_mode=StreamingMode.SSE to enable streaming.

Model provider credentials run on the Worker

Model calls run as Activities on the Worker, so the Worker process is the one that needs model provider credentials. For Gemini models, set the GOOGLE_API_KEY environment variable. The ADK supports other model providers through LiteLLM by changing the model name passed to TemporalModel.

Example: Multi-agent coordination with ActivityConfig

```python researcher = LlmAgent( name="researcher", model=TemporalModel( "gemini-2.5-flash", activity_config=ActivityConfig(summary="Researcher Agent"), ), instruction="You are a researcher. Find information about the topic.", ) writer = LlmAgent( name="writer", model=TemporalModel( "gemini-2.5-flash", activity_config=ActivityConfig(summary="Writer Agent"), ), instruction="You are a poet. Write a haiku based on the research.", ) coordinator = LlmAgent( name="coordinator", model=TemporalModel( "gemini-2.5-flash", activity_config=ActivityConfig( start_to_close_timeout=timedelta(seconds=30), summary="Coordinator Agent", ), ), instruction="You are a coordinator. Delegate to researcher then writer.", sub_agents=[researcher, writer], ) ``` This example shows multiple agents with ActivityConfig summaries for naming in Event history, and a coordinator using ADK's transfer_to_agent handoff.

Example: Using MCP tools with TemporalMcpToolSet

```python # In Worker plugin = GoogleAdkPlugin( toolset_providers=[TemporalMcpToolSetProvider("echo", echo_toolset)] ) # In Workflow agent = Agent( name="echo_agent", model=TemporalModel("gemini-2.5-flash"), instruction="Use the echo tool to echo back the user's message.", tools=[TemporalMcpToolSet("echo", not_in_workflow_toolset=echo_toolset)], ) ``` This example shows registering an MCP toolset with the plugin and using it in a Workflow. The not_in_workflow_toolset factory allows running the agent locally outside Temporal.

Example: Streaming model output with TemporalModel

```python @workflow.defn class StreamingAgentWorkflow: @workflow.init def __init__(self, prompt: str) -> None: self.stream = WorkflowStream() @workflow.run async def run(self, prompt: str) -> str: model = TemporalModel("gemini-2.5-flash", streaming_topic="responses") agent = Agent( name="streaming_agent", model=model, instruction="You are a helpful assistant.", ) runner = InMemoryRunner(agent=agent, app_name="streaming_app") session = await runner.session_service.create_session( app_name="streaming_app", user_id="user" ) final_text = "" async for event in runner.run_async( user_id="user", session_id=session.id, new_message=types.Content(role="user", parts=[types.Part(text=prompt)]), run_config=RunConfig(streaming_mode=StreamingMode.SSE), ): if event.content and event.content.parts: for part in event.content.parts: if part.text: final_text = part.text return final_text ``` This example shows streaming model output using streaming_topic and StreamingMode.SSE.

Install Google ADK integration with temporalio package

To use the Google ADK integration with Temporal Python SDK, install version 1.28.0 or later: `uv add "temporalio[google-adk]>=1.28.0"` or with pip: `pip install "temporalio[google-adk]>=1.28.0"`

Python SDK platform documentation sections

The Python SDK platform documentation covers two main areas: Observability and Enriching the UI. These sections explain how to implement platform features with the Python SDK.

Five categories of interceptors

There are five categories of inbound and outbound calls you can intercept: Outbound Client (wraps calls from application to Temporal Client to start Workflow or send Messages), Inbound Workflow (wraps calls arriving into Workflow Execution such as executing Workflow, handling Messages), Outbound Workflow (wraps calls Workflow makes to SDK such as scheduling Activities, starting Child Workflows, invoking Nexus Operations), Inbound Activity (wraps calls arriving into Activity Execution), and Outbound Activity (wraps calls Activity makes to SDK such as sending Heartbeats and reading Activity info).

Register interceptor on Client

Pass interceptors in the interceptors argument of Client.connect() to register them on the Client. Client interceptors modify outbound calls such as starting and signaling Workflows. The interceptors list can contain multiple interceptors that form a chain.

Client interceptor implementation pattern

To modify outbound Client calls, define a class inheriting from client.Interceptor and implement the method intercept_client() to return an instance of OutboundInterceptor that implements the subset of outbound Client calls to modify. It is common to create an interceptor class that inherits from both client.Interceptor and worker.Interceptor since their method sets do not overlap.

Context propagation via headers in Client interceptor example

This example implements an Interceptor on outbound Client calls that sets a certain key in the outbound headers field. A User ID is context-propagated by being sent in a header field with outbound requests. The ContextPropagationInterceptor class inherits from both temporalio.client.Interceptor and temporalio.worker.Interceptor, and the _ContextPropagationClientOutboundInterceptor overrides start_workflow() to set headers from context before calling the next interceptor.

Client.connect() with interceptors example code

client = await Client.connect( "localhost:7233", interceptors=[TracingInterceptor()], )

Example: Setting workflow retry policy in Python

import asyncio from datetime import timedelta from temporalio.client import Client from your_workflows import YourWorkflow from temporalio.common import RetryPolicy async def main(): client = await Client.connect("localhost:7233") handle = await client.execute_workflow( YourWorkflow.run, "your retry policy argument", id="your-workflow-id", task_queue="your-task-queue", retry_policy=RetryPolicy(maximum_interval=timedelta(seconds=2)), ) print(f"Handle: {handle}") if __name__ == "__main__": asyncio.run(main()) This example shows how to set a RetryPolicy with a maximum_interval parameter when executing a workflow.

Workflow retry policy in Python

Use a Retry Policy to retry a Workflow Execution in the event of a failure. Set the Retry Policy to either the start_workflow() or execute_workflow() asynchronous methods. Workflow Executions do not retry by default, and Retry Policies should be used with Workflow Executions only in certain situations.

Ruby SDK best practices documentation structure

The Ruby SDK best practices documentation covers four main areas: error handling, testing, debugging, and converters with encryption. Each area has dedicated documentation pages that provide guidance on implementing these practices with the Ruby SDK.

Ruby SDK best practices topics

Best practices for the Ruby SDK are organized into the following topics: error handling, testing suite, debugging, and data handling with converters and encryption.

Saga Pattern for handling Activity Failures in Workflows

You can implement a Saga Pattern in Workflows using rescue blocks to unwind steps that your Workflow has performed up to the point of Activity Failure. This allows you to perform compensating transactions when an Activity fails, rather than immediately failing the Workflow.

Ruby SDK resources and community

The Ruby SDK technical resources include the Ruby SDK Quickstart Setup Guide, Ruby SDK Code Samples on GitHub (github.com/temporalio/samples-ruby), Ruby API Documentation (ruby.temporal.io), Ruby SDK GitHub repository (github.com/temporalio/sdk-ruby), and Temporal 101 free course in Ruby. The Temporal Ruby community can be reached via the Ruby Community Slack channel and Ruby SDK Forum on community.temporal.io.

Large Event History impacts Workflow performance

A very large Event History can adversely affect the performance of a Workflow Execution. For example, if a Workflow Worker fails, the full Event History must be pulled from the Temporal Service and given to another Worker via a Workflow Task. If the Event History is very large, it may take significant time to load it.

Best practice: use single struct for Activity parameters

It is recommended to use a single struct as an argument that wraps all the application data passed to Activities. This way you can change what data is passed to the Activity without breaking the function signature.

Rust SDK official resources and API documentation

Official Rust SDK resources include the Rust SDK Quickstart and Setup Guide at /develop/rust/quickstart, the Rust API Documentation at https://docs.rs/temporalio-sdk/latest/temporalio_sdk/, and the Rust SDK GitHub repository at https://github.com/temporalio/sdk-rust/tree/main/crates/sdk.

RetryPolicy fields in Rust

The Rust RetryPolicy struct has the following fields: initial_interval (Option, the initial backoff interval), backoff_coefficient (f64, the multiplier for backoff between retries), maximum_interval (Option, the maximum backoff interval between retries), maximum_attempts (i32, the maximum number of retry attempts), and non_retryable_error_types (Vec<String>, a list of error types that should not trigger a retry).

Workflow retry policies in Rust

Workflow Executions do not retry by default. A Retry Policy can be used alongside timeouts to control how Workflow Executions are retried after failure. Retry Policies should only be applied when restarting the entire Workflow is safe and intentional. Retry Policies define retry behavior such as backoff intervals, maximum attempts, and retry conditions.

Rust RetryPolicy configuration example

This example shows how to configure a Retry Policy when starting a Workflow in Rust: let wf_handle = client.start_workflow( GreetingsWorkflow::run, (), WorkflowStartOptions::new( "my-task-queue", "greetings-workflow-10", ) .retry_policy(RetryPolicy { initial_interval: Some(prost_dur!(from_secs(1))), backoff_coefficient: 2.0, maximum_interval: Some(prost_dur!(from_secs(100))), maximum_attempts: 5, non_retryable_error_types: vec!["NonRetryableError".to_string()], }).build() ).await?;

Load client connection config for local and cloud - TypeScript

Use `loadClientConnectConfig()` from '@temporalio/envconfig' to configure the Temporal Client connection. It responds to environment variables and TOML configuration files, allowing the same code to work against a local dev server and Temporal Cloud without changes.

Connect Standalone Activity client to Temporal Cloud with mTLS

Set these environment variables with values from your Temporal Cloud Namespace settings to connect with mTLS: TEMPORAL_ADDRESS=<your-namespace>.<your-account-id>.tmprl.cloud:7233 TEMPORAL_NAMESPACE=<your-namespace>.<your-account-id> TEMPORAL_TLS_CLIENT_CERT_PATH='path/to/your/client.pem' TEMPORAL_TLS_CLIENT_KEY_PATH='path/to/your/client.key'

Connect Standalone Activity client to Temporal Cloud with API key

Set these environment variables with values from your Temporal Cloud API key settings to connect with API key authentication: TEMPORAL_ADDRESS=<your-namespace>.<your-account-id>.tmprl.cloud:7233 TEMPORAL_NAMESPACE=<your-namespace>.<your-account-id> TEMPORAL_API_KEY=<your-api-key>

Encryption implementations may involve key management

A complete implementation of Payload Encryption may involve selecting appropriate encryption algorithms, managing encryption keys, restricting a subset of users from viewing payload output, or a combination of these.

Server never adds encryption to Payloads

The Temporal Server itself never adds encryption over Payloads. Unless client-side encryption is implemented, Payload data will be persisted in non-encrypted form to the data store, and any Client that can make requests to a Temporal namespace (including the Temporal UI and CLI) will be able to read Payloads contained in Workflows.

Always implement Payload encryption for sensitive data

When working with sensitive data, you should always implement Payload encryption using a custom Payload Codec provided to the Client.

Temporal security model uses client-side encryption

Temporal's security model is designed around client-side encryption of Payloads. A client encrypts Payloads before sending them to the server and decrypts them after receiving them from the server. This provides confidentiality because the Temporal Server has no knowledge of the actual data.

Entity pattern best practice in TypeScript SDK

The Entity pattern is documented as a best practice for the TypeScript SDK.

TypeScript SDK best practices topics

The TypeScript SDK best practices documentation covers four main areas: Testing, Debugging, Converters and encryption (data handling), and Entity pattern.

Single-entity design pattern structure

The single-entity design pattern is used to manage workflow iterations and handle signals efficiently. It tracks the number of iterations regardless of frequency and calls continueAsNew while properly handling pending updates from signals. The pattern uses a pendingUpdates array to buffer updates received via signal handlers before processing them in iterations.

Entity pattern iteration timeout with condition

Use the condition() method with a timeout (such as '1 day') to wait for pending updates without blocking the workflow execution forever. This ensures the workflow will eventually continue-as-new even if no updates are received, preventing indefinite waiting.

Entity pattern continue-as-new call

After processing all iterations, call continueAsNew<typeof entityWorkflow>(input, false) to restart the workflow with the same input but with isNew set to false, avoiding redundant setup execution in subsequent iterations.

Entity pattern cancellation handling

When handling cancellation errors in the entity pattern, use CancellationScope.nonCancellable() to wrap cleanup logic. This ensures cleanup operations complete even when the workflow receives a cancellation request.

Single-entity pattern TypeScript example

The following TypeScript example implements the single-entity design pattern with signal handling, iteration tracking, and continue-as-new logic: ```ts interface Input { /* Define your Workflow input type here */ } interface Update { /* Define your Workflow update type here */ } const MAX_ITERATIONS = 1; export async function entityWorkflow( input: Input, isNew = true, ): Promise<void> { try { const pendingUpdates = Array<Update>(); setHandler(updateSignal, (updateCommand) => { pendingUpdates.push(updateCommand); }); if (isNew) { await setup(input); } for (let iteration = 1; iteration <= MAX_ITERATIONS; ++iteration) { // Ensure that we don't block the Workflow Execution forever waiting // for updates, which means that it will eventually Continue-As-New // even if it does not receive updates. await condition(() => pendingUpdates.length > 0, '1 day'); while (pendingUpdates.length) { const update = pendingUpdates.shift(); await runAnActivityOrChildWorkflow(update); } } } catch (err) { if (isCancellation(err)) { await CancellationScope.nonCancellable(async () => { await cleanup(); }); } throw err; } await continueAsNew<typeof entityWorkflow>(input, false); } ``` This pattern shows how to track iterations, handle pending updates from signals, implement timeouts to prevent indefinite waiting, and properly manage workflow continuation.

Namespaces isolate Workflow Executions

Namespaces are used to isolate Workflow Executions according to your needs. For example, you can use separate dev and prod Namespaces to match the development lifecycle, or separate Namespaces for different teams like teamA and teamB to ensure they never communicate or impact each other.

Custom Authorizer restricts Namespace operations

A custom Authorizer can be used on the Frontend Service in the Temporal Service to set restrictions on who can create, update, or deprecate Namespaces.

Give your agent this brain