feat(trino): move an owned org to another Trino cell - #1223
Conversation
Initial selection deliberately refuses to change an org that a cell
already owns, so there was no supported way to move an org off the legacy
cell onto a shared pool.
POST /api/v1/orgs/:id/trino/cell/move {"from","to"} (admin-only, audited)
calls ConfigStore.MoveTrinoCell: a compare-and-swap on the named source
under the org's admission lock that reassigns the row and resets it to
pending with readiness cleared. The existing projections do the rest: the
source's authoritative reconcile removes the org's catalog, password and
policy, and the destination provisions it like a new assignment.
Per-org state writes now carry the owning cell id, so a source tick that
listed the org before the move cannot stamp "ready" onto the moved row.
There is no overlap: the org has no Trino between the source's cleanup and
the destination's readiness. docs/trino-cells.md says so plainly.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nic9bR3LPTRLk56ntJ4M5Z
Test Impact PlanDeterministic summary of how this PR changes tests, CI runners, and coverage-risk signals. Summary
Signals
Coverage risk: neutral or increased No coverage-reduction warnings detected. |
There was a problem hiding this comment.
No P0 or blocking findings; approving. Non-blocking test robustness note: the opt-in E2E asserts the org is not ready immediately after the move request. A fast reconcile could make it ready before that GET, causing a flaky failure; consider asserting the pending transition through a controlled pause/barrier, or make the assertion tolerate the destination already reaching ready.
The documented Trino outage during source cleanup and destination provisioning is explicit and accepted, and the change stays within the requested move flow.
— Robo Bill
Problem
PUT /orgs/:id/trino/cellonly does initial selection. Once an org belongs to a cell, it can't move. That leaves no supported way to take an org off the legacy Trino and onto a shared pool. The immediate need is PostHog's own org: Metabase should reach it onposthog.dw.us.postwh.com(tenant host → Gateway → pool) with a baremetabaselogin.docs/trino-cells.mdtreated a move as a future live migration: source admission barrier, verified drain, and so on. This PR ships a smaller, explicit move that accepts a short Trino outage for the moved org. Only Trino is affected; pgwire isn't touched.Change
POST /api/v1/orgs/:id/trino/cell/move {"from":"legacy","to":"cell-001"}from, 404 if it has no Trino row, and 409 if Hoglake lifecycle protection applies.ConfigStore.MoveTrinoCell(org, from, to), in one transaction:trino_cell_id = from. Moving to the cell that already owns the org is a no-op.trino_cell_id = to,state = pending, clearsready_at/failed_at, and sets a status message.TrinoStateUpdate.CellIDscopesUpdateTrinoStateto the owning cell. The provisioner always sets it. Without it, a source tick that listed the org before the move could mark the moved rowreadybefore the destination has provisioned it.password.dblines, OPA group. The destination provisions it like a new assignment. A pool with tenant admission holds the org at provisioning until the pool has published it.The outage this accepts
Tests
Postgres-backed (
tests/configstore/trino_selection_postgres_test.go):Provisioner: the source cell drops the moved org's catalog, password and
password.dbline, writes no state for it, and fences every state write to its own cell id.Admin handler: admin-only; unknown, same or missing cells give 400; the store receives stored ids; conflict and not-found map to 409 and 404.
e2e: new opt-in
trino_cell_moveintests/mw-dev/e2e/harness.sh, enabled withE2E_TRINO_CELL_MOVE_ORGandE2E_TRINO_CELL_MOVE_TO. It moves an org to another cell and back and, on each leg, asserts:It's opt-in because it needs a pool-enabled cluster and a real Trino org. I'll run it on mw-dev before moving PostHog's org.
go test -tags kubernetes ./controlplane/ ./controlplane/admin/ ./controlplane/provisioner/andgo test ./tests/configstore/ -run Trinopass locally.Docs:
docs/trino-cells.md(the "Moving an existing warehouse" section replaces the migration follow-up) andCLAUDE.md.Related: #1222 (pool observer). The two are independent, but a moved pool org only advertises its connection once #1222 is in.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Nic9bR3LPTRLk56ntJ4M5Z