On this page

Typed state instead of infrastructure

DatabaseSchema is the complete server state declared as a runtime schema. Types for persisted records are inferred from their schemas; the memory store uses mutable versions internally. Persistence validates restored snapshots and reports malformed state as PersistenceError. No SQL server is required. All IDs and references are visible in TypeScript rather than hidden in protobuf blobs.

Tiny state Why it exists Real Temporal analogue
namespaces Namespace names namespaces table and namespace cache
currentRuns (namespace, workflowId) → runId current_executions
executions Mutable status, workflow type/args, history, pending Workflow Task executions plus separate history_tree/history_node
transferTasks Durable intent to send a task to Matching History transfer task queue
taskQueues Ordered delivery records keyed by physical queue identity Matching tasks
taskQueueMetadata Range ID, acknowledgement level, and last allocated task ID Matching task_queues; a subset of upstream queue metadata
userDataLegacy, userDataChasm Two teaching adapters for task-queue family config Current task-queue user-data table and a proposed CHASM component
timers Due timer commands History timer tasks
visibilityTasks Pending read-model updates Visibility task queue
visibility List/describe read model Visibility database/index
now Inspection timestamp when the snapshot was taken; restoring it does not reset the clock Snapshot metadata
sequence Durable counter behind History run and transfer IDs Shard-level task ID sequences
shards Each History shard's lease: range ID and owning host shards table (range_id, owner)

An Execution has namespace, workflowId, runId, and shardId separately. The shard (1–4) is a hash of namespace and workflow ID, so any client can compute it and route the request to the History host that owns it. A new run ID would identify a different execution of the same workflow ID. The history array is an append-only sequence of typed events; each event has an ID and timestamp.

The real PostgreSQL schema has primary keys but does not declare foreign keys between these resources. This toy uses dictionary keys for direct relationships and typed task records for cross-service links. The real system often stores those links inside protobuf bytes. See Temporal's PostgreSQL schema.

Persistence.snapshot captures committed records for inspection and memory-store test fixtures. PostgreSQL persists state automatically at each transaction: see the persistence walkthrough. JSON-compatible workflow inputs/results are required for database persistence. Worker code, loaded partition managers, waiting polls, user-data caches, and server membership are process state; execution history, queued tasks, queue ownership/acknowledgement metadata, timers, user-data rows, and visibility are stored state.

The mutable database never leaves persistenceLayer. It provides four store services — ExecutionStore (executions, current runs, transfer/timer/visibility tasks, owned by History), TaskStore (queue ownership/progress, backlog, and user data, owned by Matching — the store a Matching persistence migration would move), VisibilityStore, and NamespaceStore — plus Persistence, whose only job is the validated snapshot. ExecutionStore.transact stages History-owned tables, commits successful synchronous transitions together, and discards failed transitions. No fiber can observe a partial commit. Store reads return clones and writes copy their input, so holding a record never grants access to durable state; which service holds which store capability is the ownership boundary.

A QueueTask is Matching's record of History's task: kind, workflow ID, run ID, scheduled event ID, attempt for activities, and scheduling keys. The workflow ID is the routing key Matching needs to reach the owning History shard. Matching never stores activity names or inputs; History returns them when Matching records the start. Each QueueTask carries a History-owned versionDirective (auto or pinned). Matching uses it to choose storage and dispatch independently: auto tasks stay in default backlog and receive a deploymentVersion only at dispatch; pinned tasks use their version queue. Restore/migration fills directives on older records from their execution state.