ores.workflow.workflow_instance
Table of Contents
Tracks the lifecycle of a workflow execution, including its type, the entity it acts on, its status, the serialised request that triggered it, and any result or error produced. Instances are append-mostly; status transitions are the primary mutation.
1. Flags
2. Natural keys
3. Columns
3.1. id
UUID primary key for the workflow instance.
3.2. type
Workflow type name, e.g. 'provision_tenant_workflow'.
3.3. target_kind
What the run acts on, named by the caller: 'tenant' for a run that provisions a tenant. The engine stores it and never interprets it, so a component states its own kind without the engine learning that component's vocabulary.
A run that acts on no entity leaves it empty.
3.4. target_id
Identity of the entity the run acts on, in the kind target_kind states. It is
not a foreign key: the engine cannot hold one to every component's tables, so it
is a soft reference, the way a status row names its badge.
The target is not the owner. tenant_id states the tenant whose work the run is,
which scopes the run and governs who reads it. The target states what the run
works on, which may be a different tenant entirely. Provisioning is the case that
makes the difference plain: the run belongs to the tenant that asked for it, so
that tenant can follow its own work, and it acts on the tenant it creates, which
cannot read the run because its session does not exist yet.
3.5. state_id
FK to the FSM state record (ores_workflow_fsm_states_tbl) for this instance.
3.6. request_json
Serialised JSON payload of the originating request.
3.7. result_json
Serialised JSON result, populated on successful completion.
3.8. error
Human-readable error message, populated on failure.
3.9. correlation_id
Distributed tracing correlation ID (Nats-Correlation-Id header value).
3.10. created_by
Username or service identity that initiated the workflow.
3.11. step_count
How many steps the run's chain holds, fixed when the run starts. Nothing advances it, and a run's chain never changes, so this is a fact about the run rather than a cursor over it. It is stored so that a list of runs can say how long each one is without reading every chain.
3.12. completed_at
Timestamp when the workflow reached a terminal state. The engine writes it from its state funnel, so a caller editing the run never states it.
3.13. last_event_at
Timestamp of the run's most recent activity: every state transition, and every dispatch the run does between two of them. The engine writes it, so a caller editing the run never states it.
4. SQL
4.1. Flags
5. Indexes
The list query filters by state and orders by type, and a lookup names one
instance, so the tenant index and the tenant-scoped unique index are the hot
paths. The engine's own list read is not: it spans every tenant, so it cannot
use a leading tenant_id, and it reads the open rows in one pass at startup.
That pass is bounded by the runs in flight rather than by the table, so a scan
is the honest cost of recovering all of them. The target has no index yet: the
list read filters by target in memory, after the tenant's rows are read, so an
index would cost every write and help no read. It comes with a repository read
that filters by target.
5.1. tenant_id
5.2. state_id
5.3. tenant_type
6. C++
6.1. Flags
6.2. Repository
Reads rely on the row-level security policy instead of an application-level
tenant_id predicate. That policy admits a row to its own tenant, and to a
system-tenant session it admits every tenant's rows.
The engine is a cross-tenant service. It holds the system tenant and drives runs
that belong to whoever asked for them, so a tenant_id = ctx.tenant_id()
predicate on top of the policy hides every other tenant's run from the engine.
A run it cannot read back is a run it cannot advance or recover: the step insert
is refused, the published marker and the step progress become silent no-ops, and
the completion event finds no step. A tenant session still reads only its own
rows, because the policy is what draws that line.
6.3. Domain includes
#include <optional> #include <string> #include <boost/uuid/uuid.hpp>
6.4. Conventions
6.5. Custom repository methods
7. See also
- ores.workflow — component group overview.