actor_config.actor is the table's primary key, and queue + metadata are drift-protected structural fields (_STRUCTURAL_FIELDS in worker/startup.py, enforced via ActorConfigDriftList). That's a sensible stance for long-lived deployments, but it pushes apps that run the same code against many short-lived, per-run queues toward per-deployment actor names like my-actor.<run-id> — you can't re-point the same actor name at a different queue without tripping the drift check.
That workaround functions, but each run leaves an actor_config row behind, usually with a matching queues row, and nothing ever removes them. There's deregister_worker for the workers table, and actor_config_ops exposes list_actor_configs / get_actor_config / set_actor_config_capacity — read and tune, but no delete. For ephemeral per-run workers the tables grow monotonically and the admin actor list fills up with dead names.
Today the cleanup is hand-rolled SQL:
await conn.execute(f'DELETE FROM "{schema}".actor_config WHERE actor = $1', actor_name)
await conn.execute(f'DELETE FROM "{schema}".queues WHERE name = $1', queue_name)
which works until it doesn't — it's outside the migration-managed surface, and I have to reason about the referential questions myself: what about terminal jobs whose actor column still names the deleted row, or a cron_schedules entry pointing at it?
What I'd rather have:
await client.actors.deregister("my-actor.<run-id>")
with defined semantics — refuse while non-terminal jobs or enabled schedules reference the actor, optionally a force=True that documents what happens to terminal history, and the same question answered for orphaned queues rows once no actor_config points at them.
If there's an intended GC path for per-run actors that I've missed, point me at it. Otherwise: is deregistration in scope for the operator surface, or is "actors are forever" a deliberate design stance? If it's deliberate, it'd be worth documenting, because the drift-check-plus-PK combination quietly funnels ephemeral deployments into exactly this accumulation pattern.
actor_config.actoris the table's primary key, andqueue+metadataare drift-protected structural fields (_STRUCTURAL_FIELDSinworker/startup.py, enforced viaActorConfigDriftList). That's a sensible stance for long-lived deployments, but it pushes apps that run the same code against many short-lived, per-run queues toward per-deployment actor names likemy-actor.<run-id>— you can't re-point the same actor name at a different queue without tripping the drift check.That workaround functions, but each run leaves an
actor_configrow behind, usually with a matchingqueuesrow, and nothing ever removes them. There'sderegister_workerfor the workers table, andactor_config_opsexposeslist_actor_configs/get_actor_config/set_actor_config_capacity— read and tune, but no delete. For ephemeral per-run workers the tables grow monotonically and the admin actor list fills up with dead names.Today the cleanup is hand-rolled SQL:
which works until it doesn't — it's outside the migration-managed surface, and I have to reason about the referential questions myself: what about terminal jobs whose
actorcolumn still names the deleted row, or acron_schedulesentry pointing at it?What I'd rather have:
with defined semantics — refuse while non-terminal jobs or enabled schedules reference the actor, optionally a
force=Truethat documents what happens to terminal history, and the same question answered for orphanedqueuesrows once noactor_configpoints at them.If there's an intended GC path for per-run actors that I've missed, point me at it. Otherwise: is deregistration in scope for the operator surface, or is "actors are forever" a deliberate design stance? If it's deliberate, it'd be worth documenting, because the drift-check-plus-PK combination quietly funnels ephemeral deployments into exactly this accumulation pattern.