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/message-passing

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

ServiceClientException when sending Signal in PHP

When sending a Signal, if the client cannot contact the server, a ServiceClientException is raised. Configure RPC Retry Policy to handle this scenario.

WorkflowNotFoundException when sending Signal or Query in PHP

When sending a Signal or Query, if the Workflow does not exist, a WorkflowNotFoundException is raised.

TimeoutException for RPC timeout in message passing PHP

When sending a Signal, Update, or Query, if an RPC timeout occurs, a TimeoutException is raised. Configure RPC timeout to impose a maximum wait time.

WorkflowUpdateException in PHP

A WorkflowUpdateException is raised when an Update fails. This can happen if: (1) The Update was rejected by an Update validator, (2) The Update failed after being accepted due to Activity or Child Workflow failure, or (3) The Workflow finished while the Update handler was in progress.

WorkflowUpdateRPCTimeoutOrCanceledException in PHP

When sending an Update and no Workflow Workers are polling the Task Queue, the SDK Client retries indefinitely by default. If a configured RPC timeout is reached, a WorkflowUpdateRPCTimeoutOrCanceledException is raised.

Dynamic Query in PHP

A Dynamic Query is invoked dynamically at runtime if no statically defined Query with the same name exists. Use Workflow::registerDynamicQuery(function (string $name, ValuesInterface $arguments): string { return sprintf('Got query `%s` with %d arguments', $name, $arguments->count()); }) to register a dynamic Query handler. The handler receives the query name and arguments.

SignalMethod attribute in PHP

The #[SignalMethod] attribute indicates a method that reacts to external signals in a Workflow. It must have a void return type.

QueryMethod attribute in PHP

The #[QueryMethod] attribute indicates a method that reacts to synchronous query requests in a Workflow. It must have a non-void return type.

Multiple methods with same attribute in PHP workflows

You can have more than one method with the same attribute in a Workflow interface, except for #[WorkflowMethod]. For example, a Workflow can have multiple #[SignalMethod] and #[QueryMethod] methods.

Signal handling with Workflow::await in PHP

Workflows listen for Signals by the Signal's name. Use the #[SignalMethod] attribute to handle Signals. A Signal handler can update workflow state, and the main Workflow method can wait for state changes using Workflow::await(fn()=> $this->value) to block execution until a condition becomes true.

Workflow message handlers: Query, Signal, Update decorators

Message handlers are defined as methods on the Workflow class using one of three decorators: @workflow.query, @workflow.signal, or @workflow.update. The parameters and return values of handlers and the main Workflow function must be serializable. Data classes are preferred over multiple input parameters to allow adding fields without changing the calling signature.

Signal handler: asynchronous state mutation

A Signal is an asynchronous message sent to a running Workflow Execution to change its state and control its flow. Signal handlers use @workflow.signal decorator and should not return a value. The response is sent immediately from the server without waiting for the Workflow to process the Signal. Signal handlers can be async def to use Activities, Child Workflows, durable asyncio.sleep Timers, and workflow.wait_condition conditions. A WorkflowExecutionSignaled event appears in the Workflow's Event History.

Send Update: execute_update and start_update methods

To send an Update to a Workflow Execution, use WorkflowHandle.execute_update to wait for the Update to complete and fetch the result. Example: previous_language = await workflow_handle.execute_update(GreetingWorkflow.set_language, Language.Chinese). Alternatively, use WorkflowHandle.start_update to receive an UpdateHandle as soon as the Update is accepted, then use update_handle.result() to fetch results later. WorkflowExecutionUpdateAccepted is added to Event History when the Worker confirms the Update passed validation; WorkflowExecutionUpdateCompleted is added when the Update finishes.

Non-type-safe message sending: dynamic method invocation

When you don't have access to Workflow Definition or it isn't written in Python, use non-type-safe APIs by passing method names as strings instead of method objects. Pass method names to: Client.start_workflow, WorkflowHandle.query, WorkflowHandle.signal, WorkflowHandle.execute_update, WorkflowHandle.start_update. Use non-type-safe APIs: Client.get_workflow_handle and workflow.get_external_workflow_handle.

Async handlers: execute Activities and Child Workflows

Signal and Update handlers can be async def to use await with Activities, Child Workflows, asyncio.sleep Timers, and workflow.wait_condition conditions. This allows handlers to perform longer-running operations. Handler executions and the main Workflow method run concurrently with switching at await calls. Use asyncio.Lock to prevent concurrent handler execution issues when multiple handler instances run simultaneously.

Workflow.wait_condition: pause handler execution until condition met

Use workflow.wait_condition to prevent code from proceeding until a condition is true. Pass a function that returns True or False, optionally set a timeout. Common use cases: wait for a Signal or Update to arrive, wait in a handler until it's appropriate to continue, wait in main Workflow until all active handlers have finished. Example: await workflow.wait_condition(lambda: self.approved_for_release).

@workflow.init: process Workflow input before any handler executes

Use the @workflow.init decorator on your __init__ method to give it the same Workflow parameters as your @workflow.run method. The SDK ensures __init__ receives the Workflow input arguments sent by the Client. This is useful for message handlers that need access to workflow input. The __init__ and @workflow.run method must have the same parameters with the same type annotations.

Use asyncio.Lock to prevent concurrent handler execution issues

Concurrent handler execution can interact in unpredictable ways causing data inconsistency. Use asyncio.Lock to ensure only one handler instance executes a specific section of code at any given time. Example: Create self.lock = asyncio.Lock() in __init__, then use async with self.lock: around the critical section in async handlers.

Update failure: validation rejected, after-acceptance failures

Updates can fail in two ways: (1) Rejected by an Update validator defined in the Workflow. (2) Failed after being accepted. Update failures are like Workflow failures. Issues that cause Workflow failure in the main method also cause Update failures in the Update handler, such as failed Child Workflow, failed Activity with finite retries, ApplicationError raised by author, or errors in workflow_failure_exception_types list. You receive WorkflowUpdateFailedError exception.

Update handler Workflow Task failure: FAILED_PRECONDITION or durable after acceptance

When an Update handler causes a Workflow Task to fail (which causes the server to retry Workflow Tasks indefinitely), the outcome depends on acceptance stage: If not yet accepted, you receive FAILED_PRECONDITION RPCError exception. If already accepted, the Update is durable. Once the Workflow is healthy again after code deploy, use an UpdateHandle to fetch the Update result.

Update handler Workflow finished during execution: NOT_FOUND error

If the Workflow finished while the Update handler execution was in progress, you receive an RPCError exception with status NOT_FOUND. This can happen if the Workflow was canceled or failed, or if the Workflow completed normally or continued-as-new and the author did not wait for handlers to be finished.

Query handler no Worker polling Task Queue: FAILED_PRECONDITION error

When sending a Query and there is no Workflow Worker polling the Task Queue, you receive an RPCError exception with status FAILED_PRECONDITION.

Query handler failure: WorkflowQueryFailedError exception

If something goes wrong during a Query execution, you receive a WorkflowQueryFailedError exception. Any exception in a Query handler will trigger this error. This differs from Signal and Update requests, where exceptions can lead to Workflow Task Failure instead.

Dynamic Signal, Query, Update handler: fallback for unregistered names

A dynamic Signal, Query, or Update is a special stand-in handler used when an unregistered handler request arrives. Add dynamic=True to the handler decorator (e.g., @workflow.signal(dynamic=True)). The handler signature must accept (self, name: str, args: Sequence[RawValue]). Use a payload_converter function to convert RawValue objects to required types. After failing to find a handler with matching name and type, the Worker checks for a registered dynamic handler.

Dynamic Workflow: fallback for unknown Workflow types

A dynamic Workflow is a special stand-in Workflow Definition used when an unknown Workflow Execution request arrives. Add dynamic=True to @workflow.defn decorator. The primary Workflow method must accept a single argument of type Sequence[temporalio.common.RawValue]. Use a payload_converter function to convert RawValue objects to required types. The Worker looks for a dynamic Workflow after failing to find a Workflow Definition with matching type.

Dynamic Activity: fallback for unknown Activity types

A dynamic Activity is a stand-in implementation used when an Activity Task with unknown Activity type is received by the Worker. Add dynamic=True to @activity.defn decorator. The Activity Definition must accept a single argument of type Sequence[temporalio.common.RawValue]. Use a payload_converter function to convert RawValue objects to required types. Register the Activity with the Worker before it can be invoked. The Worker resolves unregistered Activities using the registered dynamic Activity.

Dynamic components: use judiciously as fallback mechanism

Use dynamic elements as a fallback mechanism, not a primary design. They can introduce long-term maintainability and debugging issues. Reserve dynamic invocation use for cases where a name is not or can't be known at compile time.

Query handler example: return list of languages

Example of a Query handler that returns a list of supported languages: @dataclass class GetLanguagesInput: include_unsupported: bool @workflow.defn class GreetingWorkflow: def __init__(self) -> None: self.greetings = { Language.CHINESE: "你好,世界", Language.ENGLISH: "Hello, world", } @workflow.query def get_languages(self, input: GetLanguagesInput) -> list[Language]: if input.include_unsupported: return list(Language) else: return list(self.greetings)

Signal handler example: approve workflow for release

Example of a Signal handler that mutates Workflow state: @dataclass class ApproveInput: name: str @workflow.defn class GreetingWorkflow: @workflow.signal def approve(self, input: ApproveInput) -> None: self.approved_for_release = True self.approver_name = input.name

Update handler with validator example: set language with validation

Example of an Update handler with validator: @workflow.defn class GreetingWorkflow: @workflow.update def set_language(self, language: Language) -> Language: previous_language, self.language = self.language, language return previous_language @set_language.validator def validate_language(self, language: Language) -> None: if language not in self.greetings: raise ValueError(f"{language.name} is not supported")

Async update handler example: execute Activity in update

Example of an async Update handler that executes an Activity: @activity.defn async def call_greeting_service(to_language: Language) -> Optional[str]: await asyncio.sleep(0.2) greetings = { Language.Arabic: "مرحبا بالعالم", Language.Chinese: "你好,世界", Language.English: "Hello, world", } return greetings.get(to_language) @workflow.defn class GreetingWorkflow: def __init__(self) -> None: self.lock = asyncio.Lock() @workflow.update async def set_language(self, language: Language) -> Language: if language not in self.greetings: async with self.lock: greeting = await workflow.execute_activity( call_greeting_service, language, start_to_close_timeout=timedelta(seconds=10), ) if greeting is None: raise ApplicationError( f"Greeting service does not support {language.name}" ) self.greetings[language] = greeting previous_language, self.language = self.language, language return previous_language

Wait for Signal to arrive example

Example using workflow.wait_condition to wait for a Signal: @workflow.defn class GreetingWorkflow: def __init__(self) -> None: self.approved_for_release = False self.approver_name: Optional[str] = None @workflow.signal def approve(self, input: ApproveInput) -> None: self.approved_for_release = True self.approver_name = input.name @workflow.run async def run(self) -> str: await workflow.wait_condition(lambda: self.approved_for_release) return self.greetings[self.language]

Use asyncio.Lock to prevent concurrent handler issues example

Example of using asyncio.Lock to prevent concurrent handler execution problems: @workflow.defn class MyWorkflow: def __init__(self) -> None: self.lock = asyncio.Lock() @workflow.signal async def safe_async_handler(self): async with self.lock: data = await workflow.execute_activity( fetch_data, start_to_close_timeout=timedelta(seconds=10) ) self.x = data.x await asyncio.sleep(1) self.y = data.y

Dynamic Signal handler example with RawValue conversion

Example of a dynamic Signal handler: from typing import Sequence from temporalio.common import RawValue @workflow.signal(dynamic=True) async def dynamic_signal(self, name: str, args: Sequence[RawValue]) -> None: # Convert RawValue objects to required types using payload_converter

Dynamic Activity example with RawValue conversion

Example of a dynamic Activity: from dataclasses import dataclass from datetime import timedelta from typing import Sequence from temporalio import activity, workflow from temporalio.common import RawValue @dataclass class YourDataClass: greeting: str name: str @activity.defn(dynamic=True) async def dynamic_greeting(args: Sequence[RawValue]) -> str: arg1 = activity.payload_converter().from_payload(args[0].payload, YourDataClass) return ( f"{arg1.greeting}, {arg1.name}!\nActivity Type: {activity.info().activity_type}" ) @workflow.defn class GreetingWorkflow: @workflow.run async def run(self, name: str) -> str: return await workflow.execute_activity( "unregistered_activity", YourDataClass("Hello", name), start_to_close_timeout=timedelta(seconds=10), )

Update-With-Start example with lazy initialization

Example of Update-With-Start with lazy initialization: start_op = WithStartWorkflowOperation( ShoppingCartWorkflow.run, id=cart_id, id_conflict_policy=common.WorkflowIDConflictPolicy.USE_EXISTING, task_queue="my-task-queue", ) try: price = Decimal( await temporal_client.execute_update_with_start_workflow( ShoppingCartWorkflow.add_item, ShoppingCartItem(sku=item_id, quantity=quantity), start_workflow_operation=start_op, ) ) except WorkflowUpdateFailedError: price = None workflow_handle = await start_op.workflow_handle()

@workflow.init example: process workflow input before handlers

Example of using @workflow.init to process Workflow input before any handler executes: @dataclass class MyWorkflowInput: name: str @workflow.defn class WorkflowRunSeesWorkflowInitWorkflow: @workflow.init def __init__(self, workflow_input: MyWorkflowInput) -> None: self.name_with_title = f"Sir {workflow_input.name}" self.title_has_been_checked = False @workflow.run async def get_greeting(self, workflow_input: MyWorkflowInput) -> str: await workflow.wait_condition(lambda: self.title_has_been_checked) return f"Hello, {self.name_with_title}" @workflow.update async def check_title_validity(self) -> bool: is_valid = await workflow.execute_activity( check_title_validity, self.name_with_title, schedule_to_close_timeout=timedelta(seconds=10), ) self.title_has_been_checked = True return is_valid

Start workflow and get handle: start_workflow and get_workflow_handle_for

To send Queries, Signals, or Updates, you call methods on a WorkflowHandle object. Use Client.start_workflow to start a Workflow and return its handle. Use Client.get_workflow_handle_for to retrieve a typed Workflow handle by its Workflow Id. Example: workflow_handle = await client.start_workflow(GreetingWorkflow.run, id='greeting-workflow-1234', task_queue='my-task-queue').

External Signal events in Event History

When an External Signal is sent from one Workflow to another, a SignalExternalWorkflowExecutionInitiated event appears in the sender's Event History and a WorkflowExecutionSignaled event appears in the recipient's Event History.

Update handler in Ruby workflow

An Update is a trackable synchronous request sent to a running Workflow Execution. It can change the Workflow state, control its flow, and return a result. Define it using the `workflow_update` class method decorator followed by a method that can mutate workflow state and return a value. The sender must wait until the Worker accepts or rejects the Update, and may wait further to receive a returned value or exception. Update handlers can be asynchronous and blocking, allowing use of Activities, Child Workflows, durable Timers, and wait conditions.

Query handler in Ruby workflow

A Query is a synchronous operation that retrieves state from a Workflow Execution. Define it as a method on the Workflow class using the `workflow_query` class method decorator. A Query handler must not modify Workflow state. Query handlers cannot perform async blocking operations such as executing an Activity. You can use `workflow_query_attr_reader` to expose a simple attribute as a read-only query. Example: `workflow_query` decorator followed by a method that returns state.

Signal handler in Ruby workflow

A Signal is an asynchronous message sent to a running Workflow Execution to change its state and control its flow. Define it as a method on the Workflow class using the `workflow_signal` class method decorator. A signal handler mutates workflow state but cannot return a value. The response is sent immediately from the server without waiting for the Signal to be delivered to the Workflow Execution. Signal handlers can be asynchronous and blocking, allowing use of Activities, Child Workflows, durable Timers, wait conditions, and more.

Update validator in Ruby workflow

An Update validator is defined using the `workflow_update_validator` class method decorator invoked before defining the method, with the name of the Update handler method as the first parameter. The validator must accept the same argument types as the handler and should not return a value. To reject an Update, raise an exception of any type in the validator. Validators are always optional. When a validator raises an error, the Update is rejected, not run, and `WorkflowExecutionUpdateAccepted` is not added to the Event History. The caller receives an 'Update failed' error.

Send Query from Ruby client

Call a Query method using `WorkflowHandle#query`. For example: `supported_languages = handle.query(MessagePassingSimple::GreetingWorkflow.languages, { include_unsupported: false })`. Sending a Query does not add events to a Workflow's Event History. You can send Queries to closed Workflow Executions within a Namespace's Workflow retention period, including Workflows that have completed, failed, or timed out. Querying terminated Workflows is not supported. A Worker must be online and polling the Task Queue to process a Query.

Send Signal from Ruby client

Use `WorkflowHandle#signal` from Client code to send a Signal. For example: `handle.signal(MessagePassingSimple::GreetingWorkflow.approve, { name: 'John Q. Approver' })`. The call returns when the server accepts the Signal; it does not wait for the Signal to be delivered to the Workflow Execution. The `WorkflowExecutionSignaled` Event appears in the Workflow's Event History. You can only send Signals to Workflow Executions that haven't closed.

Send external Signal from Ruby workflow

A Workflow can send a Signal to another Workflow using an External Signal. Use `Temporalio::Workflow.external_workflow_handle` passing a running Workflow Id to retrieve a Workflow handle, then call `signal` on it. Example: `handle = Temporalio::Workflow.external_workflow_handle('workflow-a-id')` followed by `handle.signal(WorkflowA.some_signal, 'some signal arg')`. When an External Signal is sent, a `SignalExternalWorkflowExecutionInitiated` Event appears in the sender's Event History and a `WorkflowExecutionSignaled` Event appears in the recipient's Event History.

Signal-With-Start in Ruby

Signal-With-Start allows a Client to send a Signal to a Workflow Execution, starting the Execution if it is not already running. Call `signal_with_start_workflow` with a `WithStartWorkflowOperation`. If there is a Workflow running with the given Workflow Id, it will be signaled. If there isn't, a new Workflow will be started and immediately signaled. Example: Create a `Temporalio::Client::WithStartWorkflowOperation` with the Workflow, input, id, and task_queue, then pass it to `client.signal_with_start_workflow` with the signal and signal input.

Send Update from Ruby client

An Update is a synchronous, blocking call that can change Workflow state, control its flow, and return a result. Use `execute_update` to call the Update method from the Workflow handle and wait for the Update to complete, returning the result. Alternatively, use `start_update` to receive a `WorkflowUpdateHandle` as soon as the Update is accepted without waiting for asynchronous operations to complete. With `start_update`, use `update_handle.result` later to fetch the results. `WorkflowExecutionUpdateAccepted` is added to Event History when the Worker confirms validation. `WorkflowExecutionUpdateCompleted` is added when the Worker confirms completion.

Update-With-Start in Ruby

Update-With-Start lets you send an Update that checks whether an already-running Workflow with that ID exists. If the Workflow exists, the Update is processed. If it does not exist, a new Workflow Execution is started with the given ID, and the Update is processed before the main Workflow method starts. Use `execute_update_with_start_workflow` to start the Update and wait for the result in one go. Alternatively, use `start_update_with_start_workflow` to start the Update and receive a `WorkflowUpdateHandle`. You must provide a `WithStartWorkflowOperation` with an `id_conflict_policy` specified. Requires Temporal Server version 1.26 or later.

Async handlers in Ruby workflows

Signal and Update handlers can be asynchronous and blocking, allowing use of Activities, Child Workflows, Durable Timers, wait conditions, and more. Handler executions and the main Workflow method run concurrently with switching occurring at await calls. It is essential to understand potential concurrency issues when using async handlers. See Workflow message passing documentation for guidance on safe usage.

Wait condition in Ruby workflow handlers

Use `Temporalio::Workflow.wait_condition` to set a function that prevents code from proceeding until the condition is truthy. This is useful for: waiting in a handler until it is appropriate to continue, and waiting in the main Workflow until all active handlers have finished. The condition state can be updated by and reflect any part of the Workflow code including the main method, other handlers, or child coroutines. Example: `Temporalio::Workflow.wait_condition { ready_for_update_to_execute(my_update_input) }`.

Use workflow_init for early Workflow input access

The `workflow_init` class method above `initialize` gives the constructor access to Workflow input. When you use `workflow_init` on your constructor, it receives the same Workflow parameters as your `execute` method. The SDK ensures the constructor receives the Workflow input arguments that the Client sent. The Workflow input arguments are also passed to your `execute` method regardless of whether you use `workflow_init`. The constructor and `execute` must have the same parameters with the same types.

Use Mutex to prevent concurrent handler execution

Concurrent processes can interact in unpredictable ways. Multiple handler instances running simultaneously may cause data corruption if not properly synchronized. Use Ruby's `Mutex` to ensure only one handler instance can execute a specific section of code at any given time. Example: `@mutex ||= Mutex.new` followed by `@mutex.synchronize do` around the critical section. For more advanced concurrency control, `wait_condition` can be used with integer attributes as a semaphore.

Dynamic Query in Ruby workflow

A Dynamic Query is invoked at runtime if no other Query with the same name is registered. Set `dynamic: true` on the `workflow_query` class method. Only one Dynamic Query can be present on a Workflow. The Query handler parameters must accept a string name as the first parameter. Often `raw_args: true` is set with the second parameter as `*args` which will be an array of `Temporalio::Converters::RawValue`. Use `Temporalio::Workflow.payload_converter.from_payload` to convert raw value instances to proper types.

Dynamic Signal in Ruby workflow

A Dynamic Signal is invoked at runtime if no other Signal with the same input is registered. Set `dynamic: true` on the `workflow_signal` class method. Only one Dynamic Signal can be present on a Workflow. The Signal handler parameters must accept a string name as the first parameter. Often `raw_args: true` is set with the second parameter as `*args` which will be an array of `Temporalio::Converters::RawValue`. Use `Temporalio::Workflow.payload_converter.from_payload` to convert raw value instances to proper types.

Dynamic Update in Ruby workflow

A Dynamic Update is invoked at runtime if no other Update with the same input is registered. Set `dynamic: true` on the `workflow_update` class method. Only one Dynamic Update can be present on a Workflow. The Update handler parameters must accept a string name as the first parameter. Often `raw_args: true` is set with the second parameter as `*args` which will be an array of `Temporalio::Converters::RawValue`. Use `Temporalio::Workflow.payload_converter.from_payload` to convert raw value instances to proper types.

Message handler parameter guidelines

Parameters and return values of handlers and the main Workflow function must be serializable. Prefer single hash/object input parameter to multiple input parameters. Hash/object parameters allow you to add fields without changing the calling signature.

Update failed error in Ruby

You receive a `WorkflowUpdateFailedError` exception when an Update fails. This can happen in two ways: the Update was rejected by an Update validator, or the Update failed after being accepted. Update failures are like Workflow failures. Issues causing Workflow failure in the main method also cause Update failures in the Update handler, including: failed Child Workflow, failed Activity with finite retries, Workflow author raising `ApplicationError`, or any error listed in `workflow_failure_exception_types` on the Worker or `workflow_failure_exception_type` on the Workflow.

Query failed error in Ruby

You receive a `WorkflowQueryFailedError` exception if something goes wrong during a Query. Any exception in a Query handler will trigger this error. This differs from Signal and Update requests, where exceptions can lead to Workflow Task Failure instead.

No Workers polling Task Queue error for Queries

When there is no Workflow Worker polling the Task Queue, you receive a `Temporalio::Error::RPCError` exception whose `code` is a `FAILED_PRECONDITION` constant defined in `Code`.

Give your agent this brain