Skip to content

perf(snowflake): plan from SHOW metadata and run inspection reads concurrently - #896

Merged
kvlonge merged 7 commits into
mainfrom
perf/chi-455-snowflake-inspection
Oct 1, 2026
Merged

kvlonge merged 7 commits into
mainfrom
perf/chi-455-snowflake-inspection

Conversation

@kvlonge

@kvlonge kvlonge commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Why

On Snowflake, sqb plan spent most of its time in warehouse inspection: serial INFORMATION_SCHEMA queries with IN lists of every relation (one took 35 s), repeated table-list reads, and about 200 cursor-bound queries one at a time. INFORMATION_SCHEMA also needs a running warehouse and queues behind other work: on a busy shared warehouse a 1 s query waited 6 s (CHI-455 items 5-6).

Changes

  • Snowflake planning metadata uses SHOW TABLES, SHOW VIEWS and SHOW COLUMNS IN SCHEMA (metadata service, no warehouse), once per schema, schemas in parallel (8). JSON column types map to the existing type strings; exact-cap and missing schema/database cases handled.
  • Freshness LAST_ALTERED reads only the needed sources, at most 50 names per query, in parallel.
  • Cursor-bound queries run 8 at a time (one statement per relation, no UNION ALL, no caching across runs).
  • Each schema's table list is read once per plan and reused; no IN list over 50 names.
  • sqb plan -v / sqb build -v log every inspection query (SQL, time, rows) to stderr.
  • CI guards: query budget per schema, IN-list cap, metadata SQL only in adapter inspection modules, cursor-bound concurrency.

Verification

  • Read-only check on a real Snowflake account: 0 differences vs INFORMATION_SCHEMA for 3,036 relations and ~83,000 columns; a not-yet-created target schema plans as first run.
  • Real ~1,350-file project, sqb plan --as prod --no-cache: planning 25-30 s over three runs (was ~135 s); compile excluded.
  • Synthetic plan output byte-identical to main; focused adapter, planner and Snowflake tests; make test, make check-ci; one independent review plus a follow-up.

Add a shared InspectionCatalog that serves list_relations and
get_columns_for_relations requests for one planning invocation.

Adapters implementing SchemaScopedMetadataReader (Snowflake) read each
schema's table list and column list at most once with unfiltered
INFORMATION_SCHEMA queries and filter client-side using the stored
identifier case, so no metadata query carries a large IN list. Sparse
column requests keep exact SHOW COLUMNS reads. Independent schema reads
run with bounded concurrency on per-thread cursors. Other adapters keep
their request shapes; identical requests are memoized.

Snowflake source freshness reuses the catalog's schema listing when a
catalog is open, and every inspection read can be recorded for verbose
output.

Refs CHI-455
Open one inspection catalog per execution plan and route snapshot,
migration-state, source-column, and semantic-source metadata lookups
through it, so each schema is listed once and state-table and source
lookups reuse the listing. Target and source schemas are prefetched
together (best effort; a failed speculative read is re-read and reported
by the lookup that needs it). Snapshot columns are read only for listed
relations that planning looks up by node name.

Cursor-bound MIN/MAX reads stay one statement per relation and run with
the adapter's bounded inspection concurrency (8 on Snowflake, sequential
elsewhere); results merge in query order and a failed read still leaves
only that relation unavailable. Bounded eligible-max reads fail the plan
naming the model and relation, as before.

Adds a recording-warehouse budget guard over a synthetic 4-schema
project, an equivalence check against the direct adapter path, and an
architecture guard keeping catalog SQL in adapter modules.

Refs CHI-455
With -v, sqb plan and sqb build write each planning inspection read
(truncated SQL, elapsed time, row count or error) and a final total to
stderr, keeping JSON stdout machine readable.

Refs CHI-455
…MATION_SCHEMA

INFORMATION_SCHEMA queries need a running warehouse and queue behind
other work on a shared warehouse; SHOW commands are served by the
metadata service. Planning schema listings now use SHOW TABLES and
SHOW VIEWS (paged past the 10,000-row cap) and SHOW COLUMNS IN SCHEMA,
mapped onto the same RelationInfo and type strings the
INFORMATION_SCHEMA path produced. A missing or unauthorized schema lists
nothing, as before.

SHOW COLUMNS silently truncates at 10,000 rows, so a schema that
reaches the cap is reread from INFORMATION_SCHEMA.COLUMNS. SHOW has no
LAST_ALTERED, so source freshness reads it only for observed sources,
in IN lists of at most 50 names run with bounded concurrency.

SHOW COLUMNS type translation lives in one helper shared with the
per-relation path and is checked against INFORMATION_SCHEMA output for
every Snowflake type family. The budget guard now requires SHOW reads
per schema and no INFORMATION_SCHEMA reads beyond freshness.

Refs CHI-455
SHOW COLUMNS IN SCHEMA returned every row on a real account well past
10,000, so a result is now reread from INFORMATION_SCHEMA only when it is
exactly 10,000 rows. SHOW TABLES/VIEWS paging already continues only on
a full 10,000-row page.

Views and materialized views now report is_transient as None, matching
INFORMATION_SCHEMA.TABLES.

Refs CHI-455
- Semantic source fallback lists candidates by name in every schema and
  matches schemas case-insensitively, as before the catalog; schema-scoped
  catalogs answer declared schemas from listings first.
- Bounded inspections cancel queued reads and re-raise promptly on any
  exception in the coordinating thread (including Ctrl-C); after a read
  fails, running reads finish and the lowest-index failure is raised.
- Snowflake SHOW is always qualified with a database (the session's when
  none is given); only a missing schema lists empty, a missing database
  fails as before.
- Column reads choose per-relation SHOW COLUMNS by the request's total
  size, as before, not per schema; a relation dropped after listing has
  no columns instead of failing.
- A cap-sized SHOW COLUMNS result rereads only the needed relations from
  INFORMATION_SCHEMA in concurrent IN chunks of at most 50 names.

Refs CHI-455
… SCHEMAS

- SHOW TABLES/VIEWS report the same generic 2043 error for a missing
  schema and a missing database, so any missing-object error from a
  SHOW ... IN SCHEMA read is disambiguated once per plan with
  SHOW SCHEMAS LIKE ... IN DATABASE: a missing schema lists empty (a
  fresh target plans as a first run), a missing or unusable database
  fails naming it, and an existing schema re-raises the original error.
- Bounded inspection reads started inside a bounded worker run serially,
  so capped-schema fallback chunks share the outer concurrency bound.
- Semantic source candidates are deduplicated by relation identity, so a
  relation seen by both SHOW and the name lookup is matched once.

Refs CHI-455
@kvlonge
kvlonge enabled auto-merge (squash) October 1, 2026 13:02
@kvlonge
kvlonge merged commit f7c8faf into main Oct 1, 2026
31 checks passed
@kvlonge
kvlonge deleted the perf/chi-455-snowflake-inspection branch October 1, 2026 13:19
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.

1 participant