Temporal Rust SDK

repository·main·Indexed 17 days ago

https://github.com/temporalio/sdk-rust

A high-performance implementation for building Temporal workflows and clients in Rust. It includes the temporalio-client for interacting with Temporal services, a Core SDK that serves as a foundation for other language SDKs, and support for the Temporal Cloud Operations API. The SDK provides capabilities for managing workflows via signals, queries, and updates, as well as implementing patterns such as activity heartbeating, inbound interceptors, child workflows, and continue-as-new.

Tokens
80K
Snippets
212
Records
323
Agent score
67%

What's inside temporalio-sdk-rust

  1. Overview of Temporal Rust SDK and Client

    main

    The repository contains several key components for interacting with Temporal using Rust:

    • Temporal Rust SDK (temporalio-sdk): A high-level Rust SDK built on top of Core. It is currently in Public Preview. Use this for building Temporal applications in Rust.
    • Temporal Rust Client (temporalio-client): A client implementation for interacting with the Temporal gRPC service. It is also in Public Preview.
    • Temporal Core SDK (temporalio-sdk-core): The foundational implementation used as a base for other language SDKs (TypeScript, Python, .NET, and Ruby).
  2. Available Temporal Rust SDK Examples

    main

    The repository contains several example directories demonstrating specific Temporal patterns. Each directory is self-contained with its own README, workflow definitions, worker, and starter.

    Pattern Reference

    • Hello World: Basic workflow that calls a single activity.
    • Activity Heartbeating: Long-running activity with heartbeating and resume-on-retry.
    • Activity Inbound Interceptor: Wrapping inbound activity execution and inspecting typed inputs/outputs.
    • Timer Examples: Workflow timers, racing timers against activities, and timer cancellation.
    • Message Passing: Signals, queries, and updates on a workflow.
    • Child Workflows: Starting and collecting results from child workflows.
    • Continue-As-New: Long-running workflows via continue-as-new.
    • Saga: Saga/compensation pattern for distributed transactions.
    • Patching: Workflow versioning with ctx.patched().
    • Local Activities: Local vs remote activity execution.
    • Search Attributes: Reading and upserting workflow search attributes.
    • Updatable Timer: Timer that can be rescheduled via signals.
    • Polling: Polling an external condition with activities and timers.
    • Cancellation: Workflow and activity cancellation with cleanup.
    • Encryption: Custom PayloadCodec for payload encryption.
    • Schedules: Creating and managing scheduled workflows.
    • WASM Workflows: Component workflow used by WASM workflow integration tests.
  3. Glossary of Temporal SDK communication terms

    main

    When working with the Temporal SDK, several terms describe the flow of data between the Server, Core SDK, and Language SDK. Understanding these helps disambiguate overloaded terminology:

    • HistoryEvent (or Event): Events from the server representing the workflow history (defined in Temporal service protobufs).
    • Command: Commands returned by workers upon completing a WorkflowTask (e.g., starting a timer or an activity).
    • WorkflowTask: How the server represents the need to run user workflow code and the results of that execution.
    • WorkflowActivation: Produced by the Core SDK when the language SDK needs to "activate" user code (either starting from the beginning or resuming from cache).
    • WorkflowActivationJob (or Job): Included in WorkflowActivations; represents specific actions since the last activation (e.g., a timer firing or an activity result).
    • WorkflowActivationCompletion: Provided by the language side to complete an activation, containing WorkflowCommands (like query responses).
  4. How Nexus works for cross-namespace operations

    main

    Nexus allows you to define and invoke functionality across different namespaces by calling Nexus Operations.

    Core Components:

    • Nexus Operation: An arbitrary piece of functionality (like starting a workflow or updating state) that inherits Temporal's durable execution guarantees.
    • Nexus Service: An interface that abstracts the target Namespace and Task Queues. It defines the available operations.
    • Nexus Endpoint: A reverse proxy that maps incoming Nexus Operations to specific Nexus Services. These are registered in a Nexus Registry.

    Instead of interacting with a service directly, a caller uses a Nexus Client to call an Endpoint, which then routes the operation to the appropriate service.

    # Example of a Nexus Service definition
    @nexusrpc.service
    class GreetingService:
        say_hello: nexusrpc.Operation[GreetInput, str]
    
    # Example of a Workflow calling a Nexus operation
    @workflow.defn
    class GreetingCaller:
        @workflow.run
        async def run(self, name: str) -> str:
            client = workflow.create_nexus_client(
                service=GreetingService, endpoint="my-nexus-endpoint",
            )
            return await client.execute_operation(
                GreetingService.say_hello, GreetInput(name=name),
            )
  5. Understand Non-Sticky Task Queues

    main
    In a standard (non-sticky) task queue configuration, multiple workers poll a single shared task queue. When a worker picks up a task (either a workflow task or an activity task), the Temporal server delivers the entire history of that workflow to the worker. The worker must then recreate the workflow state by replaying the history from the beginning before it can proceed with the task. This model is simple but requires shipping the full history for every task execution.
  6. Implement the Continue-As-New pattern for long-running workflows

    main

    The continue-as-new pattern is used for workflows that must run for an extended period. To prevent the workflow history from growing unbounded, the workflow periodically re-creates itself with its current state.

    In this pattern, a workflow performs a unit of work (e.g., incrementing a counter) and, upon reaching a certain threshold or condition, calls continue-as-new to start a new execution of itself using the updated state, effectively resetting the history while preserving the logical progress.

  7. How Replay and Determinism work in Temporal

    main

    Temporal achieves durability and statelessness through Replay.

    Replay Mechanism

    When a worker does not have a workflow in its local cache (or needs to recover), it performs a "replay": it starts the Workflow code from the very beginning and feeds it the existing Event History serially. The SDK uses internal state machines for every command type (e.g., a Timer state machine) to reconstruct the exact state the workflow had previously reached.

    The Determinism Requirement

    A Workflow must always emit the same commands in the same sequence. Temporal enforces this by checking that the commands produced by the current execution match the "dual" events already present in the history.

    Nondeterminism Errors (NDE)

    If the code is modified such that it produces commands in a different order or produces different commands than what is recorded in the history, the SDK will throw a NondeterminismError.

    Example of Nondeterminism: If a workflow uses asyncio.gather(activity_future, timer_future) and the order of arguments is swapped to asyncio.gather(timer_future, activity_future), the replay will fail. The Timer state machine will encounter an ActivityTaskScheduled event when it was expecting a TimerStarted event, triggering an error because the sequence of commands no longer matches the history.

  8. How the Temporal Core-based SDK architecture works

    main

    The Temporal SDK is split into two distinct layers to ensure high performance and language flexibility:

    1. Core SDK (sdk-core): Written in Rust. It handles the heavy lifting, including gRPC communication with the Temporal service, polling for tasks (Workflow, Activity, and Nexus), processing those tasks, and managing internal state machines.
    2. Language SDK (sdk-lang): The package specific to the language you are using (e.g., Rust, Python, TypeScript). It communicates with the Core SDK via C bindings. The language layer is responsible for periodically polling the Core SDK for tasks, executing user-defined workflow/activity/nexus functions, and returning results back to the Core.

    For a Rust developer, this means you typically pull in both temporalio-sdk-core and temporalio-sdk crates.

    // Example of the two-layer dependency for a Rust user
    // You would pull in both the core and the language-specific SDK
    // temporalio-sdk-core
    // temporalio-sdk
  9. Understand SDK and Server Interaction via Event History

    main

    Temporal workflows progress through a cycle of Workflow Tasks. A Workflow Task is the fundamental unit of progress, processed by a Worker (running the SDK).

    The Workflow Lifecycle Cycle:

    1. Polling: The SDK polls the Temporal Server for a PollWorkflowTaskQueueRequest.
    2. Task Generation: The Server generates a Workflow Task containing the current Event History and sends it via PollWorkflowTaskQueueResponse.
    3. Processing: The SDK applies the history to its internal state, unblocks user code (e.g., resolving awaitables), and runs the Workflow logic.
    4. Commands: As the code runs, it issues Commands (e.g., start a timer, run an Activity). These are not events themselves, but instructions for the server.
    5. Response: The SDK responds with RespondWorkflowTaskCompletedRequest containing the buffered commands.
    6. Event Conversion: The Server converts commands into Events (the "duals"). For example, a StartTimer command becomes a TimerStarted event. These events are persisted in the history.
    7. Triggering: When an event occurs that requires code execution (like ActivityTaskCompleted or TimerFired), the Server generates a new Workflow Task, and the cycle repeats.

    This cycle continues until the Workflow produces a CompleteWorkflowExecution command, resulting in a WorkflowExecutionCompleted event.

  10. Workflow Determinism Constraints

    main

    Workflow code must be deterministic. Violating these rules will cause workflow failures during replay.

    Prohibited operations:

    • Direct I/O (use Activities instead).
    • Threading or random number generation.
    • Accessing system time (use ctx.workflow_time() instead).
    • Global mutable state.
    • Using tokio or futures concurrency primitives directly (e.g., tokio::select!, tokio::spawn, futures::select!).

    Required deterministic wrappers:

    • select! — deterministic select (polls in declaration order).
    • join! — deterministic join for a fixed number of futures.
    • join_all — deterministic join for a dynamic collection of futures.
  11. Runtime Nondeterminism Detection

    main

    The SDK includes a runtime detector that monitors async wake sources inside workflow code. It is enabled by default.

    How it works: The SDK tracks if async wake-ups originate from SDK-provided primitives (timers, activities, etc.) or external sources. If a non-SDK wake is detected, the workflow task fails.

    Common triggers to avoid:

    • tokio::time::sleep / tokio::time::interval (use ctx.timer())
    • tokio::net / tokio::fs / async IO (use Activities)
    • tokio::spawn (do not spawn tasks from workflows)
    • std::thread::spawn with async channels
    • tokio::sync channels (use ctx.state_mut() + ctx.wait_condition())

    Disabling detection: You can disable this via WorkerOptions::detect_nondeterministic_futures(false).