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

workflows/basics

456 notes in this subject, read out of this brain and free to use. This is page 7 of 8.

Terminate a Workflow Execution in Rust

To terminate a Workflow Execution in Rust, use the terminate method on the Workflow handle with WorkflowTerminateOptions that can include a reason. Termination forcefully and immediately stops the Workflow Execution, similar to killing a process.

Workflow termination forceful stop characteristics

Terminating a Workflow forcefully stops Workflow Execution. The system records a WorkflowExecutionTerminated event in the Event History. The termination forcefully and immediately stops the Workflow Execution. The Workflow code gets no chance to handle termination, and a Workflow Task does not get scheduled.

When to use cancel versus terminate

In most cases, canceling a Workflow is preferable because it allows the Workflow to finish gracefully. Terminate only if the Workflow is stuck and cannot be canceled normally.

Terminate Workflow Execution code example in Rust

Example of terminating a Workflow Execution in Rust: ```rust let handle = client.start_workflow( GreetingsWorkflow::run, (), WorkflowStartOptions::new("my-task-queue", "greetings-workflow-10").build() ).await?; handle.terminate(WorkflowTerminateOptions::builder() .reason("Emergency shutdown") .build() ).await?; ``` This example starts a Workflow Execution and then terminates it with a reason.

Rust SDK workflow documentation sections

The Rust SDK documentation for workflows includes the following sections: Workflow basics, Child Workflows, Continue-As-New, Message passing, Cancellation, Timers, and Timeouts.

Workflow code must be deterministic

Workflow code must be deterministic, unlike Activity code. Non-deterministic changes to Workflow code can cause errors in production. Non-deterministic changes need to be protected by either Worker Versioning or patching APIs within Workflow code.

Using proxyActivities to call Activities from Workflow code

To properly call Activities from Workflow code, use proxyActivities and make sure to only import the Activity types. Example: import { proxyActivities } from '@temporalio/workflow'; import type * as activities from './activities'; const { makeHTTPRequest } = proxyActivities<typeof activities>(); export async function yourWorkflow(): Promise<string> { return await makeHTTPRequest('https://temporal.io'); }

Entity pattern isNew parameter

The entityWorkflow function accepts an isNew parameter (defaults to true) to determine whether to run the setup phase. Pass true for the first execution and false when continuing as new, ensuring setup logic runs only once.

Mock Activities when unit testing Workflows

When unit testing Workflows, mock the Activity invocation. When integration testing Workflows with a Worker, provide mock Activity implementations to the Worker. Only implement the relevant Activities for the Workflow being tested.

Example: Mocking Activities in Worker

import type * as activities from './activities'; const mockActivities: Partial<typeof activities> = { makeHTTPRequest: async () => '99', }; const worker = await Worker.create({ activities: mockActivities, }); This creates a Worker with mocked Activities that can be used for testing Workflows.

TypeScript SDK main topics and structure

The TypeScript SDK developer guide covers Workflows, Activities, Workers, Temporal Client, Temporal Nexus, Platform features, Best practices, and Integrations. Key sections include Workflow basics, Child Workflows, Continue-As-New, Message passing, Cancellation, Cancellation scopes, Timeouts, Schedules, Timers, Versioning, and Workflow Streams. Activity sections cover Activity basics, Activity execution, Timeouts, Asynchronous Activity, and Benign exceptions. Worker topics include Worker processes and Interceptors. Client topics include Temporal Client and Namespaces. Nexus topics include Quickstart, Feature guide, and Standalone Operations. Platform topics include Observability and Enriching the UI. Best practices topics include Testing, Debugging, Converters and encryption, and Entity pattern.

getCurrentDetails and setCurrentDetails in workflows

Within a workflow, you can import getCurrentDetails and setCurrentDetails from '@temporalio/workflow'. getCurrentDetails() retrieves the current workflow details, and setCurrentDetails() updates them. Unlike static summary/details set at workflow start, current details can be updated throughout the workflow lifecycle. The format is standard Markdown excluding images, HTML, and scripts and can span multiple lines.

Example: starting workflow with staticSummary and staticDetails

const handle = await client.workflow.start(yourWorkflow, { args: ['workflow input'], taskQueue: 'your-task-queue', workflowId: 'your-workflow-id', staticSummary: 'Order processing for customer #12345', staticDetails: 'Processing premium order with expedited shipping' });

Example: getting and setting current workflow details

import { getCurrentDetails, setCurrentDetails } from '@temporalio/workflow'; export async function yourWorkflow(input: string): Promise<string> { const currentDetails = getCurrentDetails(); console.log(`Current details: ${currentDetails}`); setCurrentDetails('Updated workflow details with new status'); return 'Workflow completed'; }

Hello World Workflow example with proxyActivities

Example Workflow implementation: import { proxyActivities } from '@temporalio/workflow'; import type * as activities from './activities'; const { greet } = proxyActivities<typeof activities>({ startToCloseTimeout: '1 minute', }); export async function example(name: string): Promise<string> { return await greet(name); } This demonstrates using proxyActivities to call an Activity from a Workflow with a startToCloseTimeout of 1 minute.

Workflow definition

Workflows orchestrate Activities and contain the application logic. Temporal Workflows are resilient and can run for years, even if the underlying infrastructure fails. If the application crashes, Temporal automatically recreates its pre-failure state so it can continue where it left off.

Workflow interceptor registration

Workflow interceptor registration differs from other interceptors because they run in the Workflow isolate. To register Workflow interceptors, export an interceptors function from a file located in the workflows directory and provide the name of that file to the Worker on creation via WorkerOptions.interceptors.

workflowInfo() in interceptor

The workflowInfo() function can be called from a Workflow interceptor to access Workflow-specific information, available at the time of interceptor construction when the Workflow context is already initialized.

Workflow interceptor registration example

Example of registering workflow interceptors: File `src/workflows/your-interceptors.ts`: ```ts import { workflowInfo } from '@temporalio/workflow'; export const interceptors = () => ({ outbound: [new ActivityLogInterceptor(workflowInfo().workflowType)], inbound: [], }); ``` File `src/worker/index.ts`: ```ts const worker = await Worker.create({ workflowsPath: require.resolve('./workflows'), interceptors: { workflowModules: [require.resolve('./workflows/your-interceptors')], }, }); ```

WorkflowClientInterceptor

WorkflowClientInterceptor intercepts workflow-related methods of Client and WorkflowHandle like starting or signaling a Workflow. This allows client-side interception of workflow operations.

WorkflowOutboundCallsInterceptor

WorkflowOutboundCallsInterceptor intercepts Workflow outbound calls to Temporal APIs like scheduling Activities and starting Timers. This allows workflows to modify or monitor calls made to Temporal APIs.

WorkflowInboundCallsInterceptor

WorkflowInboundCallsInterceptor intercepts Workflow inbound calls like execution, Signals, and Queries. This interceptor type is used to monitor or modify workflow-level inbound operations.

Example detecting replay for metrics

The following code shows how to guard metrics emission so it only runs on the first execution: ```ts import { workflowInfo } from '@temporalio/workflow'; if (!workflowInfo().unsafe.isReplaying) { metrics.emit('workflow_started', 1); } ```

Workflow return values must be serializable

Workflow return values must be serializable. The return type should be a Promise wrapping the return value type. Temporal APIs for retrieving Workflow results will receive either the result or the error, never both.

Workflow Type in TypeScript is the function name

In the TypeScript SDK, the Workflow Type (Workflow name) is determined by the Workflow function name. There is no mechanism to customize the Workflow Type to a different name.

Workflows run in deterministic sandboxed environment

In the Temporal TypeScript SDK, Workflows run in a deterministic sandboxed environment. The code is bundled on Worker creation using Webpack and can import any package as long as it does not reference Node.js or DOM APIs.

Use ignoreModules for libraries with Node.js/DOM API references

If a library references Node.js or DOM APIs but you are certain those APIs are not used at runtime, add that module to the ignoreModules list in BundleOptions when creating the Worker.

Workflow definition is an async function

In the Temporal TypeScript SDK, Workflow Definitions are implemented as async functions that can store state and orchestrate Activity Functions. The function takes parameters (recommended: a single object parameter) and returns a Promise.

Side effects must be done through Activities

Workflow code cannot directly access external state or perform side effects. All side effects and external state access must be done through Activities, because Activity outputs are recorded in the Event History and can be read deterministically by the Workflow.

Cannot directly import Activity Definitions in Workflows

Workflow code cannot directly import the Activity Definition. Activity Types can be imported to invoke them in a type-safe manner, but the actual Activity Definition implementation cannot be imported.

Deterministic replacements for Math and Date APIs

Functions like Math.random(), Date, and setTimeout() are replaced by deterministic versions in the Workflow sandbox. Math.random() produces the same sequence on replay. Date.now() and new Date() return the time of the last Workflow Task completion and only advance when awaiting something.

FinalizationRegistry and WeakRef are removed from sandbox

FinalizationRegistry and WeakRef are removed from the Workflow sandbox because v8's garbage collector is not deterministic.

Use workflow.log instead of console.log

Use the log function from @temporalio/workflow instead of console.log. The SDK logger automatically suppresses messages during replay to avoid duplicates.

Use uuid4() for deterministic UUID generation

For generating UUIDs in Workflows, use uuid4() from @temporalio/workflow. It draws from the sandbox's deterministic random source and requires no extra dependency. Avoid crypto.randomUUID() which is not available in the sandbox.

Date.now() advances only on await

Date.now() and new Date() in the Workflow sandbox return the time of the last Workflow Task completion. The value only advances when you await something like sleep(), so multiple consecutive calls without await return identical timestamps.

Use workflowInfo().unsafe.isReplaying to detect replay

Use workflowInfo().unsafe.isReplaying to guard code that should only run on the first execution, such as emitting metrics or sending external notifications from an Interceptor. Never use this to affect Workflow business logic, as branching on replay status breaks determinism.

Use isReplayingHistoryEvents for non-business-logic operations

Use workflowInfo().unsafe.isReplayingHistoryEvents to check for new events. This returns false during read-only operations like queries and update validators, and is what the SDK's built-in logger uses internally.

Workflow parameters must be serializable

All Workflow Definition parameters must be serializable. Use object parameters instead of multiple primitive parameters, so individual fields can be altered without breaking the Workflow signature.

Example Workflow with Activity invocation

The following code shows how to define a Workflow that invokes an Activity: ```typescript type ExampleArgs = { name: string; }; export async function example(args: ExampleArgs): Promise<{ greeting: string }> { const greeting = await greet(args.name); return { greeting }; } ```

Example Workflow with typed parameters

The following code shows how to define a Workflow with typed parameters: ```ts interface ExampleParam { name: string; born: number; } export async function example({ name, born }: ExampleParam): Promise<string> { return `Hello ${name}, you were born in ${born}.`; } ```

Example starting Workflow with parameters from client

The following code shows how to start a Workflow with parameters from the client: ```typescript import { example } from './workflows'; ... await client.workflow.start(example, { args: [{ name: 'Temporal', born: 2019 }], taskQueue: 'your-queue', workflowId: 'business-meaningful-id', }); ```

Example Workflow logging

The following code shows how to use deterministic logging in a Workflow: ```ts import { log } from '@temporalio/workflow'; export async function myWorkflow(name: string): Promise<string> { log.info('Starting workflow', { name }); // ... } ```

Example deterministic random and UUID generation

The following code shows how to use deterministic random numbers and UUID generation in a Workflow: ```ts import { uuid4 } from '@temporalio/workflow'; const value = Math.random(); const id = uuid4(); ```

Example Date.now() behavior with and without await

The following code demonstrates how Date.now() behaves differently with and without await: ```ts import { sleep } from '@temporalio/workflow'; // Prints the *exact* same timestamp on every iteration for (let x = 0; x < 10; ++x) { console.log(Date.now()); } // Prints timestamps increasing roughly 1s each iteration for (let x = 0; x < 10; ++x) { await sleep('1 second'); console.log(Date.now()); } ```

Operations affected by cancellation scopes

When a CancellationScope is cancelled, it propagates cancellation in any child scopes and of any cancelable operations created within it: Activities, Timers (created with the sleep function), and Triggers.

Cancellation scopes overview

In TypeScript SDK, workflows are represented internally by a tree of cancellation scopes, each with cancellation behaviors you can specify. By default, everything runs in the root scope. Scopes can be nested, and cancellation propagates from outer scopes to inner ones. A Workflow's main function runs in the outermost scope.

CancellationScope.cancellable static helper

CancellationScope.cancellable(fn) creates a scope where children are automatically cancelled when their containing scope is cancelled. It is equivalent to new CancellationScope().run(fn). It returns a native JavaScript promise.

CancellationScope.nonCancellable static helper

CancellationScope.nonCancellable(fn) creates a scope where cancellation does not propagate to children. It is equivalent to new CancellationScope({ cancellable: false }).run(fn). It returns a native JavaScript promise.

CancellationScope.withTimeout static helper

CancellationScope.withTimeout(timeoutMs, fn) creates a scope that is automatically cancelled after a timeout. If a timeout triggers before fn resolves, the scope is cancelled, triggering cancellation of any enclosed operations, such as Activities and Timers. It is equivalent to new CancellationScope({ cancellable: true, timeout: timeoutMs }).run(fn). It returns a native JavaScript promise.

CancellationScope API methods

CancellationScope provides the following methods: CancellationScope.current() gets the current scope; scope.cancel() cancels all operations inside a scope; scope.run(fn) runs an async function within a scope and returns the result of fn; scope.cancelRequested is a promise that resolves when a scope cancellation is requested, such as when Workflow code calls cancel() or the entire Workflow is cancelled by an external client.

CancelledFailure exception

Timers and Triggers throw CancelledFailure when cancelled. Activities and Child Workflows throw ActivityFailure and ChildWorkflowFailure with cause set to CancelledFailure. One exception is when an Activity or Child Workflow is scheduled in an already cancelled scope (or Workflow), in which case they propagate the CancelledFailure that was thrown to cancel the scope.

isCancellation helper function

Use the isCancellation(err) function to simplify checking for cancellation in TypeScript workflows.

Cancel a timer from workflow example

Example showing internal cancellation: ```ts import { CancelledFailure, CancellationScope, sleep } from '@temporalio/workflow'; export async function cancelTimer(): Promise<void> { // Timers and Activities are automatically cancelled when their containing scope is cancelled. try { await CancellationScope.cancellable(async () => { const promise = sleep(1); // <-- Will be cancelled because it is attached to this closure's scope CancellationScope.current().cancel(); await promise; // <-- Promise must be awaited in order for `cancellable` to throw }); } catch (e) { if (e instanceof CancelledFailure) { console.log('Timer cancelled 👍'); } else { throw e; // <-- Fail the workflow } } } ```

Cancel a timer alternative implementation

Example showing an alternative way to cancel a timer: ```ts import { CancelledFailure, CancellationScope, sleep } from '@temporalio/workflow'; export async function cancelTimerAltImpl(): Promise<void> { try { const scope = new CancellationScope(); const promise = scope.run(() => sleep(1)); scope.cancel(); // <-- Cancel the timer created in scope await promise; // <-- Throws CancelledFailure } catch (e) { if (e instanceof CancelledFailure) { console.log('Timer cancelled 👍'); } else { throw e; // <-- Fail the workflow } } } ```

Handle external workflow cancellation while activity running

Example showing how to handle Workflow cancellation by an external client while an Activity is running: ```ts import { CancellationScope, proxyActivities, isCancellation } from '@temporalio/workflow'; import type * as activities from '../activities'; const { httpPostJSON, cleanup } = proxyActivities<typeof activities>({ startToCloseTimeout: '10m', }); export async function handleExternalWorkflowCancellationWhileActivityRunning(url: string, data: any): Promise<void> { try { await httpPostJSON(url, data); } catch (err) { if (isCancellation(err)) { console.log('Workflow cancelled'); // Cleanup logic must be in a nonCancellable scope // If we'd run cleanup outside of a nonCancellable scope it would've been cancelled // before being started because the Workflow's root scope is cancelled. await CancellationScope.nonCancellable(() => cleanup(url)); } throw err; // <-- Fail the Workflow } } ```

nonCancellable scope shields children from cancellation

Example showing how CancellationScope.nonCancellable prevents cancellation from propagating to children: ```ts export async function nonCancellable(url: string): Promise<any> { // Prevent Activity from being cancelled and await completion. // Note that the Workflow is completely oblivious and impervious to cancellation in this example. return CancellationScope.nonCancellable(() => httpGetJSON(url)); } ```

withTimeout example for multiple activities

Example showing how to cancel multiple Activities if a deadline elapses: ```ts import { CancellationScope, proxyActivities } from '@temporalio/workflow'; import type * as activities from '../activities'; export function multipleActivitiesSingleTimeout(urls: string[], timeoutMs: number): Promise<any> { const { httpGetJSON } = proxyActivities<typeof activities>({ startToCloseTimeout: timeoutMs, }); // If timeout triggers before all activities complete // the Workflow will fail with a CancelledError. return CancellationScope.withTimeout(timeoutMs, () => Promise.all(urls.map((url) => httpGetJSON(url)))); } ```

scope.cancelRequested with nonCancellable scopes

Example showing how to await cancelRequested to make a Workflow aware of cancellation while waiting on nonCancellable scopes: ```ts import { CancellationScope, CancelledFailure, proxyActivities } from '@temporalio/workflow'; import type * as activities from '../activities'; const { httpGetJSON } = proxyActivities<typeof activities>({ startToCloseTimeout: '10m', }); export async function resumeAfterCancellation(url: string): Promise<any> { let result: any = undefined; const scope = new CancellationScope({ cancellable: false }); const promise = scope.run(() => httpGetJSON(url)); try { result = await Promise.race([scope.cancelRequested, promise]); } catch (err) { if (!(err instanceof CancelledFailure)) { throw err; } // Prevent Workflow from completing so Activity can complete result = await promise; } return result; } ```

Cancellation scopes with callbacks

Example showing how callbacks can handle cancellation by consuming the CancellationScope.cancelRequested promise: ```ts import { CancellationScope } from '@temporalio/workflow'; function doSomething(callback: () => any) { setTimeout(callback, 10); } export async function cancellationScopesWithCallbacks(): Promise<void> { await new Promise<void>((resolve, reject) => { doSomething(resolve); CancellationScope.current().cancelRequested.catch(reject); }); } ```

Nesting cancellation scopes

Example showing how to achieve complex flows by nesting cancellation scopes: ```ts import { CancellationScope, proxyActivities, isCancellation } from '@temporalio/workflow'; import type * as activities from '../activities'; const { setup, httpPostJSON, cleanup } = proxyActivities<typeof activities>({ startToCloseTimeout: '10m', }); export async function nestedCancellation(url: string): Promise<void> { await CancellationScope.cancellable(async () => { await CancellationScope.nonCancellable(() => setup()); try { await CancellationScope.withTimeout(1000, () => httpPostJSON(url, { some: 'data' })); } catch (err) { if (isCancellation(err)) { await CancellationScope.nonCancellable(() => cleanup(url)); } throw err; } }); } ```

Give your agent this brain