Skip to content

Commit 9451dd9

Browse files
HyeockJinKimclaude
andcommitted
feat(BA-6030): add RouteSubStatus enum and DB migration for provisioning sub-stages
Introduces RouteSubStatus (PENDING, STARTING, WARMING_UP) to track fine-grained progress within the PROVISIONING lifecycle stage, laying the foundation for splitting provisioning into distinct handlers. - Add RouteSubStatus enum to data/deployment/types.py - Add sub_status (nullable VARCHAR, server_default='pending') and updated_at (NOT NULL TIMESTAMPTZ, server_default=now()) columns to routings - Change traffic_status server_default from 'active' to 'inactive' so new routes start with traffic disabled until healthy - Backfill traffic_status based on current status (RUNNING→active, others→inactive) - Update RouteCreatorSpec to default traffic_status to INACTIVE Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c3347d3 commit 9451dd9

4 files changed

Lines changed: 90 additions & 3 deletions

File tree

src/ai/backend/manager/data/deployment/types.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,20 @@ class RouteTrafficStatus(enum.StrEnum):
150150
INACTIVE = "inactive"
151151

152152

153+
class RouteSubStatus(enum.StrEnum):
154+
"""Sub-status for routes in the PROVISIONING lifecycle stage.
155+
156+
Tracks fine-grained progress within the provisioning pipeline:
157+
- PENDING: session has been enqueued, waiting for scheduler
158+
- STARTING: session is running, waiting for replica host/port
159+
- WARMING_UP: replica is up, waiting for health check to pass
160+
"""
161+
162+
PENDING = "pending"
163+
STARTING = "starting"
164+
WARMING_UP = "warming_up"
165+
166+
153167
# ========== Status Transition Types (BEP-1030) ==========
154168

155169

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Add route sub_status, updated_at columns and fix traffic_status default.
2+
3+
Revision ID: a1b2c3d4e5f7
4+
Revises: d3f4a5b6c7d8
5+
Create Date: 2026-05-14
6+
7+
"""
8+
9+
# Part of: 26.5.0
10+
11+
from alembic import op
12+
13+
# revision identifiers, used by Alembic.
14+
revision = "a1b2c3d4e5f7"
15+
down_revision = "d3f4a5b6c7d8"
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade() -> None:
21+
conn = op.get_bind()
22+
23+
# Add sub_status column (nullable, defaults to 'pending' for new rows)
24+
conn.exec_driver_sql(
25+
"ALTER TABLE routings ADD COLUMN IF NOT EXISTS sub_status VARCHAR(64) DEFAULT 'pending'"
26+
)
27+
28+
# Add updated_at as nullable first, backfill, then enforce NOT NULL
29+
conn.exec_driver_sql(
30+
"ALTER TABLE routings ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP WITH TIME ZONE"
31+
)
32+
conn.exec_driver_sql("UPDATE routings SET updated_at = created_at WHERE updated_at IS NULL")
33+
conn.exec_driver_sql("ALTER TABLE routings ALTER COLUMN updated_at SET NOT NULL")
34+
conn.exec_driver_sql("ALTER TABLE routings ALTER COLUMN updated_at SET DEFAULT now()")
35+
36+
# Clear sub_status for non-PROVISIONING rows — only meaningful during provisioning
37+
conn.exec_driver_sql("UPDATE routings SET sub_status = NULL WHERE status != 'provisioning'")
38+
39+
# Backfill traffic_status based on lifecycle: RUNNING keeps ACTIVE, all others become INACTIVE
40+
conn.exec_driver_sql("""
41+
UPDATE routings
42+
SET traffic_status = CASE
43+
WHEN status = 'running' THEN 'active'
44+
ELSE 'inactive'
45+
END
46+
""")
47+
48+
# Change server default for new inserts
49+
conn.exec_driver_sql("ALTER TABLE routings ALTER COLUMN traffic_status SET DEFAULT 'inactive'")
50+
51+
52+
def downgrade() -> None:
53+
conn = op.get_bind()
54+
55+
conn.exec_driver_sql("ALTER TABLE routings ALTER COLUMN traffic_status SET DEFAULT 'active'")
56+
conn.exec_driver_sql("UPDATE routings SET traffic_status = 'active'")
57+
op.drop_column("routings", "updated_at")
58+
op.drop_column("routings", "sub_status")

src/ai/backend/manager/models/routing/row.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
RouteHealthStatus,
2020
RouteInfo,
2121
RouteStatus,
22+
RouteSubStatus,
2223
RouteTrafficStatus,
2324
)
2425
from ai.backend.manager.data.model_serving.types import RoutingData
@@ -114,8 +115,22 @@ class RoutingRow(Base): # type: ignore[misc]
114115
"traffic_status",
115116
StrEnumType(RouteTrafficStatus, use_name=False),
116117
nullable=False,
117-
server_default=sa.text("'active'"),
118-
default=RouteTrafficStatus.ACTIVE,
118+
server_default=sa.text("'inactive'"),
119+
default=RouteTrafficStatus.INACTIVE,
120+
)
121+
sub_status: Mapped[RouteSubStatus | None] = mapped_column(
122+
"sub_status",
123+
StrEnumType(RouteSubStatus, use_name=False),
124+
nullable=True,
125+
default=None,
126+
server_default=sa.text("'pending'"),
127+
)
128+
updated_at: Mapped[datetime] = mapped_column(
129+
"updated_at",
130+
sa.DateTime(timezone=True),
131+
nullable=False,
132+
server_default=sa.text("now()"),
133+
onupdate=sa.func.now(),
119134
)
120135

121136
endpoint_row: Mapped[EndpointRow] = relationship("EndpointRow", back_populates="routings")

src/ai/backend/manager/repositories/deployment/creators/route.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class RouteCreatorSpec(CreatorSpec[RoutingRow]):
3232
project_id: uuid.UUID
3333
revision_id: DeploymentRevisionID
3434
traffic_ratio: float = 1.0
35-
traffic_status: RouteTrafficStatus = RouteTrafficStatus.ACTIVE
35+
traffic_status: RouteTrafficStatus = RouteTrafficStatus.INACTIVE
3636

3737
@override
3838
def build_row(self) -> RoutingRow:

0 commit comments

Comments
 (0)