lablup / lablup/backend.ai-webui

Spec: Visualize replica-group deployment strategies (rolling / blue-green) and replica transitions

Open
#7,657 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
133
Forks
81
Avg merge
1d 12h
Merged PRs (30d)
355

Description

## Why this work is needed

A replica-group / deployment-strategy model is being introduced for model-serving deployments (backend **BA-6233**, `feat: introduce replica groups owning deployment revision pointers`, manager commit `56c41c2ff`, PR #11871). We want to visualize how replicas shift from the old revision to the new one, how traffic moves between them, and where a blue-green / rolling update currently stands.

Goal of this issue: gather competitor research, pin down the actual backend/API scope, and list open design questions before writing the formal spec.

## Backend scope — internal restructuring only (important)

**BA-6233 is an internal backend change. Replica groups are NOT being exposed in the API, and there is no plan to expose them.** Internally, revision pointers moved off the deployment (endpoint) onto a new `replica_group` entity, and a deployment can own multiple replica groups (the mechanism behind blue-green and future canary):

- `Endpoint` (deployment) now holds `primary_replica_group_id` (serving) and `target_replica_group_id` (rolling out); the old `current_revision` / `deploying_revision` columns were removed.
- `ReplicaGroup` (new, internal) owns `current_revision_id`, `target_revision_id`, `desired_current_replica_count`, `desired_target_replica_count`, `traffic_weight`.
- `Routing` (route) gained `replica_group_id` internally.

**None of these replica-group fields are surfaced in GraphQL, by design.** From the frontend's point of view the deployment shape is essentially unchanged.

### The one planned API change: a new traffic-status enum value

The **only** API-surface change planned alongside this work is **adding one new value to the traffic-status enum**. Today both relevant enums expose only `ACTIVE | INACTIVE`:

- `TrafficStatus` (per replica) — `data/schema.graphql`, `ModelReplica.trafficStatus`.
- `RouteTrafficStatus` (per route) — `data/schema.graphql`, on `Route` / `updateRouteTrafficStatus`.

A third value is expected (to distinguish replicas/routes that are serving live traffic from those being prepared/previewed during a rollout — i.e. the active-vs-preview distinction the internal replica groups create). This enum value is what lets the FE tell apart the two sides of a blue-green / rolling transition **without** ever seeing the replica groups themselves.

> Backend repo references (`/home/ubuntu/backend.ai`): `models/replica_group/row.py`, `models/endpoint/row.py`, `models/routing/row.py`, `common/data/model_deployment/types.py` (`DeploymentStrategy = ROLLING | BLUE_GREEN`; `RouteTrafficStatus = ACTIVE | INACTIVE`). The new enum value is not yet committed — confirm its exact name/semantics and which enum(s) receive it before building against it.

## What the FE actually has to work with

This visualization is built on the **existing GraphQL surface plus the one new enum value** — not on replica-group structures. Available fields:

- `ModelDeployment.currentRevisionId` vs `deployingRevisionId` (now resolved internally via the groups, but the FE shape is unchanged) → current vs in-flight revision.
- `DeploymentStrategyType` = `ROLLING | BLUE_GREEN` (CANARY not implemented).
- `ModelReplica.{status, healthStatus, activenessStatus, trafficStatus`} → per-replica state, including the new traffic-status value.
- `Route.trafficRatio` + route `trafficStatus` → per-route traffic share and on/off state.

So the active/preview lane split, the "old shrinks / new grows" transition, and the traffic on/off state are all expressible from existing fields + the new enum value. What is genuinely **not** available (and intentionally so) is a per-group object model: there is no `ReplicaGroup` GraphQL type, no per-group `traffic_weight`, no weighted N-way split, no `replica_group_id` on routes. A multi-group weighted/canary UI would need a separate future backend decision — it is out of scope here, not merely "pending."

## Current state in the webui codebase

`data/schema.graphql` already exposes `DeploymentStrategyType`, `currentRevision`/`deployingRevision`, `ModelReplica.{status,healthStatus,activenessStatus,trafficStatus`}, and `Route.trafficRatio` — none of which is visualized today. UI surfaces with no strategy/traffic visualization:

- `react/src/pages/DeploymentDetailPage.tsx` — tabs: revisions, replicas, auto-scaling, access tokens.
- `react/src/components/DeploymentReplicasTab.tsx` — flat replica table; no per-revision grouping or transition view; `trafficStatus` not shown.
- `react/src/components/DeploymentRevisionHistoryTab.tsx` — revision list + "Activate"; no rollout progress.
- `react/src/components/DeploymentSettingModal.tsx` — strategy selection + replica count.
- `Route.trafficRatio` is **not displayed anywhere**.

## How other tools visualize this (competitor research)

Recurring UI idioms across Argo Rollouts, Cloud Run, KServe/Knative, Spinnaker, AWS CodeDeploy, Flagger, Vercel:

1. **Revision-as-a-card** — each immutable version rendered as a card with id, image/tag, status. Universal.
1. **Replicas/pods as discrete glyphs** inside a card (Argo pod dots, Spinnaker instance squares); green-glyph count ≈ live capacity. Maps onto our replica list grouped by revision.
1. **Traffic share is always an explicit number** — gauge (Flagger), %-column (Cloud Run), `setWeight` vs `actualWeight` (Argo), PREV/LATEST (KServe). Best practice: show **configured vs actual** separately. Our analogue is `Route.trafficRatio` + the new traffic-status value (rather than per-group weights).
1. **Step/stage timeline with gates** — current step highlighted; a pause renders as a blocked node with a **Promote** button (+ countdown). Matches BlueGreen autoPromote / promoteDelaySeconds.
1. **Active vs preview distinction** via labeled lanes + color (Argo blue/green, Spinnaker red/black, Cloud Run tagged test URLs) → this is exactly what the **new traffic-status enum value** lets us render: active replicas/routes vs preview/standby ones.
1. **Old shrinks / new grows over time** — canonical rolling-update animation → maps to replica counts per revision (current vs deploying).
1. **Rollback as a first-class chooser** (Vercel instant-rollback, Cloud Run set-prior-to-100%, KServe PreviousRolledoutRevision).
1. **Two complementary views** — topology/structure ("state now") + time-series metrics ("is it healthy", Flagger+Grafana).

Closest references for a model-serving UI: **Argo Rollouts** (cards + pod glyphs + step timeline), **Cloud Run** (traffic-split readout, tagged preview), **KServe/Knative** (Latest/Previous model), **Flagger+Grafana** (metrics strip). Full source URLs in the competitor-research notes.

## Recommended UI directions (to refine in the spec)

Built on existing GraphQL + the new traffic-status value (no replica-group queries):

- `RevisionTransitionView` — current-revision card and deploying-revision card side by side, each with an animated replica-dot grid (ready / pending / unhealthy / terminating) and a `ready / desired` fraction. Which card is "active" vs "preview" is driven by the new traffic-status value on its replicas/routes.
- `TrafficStateBar` — shows the active vs preview split using route `trafficStatus` (+ `trafficRatio` where meaningful), with a configured-vs-actual readout.
- Per-strategy specialization:
- Rolling — single "new grows / old shrinks" view from current-vs-deploying replica counts; maxSurge / maxUnavailable annotated.
- Blue-green — active (primary) / preview (target) swimlanes keyed off the traffic-status value, a prominent "Promote preview → active" action, autoPromote / promoteDelaySeconds countdown.
- Canary — out of scope (no weighted multi-group API); note as a future direction only.
- `RolloutHistoryList` + `RollbackChooser` — chronological revisions with outcome and one-click rollback.
- Optional `RolloutMetricsStrip` — success-rate / latency / volume sparklines with deploy-event annotations.

## Open questions to resolve in the spec

- **Exact name + semantics of the new traffic-status value**, and whether it lands on `TrafficStatus` (replica), `RouteTrafficStatus` (route), or both. This is the single API dependency for the whole active/preview visualization — confirm with backend.
- Can the FE infer rollout **progress** (which revision is active vs preview, how far the transition has gone) purely from per-replica/route `trafficStatus` + counts, or is an explicit progress/step field also needed? Determines whether a step timeline is buildable now.
- Is there a **preview/test endpoint** for the in-flight (target) side, for the "preview URL" idiom?
- Is **CANARY** genuinely on the roadmap? Enum is ROLLING + BLUE_GREEN only and there is no weighted-split API; treat canary as future/out-of-scope unless backend says otherwise.
- Placement: new tab on `DeploymentDetailPage`, an enhanced Replicas tab, or a dedicated "Rollout" view shown only while `deployingRevisionId != null` / `scalingState == SCALING`?
- Real-time updates: polling vs subscription for the live transition animation.

## Expected outcomes

- Shared understanding that BA-6233 is **internal-only**, with a single new traffic-status enum value as the entire FE-facing API delta.
- Competitor patterns captured (above), mapped onto fields the FE can actually query.
- A component breakdown built on existing GraphQL + the new enum value, with **canary / weighted multi-group explicitly out of scope** (not "pending backend") until a separate decision.
- One precise `TODO(needs-backend)`: confirm the new traffic-status enum value (name, semantics, which enum) — everything else is buildable on the current schema.

JIRA Issue: FR-3012

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.