Skip to content

Durable Workflows

After each task completes, Sayiir saves a checkpoint (snapshot) of the workflow state. If the process crashes or restarts, you can call resume to continue execution from the last checkpoint. There is no replay—tasks that already completed are never re-executed.

This checkpointing mechanism ensures that long-running workflows can survive process crashes, deployments, or infrastructure failures without losing progress.

Sayiir supports two backend types:

  • InMemoryBackend: Fast, ephemeral storage for development and testing. State is lost when the process stops.
  • PostgresBackend: Durable, persistent storage for production. State survives process restarts.
from sayiir import InMemoryBackend, PostgresBackend
# Development/testing
backend = InMemoryBackend()
# Production
backend = PostgresBackend(
host="localhost",
port=5432,
user="postgres",
password="password",
database="sayiir"
)

Sayiir provides full control over workflow execution with pause, unpause, cancel, and resume operations. These operations are all durable—the workflow state is persisted and can be resumed even after a process restart.

from sayiir import cancel_workflow
# Cancel a running or paused workflow
cancel_workflow("job-123", backend=backend)
from sayiir import pause_workflow, unpause_workflow
# Pause a running workflow
pause_workflow("job-123", backend=backend)
# Unpause to allow resumption
unpause_workflow("job-123", backend=backend)
from sayiir import resume_workflow
# Resume from last checkpoint
status = resume_workflow(workflow, "job-123", backend=backend)
print(f"Workflow status: {status}")

Delays are checkpointed just like task completions. If a workflow is waiting on a delay when the process crashes, the delay will be resumed from the correct point after recovery.

from datetime import timedelta
from sayiir import task, Flow, run_durable_workflow
@task
def send_email(user_id: int) -> str:
return f"Email sent to user {user_id}"
@task
def send_reminder(user_id: int) -> str:
return f"Reminder sent to user {user_id}"
# Wait 24 hours between email and reminder
workflow = (
Flow("email_campaign")
.then(send_email)
.delay(timedelta(hours=24))
.then(send_reminder)
.build()
)
status = run_durable_workflow(workflow, "campaign-1", 42, backend=backend)

If the process crashes during the 24-hour delay, calling resume will continue the delay from where it left off, not restart it from zero.