Rearchitect the warehouse executor: named backends, one route resolver, structural ingest routing#231
Merged
Merged
Conversation
…ackends and route-as-data
The three warehouse paths (managed Tinybird, Tinybird CH-gateway, BYO
ClickHouse) were encoded as _tag×provider flag combinations branched on
across two layers, with route selection split between the package
executor and four host resolvers, ingest pinning enforced by a
per-call-site boolean, three drifting option types, duplicate facade
spans, a 500-flatten on observability errors, and a hand-copied CLI
executor.
- Name the backends: WarehouseBackendKind + one BackendDialect behavior
table (driver, settings strip, SQL normalize, peer service); delete
the _tag/provider dual discriminator.
- Collapse the four injected resolvers into one resolveRoute(tenant,
purpose, label) returning route-as-data (source, config, cache key);
raw-SQL isolation invariants and cache-key strings preserved verbatim.
- Make ingest routing structural: .routing("ingest") declared at the
query definition rides CompiledQuery; delete pinToIngestConfig; warn
when a BYO route reads an ingest-pinned table.
- Unify on one SqlQueryOptions (profiles), delete ExecutorQuery* dupes,
drop the duplicate WarehouseExecutor.* span wrappers; executeSql adds
warehouse.backend / warehouse.route / warehouse.config_source (legacy
attrs dual-emitted for one release).
- Surface warehouse errors with their canonical statuses on the
observability endpoints (429 quota / 400 validation / 503 transient
instead of a blanket 500); map quota/validation properly in the v2
alerts error map; classify ingest failures.
- CLI local mode reuses the real executor via a chdb dialect + constant
route, deleting the ~190-line hand-rolled copy; /local/query returns
400 for failing SQL so the executor doesn't retry it; the raw-SQL
escape hatch posts directly to /local/query (single-tenant, no OrgId
guard weakening).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
|
Your Pullfrog Router balance is exhausted. You have a card on file but auto-reload is disabled, so runs paused once your balance went past the overdraft buffer. Top up balance → · Enable auto-reload →
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Why
The query-execution stack had grown hard to trace across the three backends (managed Tinybird, Tinybird's ClickHouse gateway, BYO self-hosted ClickHouse):
_tag×provider), branched on in ~6 places across the package executor and the host wiring. Nothing in the code named the three deployments.pinToIngestConfigboolean — forgetting it on a new read of an ingest-only datasource silently returned empty rows for BYO-CH orgs.ExecutorQueryOptionshad already drifted), plus a duplicateWarehouseExecutor.*span nested around every canonicalexecuteSqlspan.upstreamError.What changed
One backend enum + one behavior table —
WarehouseBackendKind = "tinybird" | "tinybird-gateway" | "clickhouse" | "chdb"with a singleBackendDialecttable (execution/backend.ts): driver, Tinybird settings-strip policy, SQL normalization, peer-service label. All scattered branches became table lookups. The gateway keepspeerService: "clickhouse"deliberately so service-map DB nodes don't churn.Route-as-data — the executor computes a purpose (
read/raw/ingest) and calls a single injectedresolveRoute, implemented as one ordered decision function inWarehouseQueryService.ts. Raw-SQL isolation is preserved verbatim (textual OrgId filter in the executor, org-scoped JWT for shared Tinybird, per-org creds for BYO,self_hosted-only gate for shared vanilla CH), andclientCacheKeystrings are byte-identical so warm clients aren't churned.Structural ingest routing — control-plane queries declare
.routing("ingest")at their definition (new chain on the CH builder, carried onCompiledQuery);alert_checks+ the cross-org activity scans are tagged. Hand-written SQL usesSqlQueryOptions.route: "ingest"(the only runtime-conditional site is the Cloudflare usage read, gated on write-readiness). A safety net warns when a BYO route reads an ingest-pinned table.pinToIngestConfigis deleted.One option type, one span —
SqlQueryOptionslives next to the profiles and is shared by the executor, the observability facade, and the runtime port; theExecutorQuery*duplicates are deleted.asExecutorno longer wraps duplicate spans —WarehouseQueryService.executeSqlis the single client span, now also carryingwarehouse.backend,warehouse.route, andwarehouse.config_sourceso a trace answers "which path and why" in one place. Legacyquery.routing/clientSource/db.clientare dual-emitted for one release.Canonical error statuses — observability endpoints declare
warehouseHttpErrors, so quota surfaces as 429, validation as 400, transient as 503 instead of a blanket 500 (themapErrorflatten is gone). The v2 alerts mapper sends quota →rateLimited()and validation →invalidRequest.ingestfailures are classified withmapWarehouseError.CLI local mode runs the real executor — a
chdbdialect row + constant local route replace the hand-rolled shape (deleted)./local/queryreturns 400 (not 500) for failing SQL so the shared executor doesn't classify it as transient and retry. The raw-SQL escape hatch (maple query) posts directly to/local/query: arbitrary local SQL has no OrgId, and the executor's scoping guard must not weaken.Net −59 lines of production code despite ~130 lines of new tests.
Reviewer notes
ObservabilityApiErrorby name, so no web edits were needed). Dashboards filtering on the legacy span attributes should migrate towarehouse.*before the dual-emit is removed.lib/clickhouse-buildergained the.routing("ingest")chain — its dist is rebuilt as part of this change.warehouse.sqlQuery→WarehouseQueryService.executeSql(local mode only).🤖 Generated with Claude Code
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.