temporalio/temporal
Architecture
Active contributors: wxing1292, alex, yichaoyang
This page describes how the four Temporal services fit together, what data they exchange, and how a single workflow execution moves through the system. It is the wiki's starting point for engineers who need to reason about the server end to end.
The four services
graph LR
subgraph user[User-hosted]
App[App / SDK Client]
Worker[Worker process]
end
subgraph cluster[Temporal Cluster]
FE[Frontend]
H[History<br/>sharded]
M[Matching<br/>sharded by task queue]
W[Internal Worker]
end
subgraph store[Storage]
DB[(Persistence)]
Vis[(Visibility)]
end
App -->|StartWorkflow / Signal /<br/>Update / Query| FE
Worker -->|PollWorkflowTask /<br/>PollActivityTask| FE
Worker -->|RespondWorkflowTaskCompleted /<br/>RespondActivityTaskCompleted| FE
FE -->|StartWorkflowExecution<br/>(by workflow id)| H
FE -->|PollWorkflowTaskQueue| M
M <-->|AddWorkflowTask /<br/>AddActivityTask| H
H --> DB
H --> Vis
M --> DB
W -->|internal SDK<br/>via Frontend| FEEach service is wired up by a top-level fx module in temporal/fx.go and started from cmd/server/main.go. Service membership and host-shard ownership are tracked via a Ringpop gossip ring; see common/membership/.
| Service | Sharded by | Stateful? | Default port |
|---|---|---|---|
| Frontend | none (stateless) | no | 7233 / 7243 |
| History | shard ID | yes | 7234 |
| Matching | task-queue name | partial | 7235 |
| Worker | none | no | 7239 |
Stateful here means "owns leases on durable shards"; see Membership.
Workflow lifecycle in one diagram
A normal workflow that runs an activity and a timer:
sequenceDiagram
participant App
participant Worker
participant FE as Frontend
participant H as History (shard)
participant M as Matching
participant DB as Persistence
App->>FE: StartWorkflowExecution
FE->>H: StartWorkflowExecution
H->>DB: append history events,<br/>create transfer task
H-->>FE: ok
FE-->>App: ok
Note over H: Transfer-queue executor<br/>processes the task
H->>M: AddWorkflowTask
Worker->>FE: PollWorkflowTaskQueue
FE->>M: PollWorkflowTaskQueue
M-->>Worker: WorkflowTask
Worker->>FE: RespondWorkflowTaskCompleted<br/>(commands: ScheduleActivityTask, StartTimer)
FE->>H: RespondWorkflowTaskCompleted
H->>DB: append events,<br/>create transfer + timer tasks
H->>M: AddActivityTask (transfer task)
Worker->>FE: PollActivityTaskQueue → ActivityTask
Worker->>FE: RespondActivityTaskCompleted
FE->>H: RespondActivityTaskCompleted
H->>DB: append eventsTwo queue families inside History drive the asynchronous half:
- Transfer queue — handles the transactional-outbox pattern between History and Matching. Its tasks dispatch Workflow / Activity tasks via gRPC to Matching, update visibility, etc.
- Timer queue — handles deferred work (workflow sleeps, schedule-to-start timeouts, retry backoffs).
Both are ordered queues inside the History shard, materialised in the persistence backend's executions table family, processed by goroutines started in service/history/queue_factory_base.go and the executors in service/history/transfer_queue_active_task_executor.go and service/history/timer_queue_active_task_executor.go.
Shards and ownership
The unit of horizontal scaling is the History shard. The number of shards is fixed at cluster creation. Every workflow ID is hashed to a shard, and a shard is owned by exactly one History host at a time; ownership transfers via a generation number called RangeID (a RangeID mismatch fences out stale writes).
flowchart LR
NS[NamespaceID + WorkflowID] -->|hash mod NumShards| Shard
Shard -->|Ringpop ring| HistoryHost
HistoryHost -->|owns lease| PersistenceMatching shards are simpler: each (namespace, task-queue, partition) triple is owned by one Matching host at a time, again via Ringpop.
See common/membership/ and service/history/shard/ for the implementation.
State sources for a workflow execution
For every workflow execution, three kinds of state are persisted, all atomically updated through History's persistence interface:
| State kind | Where it lives | What it contains |
|---|---|---|
| Workflow History | history_node / history_tree tables — see schema/cassandra/temporal/schema.cql |
Append-only event log; what the workflow has done. |
| Mutable State | executions row, loaded into MutableStateImpl |
A summarised snapshot used to make decisions without replaying all events. |
| History Tasks | Per-shard internal task queues (transfer, timer, visibility, archival, replication, outbound) | The "what to do next" inbox for the shard's queue processors. |
Mutable State could in principle be reconstructed by replaying History Events, but the cost would be prohibitive — the summary is itself persisted and lives in an LRU cache workflow.Cache. Consistency between History Events, Mutable State, and tasks is provided by writing them in the same persistence transaction; see Consistency guarantees in History service.
Cross-cutting frameworks
A few internal frameworks deserve their own pages because they cross service boundaries:
- CHASM (
chasm/) — generalised, registry-based state-machine runtime. Workflow, Scheduler, and NexusOperation are CHASM Libraries. See systems/chasm.md. - HSM (
service/history/hsm/) — the hierarchical state-machine machinery used by callbacks and Nexus operations inside History. - Persistence (
common/persistence/) — pluggable storage interface with Cassandra, MySQL, PostgreSQL, and SQLite implementations behind the same Go interfaces. - Replication (
service/history/replication/) — multi-cluster (XDC) replication of namespaces and history events.
Lifecycle of a server process
graph TD
Start[cmd/server/main.go: buildCLI]
Load[temporal/fx.go: load config + dynamicconfig]
Auth[authorization.GetAuthorizer]
NewSrv[temporal.NewServer]
StartFx[fx graph starts each service]
Ringpop[membership ring joins]
Shards[History acquires shards]
Ready[Frontend accepts traffic]
Start --> Load --> Auth --> NewSrv --> StartFx --> Ringpop --> Shards --> ReadyThe dependency graph is constructed with Uber fx; the top-level provider list is in temporal/fx.go. Each service has its own fx.go (e.g. service/history/fx.go, service/matching/fx.go) that depends on shared providers from common/.
Further reading inside this wiki
- Frontend service — public API surface.
- History service — workflow execution engine.
- Matching service — task queue dispatcher.
- Internal Worker service — system workflows.
- Persistence — storage abstractions and backends.
- CHASM — state-machine framework.
- Replication / XDC — cross-cluster replication.
docs/architecture/— hand-maintained deeper dives kept in-tree.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.