Skip to content

Schema Reference ​

cyclone.Migrate creates this schema. The embedded files in migrations/ are the canonical source for a deployment review. Migrations lists what each file changes. This page describes the resulting schema, not one version of it.

Table Overview ​

ObjectPurpose
cyclone_runsOne row per workflow or standalone run
cyclone_stepsDurable step state, input/output, retries, waits, and leases
cyclone_depsControl/data dependency edges
cyclone_step_inputsOrdered multi-source input bindings
cyclone_signalsDurable signal mailbox
cyclone_schedulesCron schedule definitions and scanner leases
cyclone_schedule_firesDeduplication record for each schedule slot
cyclone_schema_versionTern migration version tracking

cyclone_runs ​

ColumnTypeNotes
idbigint identityPrimary key
workflowtextWorkflow name or standalone kind
def_versiontextDAG topology fingerprint
inputjsonb, nullableSubmitted workflow input
statetextrunning, paused, completed, failed, or cancelled
created_at, updated_attimestamptzCreation and last-update timestamps
finished_attimestamptz, nullableTerminal timestamp
deadline_attimestamptz, nullableAbsolute run deadline (migration 6). Resume moves it forward by the length of the pause
result_nodetext, nullableNode whose output is the run result, for a DefineResult workflow (migration 15)
submit_keytext, nullableSubmitOnce key (migration 16). NULL for all other runs
has_fan_outbooleanThe run has a fan-out node, default false. The engine reads it with the run row lock (migration 17)
paused_attimestamptz, nullableTime of the pause while the run is paused (migration 18). Resume clears it. A run that ends during the pause keeps it
parent_run_id, parent_node_idbigint / text, nullableThe parent run and the parent node of a child run (migration 19). NULL for a run with no parent. There is no foreign key. A purge can delete the parent run before the child run

The primary key is id. A run starts in running. There is no persisted pending run state.

idx_runs_submit_key keeps (workflow, submit_key) unique for every run with a key, in every state. When Cyclone purges a run, its key is free again.

idx_runs_parent keeps (parent_run_id, parent_node_id) unique for every child run, in every state. Each child node therefore has at most one child run. A purge keeps a terminal child run until its parent run completes or is deleted.

cyclone_steps ​

ColumnTypeNotes
idbigint identityPrimary key and insertion-order tie breaker
run_idbigintFK to cyclone_runs(id) with ON DELETE CASCADE
node, kindtextNode ID and registered handler kind
statetextpending, available, running, waiting_signal, waiting_timer, waiting_child, paused, completed, discarded, or cancelled
input, outputjsonb, nullablePersisted handler input and output
input_fromtext, nullableSingle-source upstream node
queuetextDefault default; API and DB cap it at 256 bytes
priorityintegerHigher values claim first
attemptintegerFailed executions consumed; a first-try success remains 0
max_attemptsintegerDefault 5
scheduled_attimestamptzEarliest claim time or wait deadline
timeoutinterval, nullablePer-execution timeout
signal_nametext, nullableWaitSignal/WaitN signal
signal_namestext[], nullableWaitAny candidates (migration 8)
wait_forinterval, nullableTimer or signal-wait duration
wait_countinteger, nullableExplicit WaitN quorum, including 1
on_dep_failuretext, nullableNULL/fail-run or cancel_branch (migration 9)
sequence_keytext, nullableFIFO-per-key gate (migration 10)
unique_keytext, nullableStandalone uniqueness key; idx_steps_unique_active scopes it to active work
parent_node, unique_untiltext / timestamptz, nullableReserved by the initial schema. No query reads them or writes them. The parent link of a child run is on cyclone_runs, not in parent_node
lease_ownertext, nullableWorker holding the current lease
lease_expiry, heartbeat_attimestamptz, nullableDB-clock lease timestamps
lease_generationbigintIncremented on each claim; fences stale workers (migration 11)
reclaimsintegerExpired-lease reclaims so far, default 0; bounded by WithMaxReclaims and reset by an operator retry (migration 14)
fan_out_parallelinteger, nullableSet on a fan-out node only: its WithMaxParallel limit, 0 for no limit (migration 17)
fan_out_max_bytesbigint, nullableSet on the fan-out node of a step: the byte limit of the collected output. 0 means no limit. NULL on the fan-out node of an action, which collects no output (migration 17)
item_of, item_indextext / integer, nullableSet on an item task only: its fan-out node and the position of its item, from 0 (migration 17)
child_workflowtext, nullableSet on a child node only: the workflow that it starts. Its kind is @child (migration 19)
child_needs_resultboolean, nullableSet on a child node only. True for a ResultWorkflow.Child node, which needs the result of its child run. False for a Workflow.Child node (migration 19)
error, error_kindtext, nullableLast failure and classification
panic_stacktext, nullableCaptured panic stack (migration 5)
retentioninterval, nullablePer-step run retention override
created_at, started_at, completed_attimestamptzLifecycle timestamps; latter two nullable

(run_id, node) is unique in addition to the id primary key. The node ID of an item task is the ID of its fan-out node plus the item index in brackets, for example process[3]. This constraint therefore gives each item task one identity.

cyclone_deps ​

ColumnTypeNotes
run_idbigintFK to cyclone_runs(id) with cascade delete
nodetextDependent node
dep_nodetextUpstream dependency
dep_typetextcontrol by default; data edges are also recorded

Primary key: (run_id, node, dep_node).

cyclone_step_inputs ​

ColumnTypeNotes
run_id, nodebigint / textFK to the consuming (run_id, node) step
slotintegerStable source position
dep_nodetextFK to the producing step in the same run
assembly_modetexttuple or array (migration 3)

Primary key: (run_id, node, slot).

cyclone_signals ​

ColumnTypeNotes
idbigint identityPrimary key and delivery order
run_idbigintFK to cyclone_runs(id) with cascade delete
nametextDeclared signal name
payloadjsonb, nullableDelivered value
created_attimestamptzMailbox insertion time
consumed_bytext, nullableWait node that consumed the row
consumed_attimestamptz, nullableConsumption time

The Go API bounds pending mailbox rows to the useful capacity of the declared wait. The database does not cap payload byte size. Validate untrusted inputs at the application boundary.

Lifecycle ​

A signal row has no lifecycle of its own. It lives as long as its run. Only the ON DELETE CASCADE from cyclone_runs deletes it, and retention is what triggers that delete. Cyclone keeps a consumed row (consumed_by IS NOT NULL) for audit until it purges the run.

Depth has a bound per run. It is not unbounded. Signal refuses to insert once buffered >= limit (1 for WaitSignal or WaitAny, N for WaitN). A delivery that arrives after the wait has completed is not stored.

cyclone_schedule_fires shares only the cascade lifetime. It has no consumed_by and no mailbox bound. There is one row per fire, kept until Cyclone purges its run.

cyclone_schedules ​

ColumnTypeNotes
idtextPrimary key
kind, inputtext / jsonbFired handler and input
cron_spec, locationtextCron expression and timezone (Local default)
next_run_attimestamptzNext slot
enabledbooleanScanner eligibility
lease_owner, lease_expirytext / timestamptz, nullableSchedule scanner lease
last_run_idbigint, nullableFK to last fired run, ON DELETE SET NULL
last_errortext, nullableLast scanner/fire error
consecutive_failuresintegerFailure count
max_attempts, timeoutinteger / intervalFired-step policy
queue, priority, retentiontext / integer / intervalFired-step routing and retention
def_versiontext, nullablePinned workflow-definition fingerprint (migration 12). NULL for a step schedule or an action schedule
created_at, updated_attimestamptzLifecycle timestamps

Queue values are capped at 256 bytes by migration 11.

cyclone_schedule_fires ​

The columns are schedule_id, scheduled_for, run_id, and created_at. The primary key (schedule_id, scheduled_for) stops two scanners from recording the same slot. Both IDs are foreign keys with cascade delete.

Views ​

  • cyclone_v_runs: run identity, state, and timestamps. It also has step counts for total, completed, discarded, running, signal-waiting, and timer-waiting. It has the parent link of a child run.
  • cyclone_v_steps: step status, attempts, errors, lease metadata, computed lease_expired, timestamps, signal name, panic stack, and reclaim count. For an item task, it also has the fan-out node and the item index.
  • cyclone_v_kinds: count, oldest available age, and expired leases by (kind, state).
  • cyclone_v_queues: available, running, and waiting counts, oldest available age, and expired leases by queue.

Indexes ​

IndexDefinition / purpose
idx_steps_claim(queue, priority DESC, scheduled_at) for available claims
idx_steps_leaselease_expiry for running-step recovery
idx_steps_timerscheduled_at for timer waits
idx_steps_signal(run_id, signal_name) for signal waits
idx_steps_signal_timeoutscheduled_at for capped signal waits
idx_steps_timeoutstarted_at for running steps with a timeout
idx_steps_signal_name_uniqueunique (run_id, signal_name) when non-NULL
idx_steps_unique_activeunique (kind, unique_key) for active standalone work, paused included (migration 18)
idx_steps_sequence(sequence_key, scheduled_at, id) for active sequenced work, paused included (migration 18)
idx_signals_unconsumed(run_id, name, id) for unconsumed mailbox rows
idx_signals_run(run_id) for the FK cascade retention triggers
idx_schedule_fires_run(run_id) for the FK cascade retention triggers
idx_deps_depnode(run_id, dep_node) for downstream traversal
idx_step_inputs_depnode(run_id, dep_node) for fan-in lookup
idx_schedules_duenext_run_at for enabled schedules
idx_schedules_leaselease_expiry for leased schedules
idx_runs_state_id(state, id DESC) for run listing
idx_runs_deadlinedeadline_at for running runs with deadlines
idx_runs_submit_keyunique (workflow, submit_key) when submit_key is non-NULL
idx_steps_fan_out(run_id) for pending fan-out nodes
idx_steps_items(run_id, item_of, state, item_index) for item tasks
idx_runs_parentunique (parent_run_id, parent_node_id) for child runs (migration 19)
idx_steps_waiting_child(run_id) for waiting child nodes (migration 19)

Migration and Compatibility Notes ​

A pre-1.0 schema has no backward-compatibility guarantee. Review each embedded migration for locks and for mixed-version safety. In particular, do not deploy migration 11 next to v0.1.0 workers. Stop those workers and drain them, migrate, then start the upgraded workers.

Migrate uses a session-level advisory lock. Connect directly to Postgres, or through session-mode PgBouncer. Transaction-mode PgBouncer is not safe for the migration connection.

Cyclone stores each run in Postgres.