Repository navigation
Remove legacy Trino runtime in favor of shared pools - #1246
Conversation
Test Impact PlanDeterministic summary of how this PR changes tests, CI runners, and coverage-risk signals. Summary
Signals
Coverage risk: likely reduced Warnings
|
There was a problem hiding this comment.
Reviewed the full diff (130 files, +1.3k/-11k) with the checkout at 2734e20. Focused on what survives rather than what is deleted: the registry/env contract (trino_inputs.go, trino_registry.go), the enablement + ownership path (trino_fleet.go, configstore/trino.go, EnableTrinoInTransaction, assignedCellOrgs), pool wiring and the new catalog-writer requirement (trino_pool_wiring.go, trino_pool_catalog.go), the rollout* → trinoPool* rename in the probe/validate path, the admin fleet API now that legacy is gone, and the rewritten CI fixture (poolfixture, run.sh, trino-controlplane-patch.tmpl.json, e2e/trino.sh).
Things I checked and found sound: the registry decoder keeps DisallowUnknownFields, so a stale backends/catalog_management registry file fails loudly rather than being half-understood; Namespace/CellID are now required at provisioner construction and the registry validates both; EnableTrino still stamps the validated deployment default so enablementCheck's "default exists" branch can't produce a row nobody reconciles; the pool operator refuses to start without the Gateway and the catalog writer, and the writer is still only installed under the won lease; the probe rename is mechanical and keeps the continuation-origin and redirect checks (well covered by the new trino_pool_probe_test.go); unavailableTrinoCatalogClient still refuses with ErrTrinoCatalogNotThisReplica so non-leaders leave state rows alone; no production code references any removed symbol (TrinoCustomerNamespace, DefaultTrinoCellID, ClaimTrinoCell, ErrTrinoNodeInventoryUnavailable); and the e2e fixture's instance/service/deployment names line up with trinopool.instantiate (<id>, <id>-worker) and the /trino/instances payload.
The deleted-test signal from the bot is expected — the removed suites cover removed code. Nothing here is a merge blocker.
P1
- Retired env vars are now silently ignored, so a mis-ordered deploy comes up with Trino completely off.
trinoProvisionerEnabled()keys only onDUCKGRES_TRINO_DEFAULT_CELL/DUCKGRES_TRINO_CELLS_FILE(controlplane/trino_inputs.go:64). A pod that still carries onlyDUCKGRES_TRINO_COORDINATOR_URL/DUCKGRES_TRINO_CELL_ID/DUCKGRES_TRINO_NAMESPACE— i.e. step 3 shipped before the charts change, or the charts change is rolled back — starts cleanly with no fleet:/api/v1/trino/*routes disappear, no cell reconciles, and every enabled warehouse stops being projected, with no error and only the absence of a log line to notice. The rest of this PR is deliberately fail-loud in exactly this situation (unknown registry fields, operator-without-writer, missing namespace/cell id). Rejecting startup when any retiredDUCKGRES_TRINO_*variable is still set would make the documented deployment gate self-enforcing instead of relying on ordering.
P2
- The ownership audit logs a bare count (
controlplane/trino_fleet.go:30-34). The pre-merge gate is "verify no enabled warehouse retains an unassigned or removed owner", but the warning gives an operator no way to find those warehouses — a bounded sample of org ids plus the offendingcell_ids would make it actionable. It also only runs at wiring time, so a row that drifts after startup is never re-reported. wait_trino_poolfails the whole deploy on the first transientpsqlblip (tests/mw-dev/run.sh:70):count="$(kubectl exec ... )" || return 1inside the retry loop. The exec races with ordinary pod churn;continueing and only failing after the 180 attempts are exhausted matches the loop's intent.- The catalog-writer gate swallows its
ParseBoolerror (controlplane/trino_pool_wiring.go:57).DUCKGRES_TRINO_POOL_CATALOG_WRITER_ENABLED=ture(oryes) reports "OPERATOR_ENABLED requires CATALOG_WRITER_ENABLED" even though the operator did set it, which sends them looking in the wrong place. - CI now hard-depends on reading the deployed Gateway (
tests/mw-dev/run.sh:46). Neithere2e-mw-dev.ymlnorscenario-dev.ymlsetsTRINO_GATEWAY_IMAGE, so every Trino lane takes the cluster-read path and requires (a)get deploymentin thetrino-gatewaynamespace for the CI role — not granted by anything in this repo's manifests — and (b) the deployed gateway image to be digest-pinned, orrequire_pool_imagesaborts. The previous shared-catalogs lane was opt-in and never enabled in CI, so this path is newly load-bearing; worth confirming on the first run. TestTrinoSharedPoolNeedsNoLegacyConfigurationasserts nothing about the legacy env (controlplane/trino_legacy_removal_test.go:29-30): it setsDUCKGRES_TRINO_REGISTRY_ONLYandDUCKGRES_TRINO_COORDINATOR_URLto"", which is indistinguishable from unset. Setting them to realistic non-empty values would turn this into the regression test the name promises (and would cover the P1 above if that guard lands).- The mount-lag classifier is now unreachable.
tenantSecretNotMountedYet(controlplane/provisioner/trino_provisioner.go:1843) matches*TrinoStatementError, which nothing constructs any more — the HTTP catalog clients that produced it are gone and the pool writer publishes through the store. The PR calls this out as intentionally retained, so just confirming the intent: the "tenant secret not mounted yet → Pending, not Failed" behavior it provided now has to come entirely from the pool's tenant-admission gate.
Inline comments
controlplane/trino_inputs.go:65— P1: with the coordinator vars gone from this predicate, a pod that still carries onlyDUCKGRES_TRINO_COORDINATOR_URL/DUCKGRES_TRINO_CELL_ID(charts change not yet applied, or rolled back) starts with no Trino fleet at all: admin/trino/*routes absent, no cell reconciling, every enabled warehouse silently unprojected — no error, just a missing log line. Everything else in this PR fails loudly in the same situation (unknown registry fields, operator-without-writer, missing namespace/cell id). Consider erroring at startup when any retiredDUCKGRES_TRINO_*variable is still set, so the documented deployment ordering enforces itself.controlplane/trino_fleet.go:33— P2: this is the signal for the pre-merge gate ("verify no enabled warehouse retains an unassigned or removed owner"), but a bare count gives the operator nothing to act on. A bounded sample of org ids plus the offendingcell_ids would make it directly actionable. Also note this only runs at wiring time, so a row that drifts later is never re-reported.controlplane/trino_pool_wiring.go:57— P2: theParseBoolerror is dropped, soDUCKGRES_TRINO_POOL_CATALOG_WRITER_ENABLED=ture(oryes) is reported as "OPERATOR_ENABLED requires CATALOG_WRITER_ENABLED" even though the operator did set it. Surfacing the parse failure points at the actual problem.tests/mw-dev/run.sh:70— P2: one transientkubectl exec/psqlfailure aborts the whole deploy instead of retrying — the exec races with ordinary pod churn, and this loop gates the entire Trino lane.continueon error and fail only once the 180 attempts are exhausted.tests/mw-dev/run.sh:48— P2: neithere2e-mw-dev.ymlnorscenario-dev.ymlsetsTRINO_GATEWAY_IMAGE, so every Trino lane now takes this path. It needsget deploymentin thetrino-gatewaynamespace for the CI role (nothing in this repo's manifests grants it) and a digest-pinned image on that deployment, orrequire_pool_imagesaborts the run. The old shared-catalogs lane was opt-in and never enabled in CI, so this is newly load-bearing — worth watching the first run.controlplane/trino_legacy_removal_test.go:29— P2: setting these to""is indistinguishable from unset, so the test doesn't actually assert that legacy configuration can't resurrect the old path. Realistic non-empty values would make the name true (and would cover the silent-disable case if a startup guard for retired vars lands).
— Robo Bill v2 (opus, high reasoning)
There was a problem hiding this comment.
Re-reviewed at 975458e, focused on the delta since my review of 2734e20 (one commit, 8 files: the mount-lag classifier removal in trino_provisioner.go, the new pool alert metrics in trino_pool_observability.go + its wiring in multitenant.go/trino_pool_operator.go, the new tests, and the docs/trino-cells.md section). The rest of the PR is unchanged from what I already reviewed.
This commit closes out two of my earlier P2s, and does it properly:
- Retired retry code is gone, not just unreachable.
TrinoStatementError,tenantSecretNotMountedYetandtrinoSecretMountLagErrorare fully removed, no production or test reference survives (trinoDuckLakePasswordFilePropertyis still used to build catalog properties, anderrors/stringsare still needed in the file, so no dead imports). The replacementTestReconcile_CatalogPublicationFailureRecoversOnNextTickis a real regression test: it covers both a plain error andcontext.DeadlineExceeded, asserts the error isn't swallowed, thatfailed_atis stamped, and that the next tick recovers to Ready. Worth noting that removing the onlyPendingbranch inreconcileBackendCatalogs's create path doesn't widen the failure blast radius — theexistingSet[name]shortcut still returnsExistedbeforeCreateCatalogis reached, so an already-published org can't be flipped to Failed by a publisher blip, and the all-orgs-Failed-on-ListCatalogs-error path is unchanged and pre-existing. - The new metrics are carefully scoped.
snapshotsetsminServing/servingInstancesonly after theowners[pool] != oownership check, so a stale term can't overwrite a newer one;cleardeletes both byDeletePartialMatchonpool, andend()is ownership-guarded so an old term'send()can't wipe the new term's samples (covered byTestTrinoPoolServingInventoryMetricsFollowSnapshotAndTerm). The departed-identity pruning diffs against the previouso.servingset rather than resetting, so unchanged series stay present across scrapes. Label order inDeleteLabelValuesmatchesWithLabelValuesand theGaugeVecdeclaration. The test explicitly asserts noinstance/namespacelabel collides with scrape identity —pool_instance/workload_namespaceare the right choice. Cardinality is bounded by live pool membership.configurePoolsandsnapshotsharem.muand touch disjoint gauges, so the startup call racing an early-winning operator loop is safe.
Nothing here is a merge blocker.
P2
- A failed tableflip reload permanently drops
duckgres_trino_pool_configuredwhile the process keeps serving metrics.apiServer.RegisterOnShutdown(...)(controlplane/multitenant.go:889) is the only consumer of the cleanup closure, andcp.apiServer.Shutdownhas exactly one call site:handleUpgrade(controlplane/control.go:2727). On SIGTERM the hook never runs at all, which is harmless. But ifupg.Upgrade()fails,recoverAfterFailedReloadexplicitly stands a fresh metrics server back up (controlplane/control.go:2875) while the API server is not recovered andconfigurePoolsis never re-run — so the process goes on reconciling pools and exportingserving_instance_info/min_serving/snapshot_timestamp, with the configured expectation series gone for good. That blinds exactly the alert the docs describe ("Alert separately when an expected configured pool has no fresh telemetry"): the expectation vanishes, so the alert stops evaluating instead of firing. CallingconfigurePoolsagain fromrecoverAfterFailedReload, or tying the reset to process exit rather than tohttp.Server.Shutdown, would make the metric's lifetime match the sentence in the docs. duckgres_trino_pool_configuredis gated onoperatorEnabled, which the doc wording doesn't say.controlplane/trino_pool_observability.go:78sets the gauge only for operators withoperatorEnabledtrue — i.e.DUCKGRES_TRINO_POOL_OPERATOR_ENABLED, not "is this pool in the registry".docs/trino-cells.md:117-119says "enabled pools from this process's startup registry, independently of operator ownership", which reads as if it's registry-derived; "ownership" (lease authority) and "enabled" (the env flag) are different things here. In the read-only-operator deployment the PR description says stays supported, the metric is absent entirely, so it can't distinguish "pool configured, operator deliberately off" from "pool not configured" — which matters because the alerts in charts #16533 are being written against this. One sentence in the doc pinning it to the operator flag would remove the ambiguity.- The
minNamespacere-label bookkeeping can never fire.controlplane/trino_pool_observability.go:167-170deletes the previousminServingnamespace label when it changes, but thenamespaceargument is alwayso.config.Namespace(trino_pool_operator.go:356), which is read from the registry at startup and never mutated for the operator's lifetime; a telemetry token also never outlives its term, andclearwipes the pool's samples by partial match at term boundaries anyway. It's harmless, but it's three lines of state implying a namespace change that the current wiring can't produce. TestTrinoRejectsRetiredCoordinatorCatalogRetryCodeonly greps one file.controlplane/trino_legacy_removal_test.go:14readsprovisioner/trino_provisioner.goby relative path, so the guard passes if the symbols are reintroduced in any sibling file in the same package (trino_catalog_client.go, etc.) — which is the likelier shape of a regression, since the type was a catalog-client concern. Walking theprovisionerpackage's.gofiles would make the assertion match the test's name. (Grepping source text will also trip on a passing mention in a comment, but that's the conservative direction.)
Inline comments
controlplane/multitenant.go:889— P2: the cleanup closure only runs fromhttp.Server.Shutdown, whose single call site ishandleUpgrade(controlplane/control.go:2727). On SIGTERM it never runs (harmless), but on a failed tableflip upgraderecoverAfterFailedReloadstands a fresh metrics server back up (control.go:2875) whileconfigurePoolsis never re-run — so the process keeps reconciling and exportingserving_instance_info/min_serving/snapshot_timestampwithduckgres_trino_pool_configuredgone permanently. That silently blinds the "expected configured pool has no fresh telemetry" alert the docs describe, rather than firing it. Re-runningconfigurePoolsfromrecoverAfterFailedReload, or tying the reset to process exit instead of API-server shutdown, would match the documented lifetime.controlplane/trino_pool_observability.go:78— P2: this gates onoperatorEnabled(theDUCKGRES_TRINO_POOL_OPERATOR_ENABLEDflag), butdocs/trino-cells.md:117-119describes the metric as coming "from this process's startup registry, independently of operator ownership" — ownership (lease authority) and enablement (the env flag) are different things. In the read-only-operator deployment the PR description keeps supporting, the series is absent entirely, so it can't distinguish "configured, operator deliberately off" from "not configured". Worth pinning down in the doc before charts #16533 builds alerts on it.controlplane/trino_pool_observability.go:167— P2: this branch is unreachable.namespaceis alwayso.config.Namespace(trino_pool_operator.go:356), read from the registry at startup and never mutated for the operator's lifetime; a telemetry token never outlives its term, andclearalready wipes the pool'sminServingsamples by partial match at term boundaries. Harmless, but it implies a namespace change the current wiring can't produce.controlplane/trino_legacy_removal_test.go:14— P2: this only readsprovisioner/trino_provisioner.go, so the guard passes ifTrinoStatementError/tenantSecretNotMountedYetcome back in any sibling file of the same package — which is the likelier shape of a regression, given the type was a catalog-client concern. Walking the package's.gofiles would make the assertion match the test's name.
— Robo Bill v2 (opus, high reasoning)
#1246 removed cell.coordinator_url from GET /api/v1/orgs/:id/trino; pooled cells advertise their client endpoint (the Gateway) as status.connection instead. The scenario perf step still required the removed field, so the nightly posthog_frozen_perf run failed at perf_queries with "has no coordinator URL" even though the stack was healthy. Build the Trino server URL from status.connection host/port, the same source tests/mw-dev/e2e/trino.sh already uses. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Remove the retired standalone and fixed blue/green Trino runtime. The only supported configured topology is now the Duckgres-managed shared compute pool.
Deployment order
Before step 3, verify no enabled warehouse retains an unassigned or removed owner. New warehouse provisioning that enables Trino needs a configured default pool. These are explicit operator gates; this PR does not reassign, disable, or delete customer warehouses.
The existing Gateway token/username/server-name settings remain because the shared-pool client still uses them. Deploy this binary before the final charts cleanup and pool-aware alerts in charts #16533.
Validation
just test-mw-fixtures: all eight fixture/scenario packages, including blueprint validation and image-resolution contracts.just lint, Kubernetes-tagged changed-code lint, shell syntax checks, andgit diff --check.Not run locally: Docker-bound admin PostgreSQL tests (Docker unavailable), or live Kubernetes end-to-end deployment. The updated CI fixture must validate the full deployed path before rollout.