Skip to content

feat(trino): move an owned org to another Trino cell - #1223

Merged
fuziontech merged 2 commits into
mainfrom
james/trino-cell-move
Sep 23, 2026
Merged

fuziontech merged 2 commits into
mainfrom
james/trino-cell-move

Conversation

@fuziontech

Copy link
Copy Markdown
Member

Problem

PUT /orgs/:id/trino/cell only 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 on posthog.dw.us.postwh.com (tenant host → Gateway → pool) with a bare metabase login.

docs/trino-cells.md treated 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"}
    • Admin-only, and audited by the existing middleware.
    • Both cells are named by console id and must be configured and different.
    • Returns 409 if the org isn't on from, 404 if it has no Trino row, and 409 if Hoglake lifecycle protection applies.
  • ConfigStore.MoveTrinoCell(org, from, to), in one transaction:
    • Takes the org admission lock, runs the Hoglake guard, then locks the row.
    • Compare-and-swap: it proceeds only while trino_cell_id = from. Moving to the cell that already owns the org is a no-op.
    • Sets trino_cell_id = to, state = pending, clears ready_at/failed_at, and sets a status message.
  • Fencing state writes: TrinoStateUpdate.CellID scopes UpdateTrinoState to the owning cell. The provisioner always sets it. Without it, a source tick that listed the org before the move could mark the moved row ready before the destination has provisioned it.
  • Nothing else needs to change. The source's authoritative projection already removes an org that isn't in its wanted set: catalog, tenant password, password.db lines, 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

  • The moved org has no Trino between the source's cleanup and the destination's readiness, typically a reconcile tick or two.
  • Queries running on the source when its catalog is dropped fail.
  • Direct clients of the source endpoint stop working for that org.
  • The docs say this and point at what a live handover would still need.

Tests

  • Postgres-backed (tests/configstore/trino_selection_postgres_test.go):

    • wrong-source conflict, and not-found
    • readiness reset and idempotence
    • a stale source write fenced out while the destination write lands
    • legacy can't reclaim the moved org
    • moving back works
    • of two moves racing from the same source, exactly one wins
  • Provisioner: the source cell drops the moved org's catalog, password and password.db line, 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_move in tests/mw-dev/e2e/harness.sh, enabled with E2E_TRINO_CELL_MOVE_ORG and E2E_TRINO_CELL_MOVE_TO. It moves an org to another cell and back and, on each leg, asserts:

    • a request naming a non-owner source gets 409
    • the org is pending on the destination
    • it becomes ready there
    • repeating the move is a no-op

    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/ and go test ./tests/configstore/ -run Trino pass locally.

Docs: docs/trino-cells.md (the "Moving an existing warehouse" section replaces the migration follow-up) and CLAUDE.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

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
@fuziontech
fuziontech requested review from a team and benben September 23, 2026 20:39
@github-actions

Copy link
Copy Markdown

Test Impact Plan

Deterministic summary of how this PR changes tests, CI runners, and coverage-risk signals.

Summary

Area Added Changed Deleted
Test files 0 4 0
E2E/journey files 0 0 0
Workflow files 0 0 0

Signals

  • Test cases: +4 / -0
  • Assertions: +45 / -0
  • Skips or known failures added: 0
  • Workflow continue-on-error added: 0
  • Workflow path filters added: 0
  • Test commands removed from justfile: 0
  • E2E/journey retry lines added: 0

Coverage risk: neutral or increased

No coverage-reduction warnings detected.

@bill-ph bill-ph left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@fuziontech
fuziontech enabled auto-merge (squash) September 23, 2026 21:16
@fuziontech
fuziontech merged commit 197f3b6 into main Sep 23, 2026
22 checks passed
@fuziontech
fuziontech deleted the james/trino-cell-move branch September 23, 2026 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants