# Execution Model (Token & Concurrency)
> This document describes **how workflows are actually executed at runtime**.
> It defines the token lifecycle, concurrency rules, and transactional boundaries of the engine.
---
## 1. Core Idea
> **Token is the unit of execution.**
The engine does not execute a workflow as a call stack.
Instead, it advances the workflow by **moving tokens through a process graph**.
- One token = one execution authority
- Multiple tokens = parallel execution
---
## 2. Token Structure
A token represents the right to execute a node.
```
Token
├─ id
├─ instance_id
├─ node_id
├─ status
├─ mode (Forward / Compensation)
├─ parallel_group_id?
├─ version // optimistic lock
└─ timestamps
```
---
## 3. Token Lifecycle
### 3.1 States
```
Created → Ready → Executing → (Waiting) → Completed
↘︎ Failed / Terminated
```
| Created | Token has been created but not scheduled |
| Ready | Token can be claimed by an executor |
| Executing | Token is currently executing a node |
| Waiting | Token is blocked (timer / user task) |
| Completed | Token finished its work |
| Terminated | Token will never be scheduled again |
---
## 4. Token Claim Mechanism
### 4.1 Why Claim Is Required
Without a claim step:
- Multiple executors may run the same token
- Side effects may be duplicated
---
### 4.2 Claim Algorithm (CAS)
A token can only be claimed if it is **Ready**.
```
UPDATE token
SET status = Executing,
version = version + 1
WHERE id = ?
AND status = Ready
AND version = ?
```
- Success (1 row updated): claim acquired
- Failure (0 rows): token already taken
> Claim is the **concurrency gate** of the engine.
---
## 5. Transaction Boundary
> **One event handler = one database transaction**
### Handler Responsibilities
Within a single transaction, a handler may:
- Claim or update tokens
- Create or terminate tokens
- Persist timers / user tasks
- Persist outgoing events (Outbox)
A handler must **not**:
- Call other handlers directly
- Perform long-running IO
---
## 6. Parallel Execution Model
### 6.1 Fork
- One token arrives at a parallel gateway
- Engine creates N child tokens
- Parent token is completed
```
Token A → Fork → Token B + Token C + Token D
```
---
### 6.2 Parallel Group
All tokens created by a fork share the same `parallel_group_id`.
This group ID is used to coordinate joins.
---
### 6.3 Join
A join completes when **all tokens in the same parallel group** reach the join gateway.
To prevent duplicate joins:
```
parallel_join
├─ group_id (unique)
└─ joined (bool)
```
Join algorithm:
```
UPDATE parallel_join
SET joined = true
WHERE group_id = ? AND joined = false
```
- Success → create next token
- Failure → join already completed
---
## 7. Waiting Tokens
A token enters `Waiting` when execution cannot continue immediately.
Typical reasons:
- Timer (delay / timeout / retry backoff)
- User task
- External signal
Waiting tokens are **not executable** until resumed by an event.
---
## 8. Failure Handling
### 8.1 Failure as Event
Failures are not exceptions.
They are emitted as events:
```
TokenFailed
```
---
### 8.2 Retry
- Retry does not create new tokens
- The same token is rescheduled
- Backoff is driven by timers
---
## 9. Idempotency & Safety
The execution model guarantees:
- A token is executed at most once at a time
- Parallel joins happen exactly once
- State transitions are atomic
This is achieved via:
- Optimistic locking (version)
- Unique constraints
- Event-driven progression
---
## 10. Relationship to Other Documents
- Saga & compensation behavior: `saga.md`
- Crash recovery & rehydration: `recovery.md`
---
> **If token execution is correct, the engine is correct.**