You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add stable logical aliases for immutable concrete flow versions through a DeployedFlow wrapper.
Callers start a stable alias such as greetUser. Startup deployment resolves that alias to one concrete version such as greetUserV2. Runs, tasks, queues, broadcasts, and history retain the concrete slug.
Alias metadata remains independent from DAG shape and queue mode.
A plain Flow remains supported and defaults to a self-alias:
alias = concrete flow_slug
defineDeployedFlow() preserves the exact wrapped plain or step-queued flow type. It must not weaken handler, dependency, condition, queue-mode, step-selector, context, or environment inference.
Ownership model
flow_slug
immutable concrete version and runtime identity
flow_alias
stable dispatch name with one active concrete target
Flow or StepQueuedFlow
DAG, handlers, and queue mode
DeployedFlow
immutable alias membership for one concrete definition
Aliases never appear in generated private queue names. Queue names use the concrete slug so versions remain isolated.
Data model
Add stable alias membership and one active pointer. Supported definition operations reject membership changes; general database-enforced identity immutability belongs to #678, not this feature.
pgflow.flows
flow_slug primary key
flow_alias not null
unique (flow_slug, flow_alias)
pgflow.flow_aliases
flow_alias primary key
flow_slug not null
created_at timestamptz not null
updated_at timestamptz not null
foreign key (flow_slug, flow_alias)
references pgflow.flows (flow_slug, flow_alias)
Invariants:
every concrete flow has one immutable alias membership;
omitted aliases resolve to the concrete slug;
several concrete versions may share one alias;
exactly one concrete version is active for each alias;
an existing concrete slug cannot move to another alias;
existing flows backfill as self-aliases;
the migration does not infer version families from slug suffixes.
Startup deployment and activation
Every worker carries the complete wrapped definition. For operations touching both an alias and a concrete definition, use this order:
compile or verify the complete concrete flow shape, queue mode, and route map;
set the active pointer only when the alias has no active version yet;
commit before worker registration.
Compiling a new concrete version for an existing alias never changes the active pointer. This prevents concurrent V2 and V3 deployments from using last-lock-holder-wins activation.
Activation rules:
the first concrete member initializes an empty alias;
later concrete versions remain inactive after compilation;
rechecking the active slug preserves activation;
rechecking an inactive slug leaves it inactive;
restarting an old worker never reclaims the alias;
local destructive recompilation preserves membership and activation state;
alias mismatch always fails;
worker startup failure cannot switch an existing alias.
Several step workers may race to deploy the same concrete version. The locks make one compile and the others verify. Keep runtime task synchronization unchanged; these alias/definition locks do not justify a broad callback or recovery rewrite.
Start APIs
Move concrete behavior behind explicit slug functions and make aliases the default:
start_flow() alias
start_flow_by_alias() alias, explicit
start_flow_by_slug() concrete slug
start_flow_with_states() alias
start_flow_with_states_by_slug() concrete slug
Compatibility:
keep the existing flow_slug RPC argument name on default start functions;
missing aliases fail and never fall back to concrete lookup;
alias resolution and concrete start happen in one database call;
returned runs and events expose the resolved concrete slug.
Clients use alias semantics by default and add startFlowBySlug() for pinned starts.
Activation, rollback, and deletion
Add one compare-and-swap operation:
pgflow.activate_flow_version(
flow_alias text,
flow_slug text,
expected_current_flow_slug text
)
It validates membership, takes the alias lock, compares the current pointer, and atomically switches only when the expected current version still matches. The caller uses null only to initialize an alias with no active version.
Production rollout follows #654 and #651: deploy and enable the complete new worker set, check queue coverage as an operator, then call activate_flow_version(). Rollback uses the same expected-current check. Coverage is a documented deployment check, not a database-wide startup scan or automatic activation gate.
Keep deletion concrete-slug based. Rules:
deleting an inactive version is allowed;
deleting the active target is blocked by default;
removing an alias requires that concrete slug to be its sole version;
future explicit shared queues are never dropped with one version.
Recompilation and membership
Alias membership and the active pointer must survive local startup recompilation.
Once aliases exist, destructive recompilation must not lose alias membership or switch the active pointer. Prefer retaining the concrete identity row while replacing runtime rows, steps, route metadata, and private queues. Reuse existing operations with the smallest transaction adjustment rather than redesigning the lifecycle.
Take alias and concrete locks in the documented order for activation, recompilation, and alias-aware deletion.
Versioned private queues
For a step-queued deployment:
greetUserV1 -> V1 private step queues
greetUserV2 -> V2 private step queues
alias greetUser -> active concrete version for new runs
Existing V1 tasks never move to V2 queues. Keep V1 workers until no V1 run can create or execute more tasks.
Queue and migration boundary
Reuse #650/#651's public PGMQ operations, persisted routes, read-once claiming, and warn/skip behavior. Alias resolution needs no queue-body inspection, physical-object validation, pgmq.meta lock, or fatal-message protocol. Existing set_vt_batch() and direct archive pruning remain supported.
Use existing foreign keys and uniqueness constraints for membership and pointer integrity. Keep compare-and-swap activation and active-target deletion guards: they prevent real lost updates and accidental version removal. Do not add unconditional identity triggers or a general audit framework.
Use the repository's schema-first migration workflow. Backfill existing definitions as self-aliases transactionally, preserve released migrations, and limit checks to the new alias constraints. Fail without automatic renaming or repair. Document the maintenance upgrade and use bounded lock waits.
Observability
Startup results and logs distinguish:
compilation: compiled | verified | recompiled
activation: activated | active | inactive
alias
active concrete slug
queue mode
selected worker queue
Do not log credentials or message bodies.
Acceptance criteria
defineDeployedFlow() preserves exact plain and step-queued flow types.
Plain flows retain self-alias behavior.
Every concrete flow has non-null alias membership; supported operations reject reassignment without a new general immutability trigger.
Existing definitions backfill as self-aliases without suffix inference.
Exactly one active concrete target exists per alias.
New concrete definitions compile completely but do not replace an existing alias automatically.
The first concrete member initializes only an empty alias.
Summary
Add stable logical aliases for immutable concrete flow versions through a
DeployedFlowwrapper.Callers start a stable alias such as
greetUser. Startup deployment resolves that alias to one concrete version such asgreetUserV2. Runs, tasks, queues, broadcasts, and history retain the concrete slug.Alias metadata remains independent from DAG shape and queue mode.
Dependencies and stage
0.16.0through refactor: make worker startup the only flow compilation path #672/Version Packages #674.Public API
Plain flow:
Private per-step flow:
A plain
Flowremains supported and defaults to a self-alias:defineDeployedFlow()preserves the exact wrapped plain or step-queued flow type. It must not weaken handler, dependency, condition, queue-mode, step-selector, context, or environment inference.Ownership model
Aliases never appear in generated private queue names. Queue names use the concrete slug so versions remain isolated.
Data model
Add stable alias membership and one active pointer. Supported definition operations reject membership changes; general database-enforced identity immutability belongs to #678, not this feature.
Invariants:
Startup deployment and activation
Every worker carries the complete wrapped definition. For operations touching both an alias and a concrete definition, use this order:
Within one startup transaction:
Compiling a new concrete version for an existing alias never changes the active pointer. This prevents concurrent V2 and V3 deployments from using last-lock-holder-wins activation.
Activation rules:
Several step workers may race to deploy the same concrete version. The locks make one compile and the others verify. Keep runtime task synchronization unchanged; these alias/definition locks do not justify a broad callback or recovery rewrite.
Start APIs
Move concrete behavior behind explicit slug functions and make aliases the default:
Compatibility:
flow_slugRPC argument name on default start functions;Clients use alias semantics by default and add
startFlowBySlug()for pinned starts.Activation, rollback, and deletion
Add one compare-and-swap operation:
It validates membership, takes the alias lock, compares the current pointer, and atomically switches only when the expected current version still matches. The caller uses
nullonly to initialize an alias with no active version.Production rollout follows #654 and #651: deploy and enable the complete new worker set, check queue coverage as an operator, then call
activate_flow_version(). Rollback uses the same expected-current check. Coverage is a documented deployment check, not a database-wide startup scan or automatic activation gate.Keep deletion concrete-slug based. Rules:
Recompilation and membership
Alias membership and the active pointer must survive local startup recompilation.
Once aliases exist, destructive recompilation must not lose alias membership or switch the active pointer. Prefer retaining the concrete identity row while replacing runtime rows, steps, route metadata, and private queues. Reuse existing operations with the smallest transaction adjustment rather than redesigning the lifecycle.
Take alias and concrete locks in the documented order for activation, recompilation, and alias-aware deletion.
Versioned private queues
For a step-queued deployment:
Existing V1 tasks never move to V2 queues. Keep V1 workers until no V1 run can create or execute more tasks.
Queue and migration boundary
Reuse #650/#651's public PGMQ operations, persisted routes, read-once claiming, and warn/skip behavior. Alias resolution needs no queue-body inspection, physical-object validation,
pgmq.metalock, or fatal-message protocol. Existingset_vt_batch()and direct archive pruning remain supported.Use existing foreign keys and uniqueness constraints for membership and pointer integrity. Keep compare-and-swap activation and active-target deletion guards: they prevent real lost updates and accidental version removal. Do not add unconditional identity triggers or a general audit framework.
Use the repository's schema-first migration workflow. Backfill existing definitions as self-aliases transactionally, preserve released migrations, and limit checks to the new alias constraints. Fail without automatic renaming or repair. Document the maintenance upgrade and use bounded lock waits.
Observability
Startup results and logs distinguish:
Do not log credentials or message bodies.
Acceptance criteria
defineDeployedFlow()preserves exact plain and step-queued flow types.activate_flow_version()uses expected-current compare-and-swap semantics.Out of scope