Workflows
A workflow is a class decorated with @registry.workflow(). The class has exactly one @workflow.run method that contains the workflow logic.
Defining a workflow
from cadence import Registry, workflow
registry = Registry()
@registry.workflow()
class OrderWorkflow:
@workflow.run
async def run(self, order_id: str) -> str:
# workflow logic
return "completed"
@registry.workflow() registers the class under its class name by default. Pass name= to use a different name:
@registry.workflow(name="order-workflow-v2")
class OrderWorkflow:
...
Signal handlers
Signal handlers receive signals sent while the workflow is running. They can be sync or async.
@registry.workflow()
class ApprovalWorkflow:
def __init__(self):
self._approved = False
@workflow.run
async def run(self) -> bool:
await workflow.wait_condition(lambda: self._approved)
return self._approved
@workflow.signal
def approve(self, approved: bool) -> None:
self._approved = approved
Use name= to override the signal name:
@workflow.signal(name="cancel-order")
async def cancel(self) -> None:
...
Query handlers
Query handlers answer read-only questions about workflow state. They must be synchronous and must not modify state.
@registry.workflow()
class StatusWorkflow:
def __init__(self):
self._status = "pending"
@workflow.run
async def run(self) -> None:
...
@workflow.query
def get_status(self) -> str:
return self._status
Workflow info
Inside a workflow, use workflow.WorkflowContext.get().info() to access the current workflow's metadata:
from cadence.workflow import WorkflowContext
ctx = WorkflowContext.get()
print(ctx.info().workflow_id)
print(ctx.info().workflow_run_id)
print(ctx.info().workflow_domain)
print(ctx.info().workflow_task_list)
Initialization
The __init__ method runs before run and is a good place to initialize instance state that signal handlers read and write.
from datetime import timedelta
@registry.workflow()
class CounterWorkflow:
def __init__(self):
self._count = 0
@workflow.run
async def run(self) -> int:
await workflow.sleep(timedelta(hours=1))
return self._count
@workflow.signal
def increment(self) -> None:
self._count += 1
Workflow constraints
Workflow code must be deterministic. The same sequence of inputs must always produce the same sequence of decisions. Specifically:
- Do not use
time.time()ordatetime.now()-- use timers viaworkflow.sleep()instead. - Do not use
random,uuid, or other non-deterministic sources. - Do not make I/O calls directly -- run them as activities.
- Do not use threading or
asyncio.create_task()-- the workflow event loop is controlled by the worker.