"""worker_lane — settings-backed slots for each celery lane. Milestone 422 step 1. One row per lane, holding only what an operator can change: how many slots it runs, the ceiling they have set for themselves, and whether it consumes its queues at all. ## What is deliberately not a column **The queues.** They are decided by `celery_app.py`'s `task_routes`, not by preference, so a stored copy could contradict the routing table with nothing to notice until a queue had no consumer. They live in `services/worker_lanes.py`. **The derived ceiling.** Computed from the container's cgroup limits on every read. A row written on a 32GB host and later run in a 4GB container must be bounded by the 4GB; a stored ceiling would quietly authorise what the box can no longer hold. ## The seeded values Written out literally rather than imported from `worker_lanes.LANES`. A migration is a statement about one moment in the schema's history — if it imported the live defaults, changing them in 2027 would silently change what this 2026 revision does on a fresh database. The two are allowed to diverge afterwards, and that is correct: `LANES` supplies defaults for a lane added later, this file records what was seeded today. lane slots cap enabled worker 1 4 yes scheduler 1 2 yes maintenance_long 1 2 yes ml 0 1 NO One of each, per the operator (2026-09-22: *"that starting value should be one of each"*), and far below their own production numbers — worker 8 and ml 2 are tuned for their hardware and are not a sane first boot for a stranger. **ml ships at zero and disabled**, which is milestone 422 step 6's requirement arriving early: enabling the lane is what triggers the SigLIP download, and rule 164 permits a runtime fetch only for a feature that is "optional and clearly off". Seeding it on would make every fresh install reach HuggingFace. The caps start low on purpose. A cap that begins at the ceiling is a rubber stamp; starting at 4/2/2/1 means raising slots within the cap is ordinary and raising the cap is a deliberate act. ## Existing installs Nothing is migrated FROM. The `CELERY_QUEUES` / `CELERY_CONCURRENCY` env vars stay exactly as they are and remain the baseline each lane boots at; these rows are the adjustment applied on top (step 3). So this migration changes no behaviour on a running stack — it only makes the numbers storable. Revision ID: 0103 Revises: 0102 Create Date: 2026-09-22 """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op revision: str = "0103" down_revision: Union[str, None] = "0102" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None # (name, slots, slots_cap, enabled) — see the docstring for why these are # literals and not an import. _SEED = ( ("worker", 1, 4, True), ("scheduler", 1, 2, True), ("maintenance_long", 1, 2, True), ("ml", 0, 1, False), ) def upgrade() -> None: worker_lane = op.create_table( "worker_lane", sa.Column("name", sa.String(length=32), nullable=False), sa.Column("slots", sa.Integer(), nullable=False), sa.Column("slots_cap", sa.Integer(), nullable=False), sa.Column("enabled", sa.Boolean(), nullable=False), sa.Column( "updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False, ), sa.PrimaryKeyConstraint("name", name=op.f("pk_worker_lane")), # Bare constraint names: Base.metadata's naming convention prepends # ck_worker_lane_, and pre-prefixing doubles it — the defect alembic # 0088 had to rename four constraints for (#3275). op.f() marks these # as already-final so autogenerate does not propose renaming them. sa.CheckConstraint("slots >= 0", name=op.f("ck_worker_lane_slots_non_negative")), sa.CheckConstraint("slots_cap >= 0", name=op.f("ck_worker_lane_cap_non_negative")), # The invariant that makes the cap mean anything, in the database # rather than only in the service: a row violating it is not a # rejected request, it is a lane that step 3's reconcile will drive UP # to a number the operator capped. sa.CheckConstraint("slots <= slots_cap", name=op.f("ck_worker_lane_slots_within_cap")), ) # No index beyond the primary key, deliberately — four rows, forever. Same # reasoning as service_seen, and the lesson of #3301, which removed seven # indexes that were write cost buying nothing. op.bulk_insert( worker_lane, [ {"name": name, "slots": slots, "slots_cap": cap, "enabled": enabled} for name, slots, cap, enabled in _SEED ], ) def downgrade() -> None: # The rows go with the table. They are settings with shipped defaults, not # operator data that predates this revision — a downgrade returns the stack # to reading its concurrency from env, which is where it reads it from # today anyway. op.drop_table("worker_lane")