fix(ol-dbt-cli): stop registering dbt's __dbt_tmp/__dbt_backup shadow tables - #2660
Merged
Merged
Conversation
… tables
`ol-dbt local register` enumerated Glue tables and filtered on two conditions
only — `table_type == ICEBERG` and a present `metadata_location`. dbt's
create-temp-then-swap build artifacts satisfy both: `<name>__dbt_tmp` and
`<name>__dbt_backup` are genuine Iceberg tables that Glue reports like any other.
So they were registered as first-class sources.
They are not sources, and they do not survive. dbt deletes them on its next run,
so a DuckDB view over one starts failing with `HTTP 404` reading its metadata
JSON, and the table can vanish between `get_tables` and `CREATE VIEW` inside a
single register run — which is what an error during registration looks like.
Measured against ol_warehouse_production_staging on 2026-09-09, diffing Glue's
live table list against `_glue_source_registry`:
Glue staging tables: 249 registered: 256
in Glue but NOT registered: 0
registered but NO LONGER in Glue: 7 <- all seven __dbt_tmp / __dbt_backup
That same run reported `✗ Errors: 6`. Attributing those six specifically to
these seven is inferred rather than proven — the per-table error lines were not
captured — but the mechanism matches exactly and no non-shadow table was missing
from the registry.
Filters on the table NAME, checked before the ICEBERG test so a shadow table
without Iceberg parameters is not reported twice. Numbered variants
(`__dbt_tmp1`, from an interrupted build) are covered, and the suffix is anchored
to the end of the name so a real table merely containing the token —
`int__dbt_tmp_usage_metrics` — is still registered.
SCOPE: this is deliberately NOT the fix for the larger problem that Glue points
the CANONICAL name of a dbt-built table at a `__dbt_tmp` metadata location, which
is why 624 of 640 non-raw registered views read duplicated or partial data. That
needs resolving the table's current snapshot rather than trusting Glue's
`metadata_location`, and it is a separate design decision. This change removes
only the junk views and the errors they cause.
20 new tests, including the seven real table names measured above. Verified by
negative control: stubbing the predicate to `return False` fails 10 of them.
Full ol_dbt_cli suite 639 passed; ruff clean. The 6 mypy errors visible from this
package are pre-existing in `lib/sql_parser.py` (identical to main, reproduced on
main with `--no-incremental`) and are untouched here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
🔎 ol-dbt impact — column-level blast radius✅ No column-level downstream impact detected for the changed models. Posted by |
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The focused filtering logic matches the documented scope and is adequately covered by tests.
Pull request overview
Filters transient dbt Iceberg shadow tables from local Glue registration.
Changes:
- Detects
__dbt_tmpand__dbt_backupsuffixes, including numbered variants. - Adds focused positive, negative, and integration-style tests.
File summaries
| File | Description |
|---|---|
src/ol_dbt_cli/ol_dbt_cli/commands/local_dev.py |
Excludes dbt shadow tables during Glue enumeration. |
src/ol_dbt_cli/tests/test_local_dev.py |
Tests shadow-table detection and filtering. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
blarghmatey
approved these changes
Sep 10, 2026
quazi-h
added a commit
that referenced
this pull request
Sep 15, 2026
…is tuned an order of magnitude too loose ol-dbt-local-dev told you to register, and `list-sources` warns when the registry is ">1 day old". Both understate the problem. `register` stores the Iceberg metadata_location Glue reports at that instant, and for a dbt-built table Glue routinely points the canonical name at a __dbt_tmp directory that the next production materialization swaps and deletes. Measured while validating #2403: a registry refreshed 60 MINUTES earlier failed with HTTP 404 reading the metadata JSON for int__micromasters__dedp_proctored_exam_grades, killing two mart models mid-validation. Re-registering only the intermediate layer (41 pointers moved, 118 unchanged) fixed it. Re-registering the same three layers 26 hours later moved another 336 pointers — 184 staging, 108 intermediate, 44 dimensional — which is the churn rate the one-day threshold is failing to track. The 404 is the lucky shape. When the __dbt_tmp directory still exists but holds a mid-build snapshot, the view returns duplicated or partial rows and nothing fails at all: a clean run and wrong numbers, which is the dangerous case when those numbers are about to be quoted in a PR. So the guidance is unconditional re-registration immediately before a build, not a reaction to a visible error. #2660 stopped __dbt_tmp tables being registered as sources in their own right but deliberately scoped out canonical names pointing at __dbt_tmp locations (621/636 dbt-built tables at last count); that remains tracked separately. Until it lands, freshness is the caller's responsibility, so the skill now says so. Verified the three documented register commands run verbatim from a clean worktree (0 errors each) and pre-commit passes on the file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
What are the relevant tickets?
N/A — found while validating #2403. Partial mitigation of a larger
ol-dbt local registerissue tracked internally; see Scope below for what this deliberately does not fix.Description (What does it do?)
ol-dbt local registerenumerated Glue tables and filtered on two conditions only:dbt's create-temp-then-swap build artifacts satisfy both.
<name>__dbt_tmpand<name>__dbt_backupare genuine Iceberg tables and Glue reports them with a realmetadata_location, so they were registered as first-class sources alongside real ones.They are not sources, and they do not survive. dbt deletes them on its next run, so a DuckDB view over one begins failing with
HTTP 404reading its metadata JSON — and the table can disappear betweenget_tablesandCREATE VIEWwithin a single register run, which is exactly what an error during registration looks like.Measured against
ol_warehouse_production_stagingon 2026-09-09, diffing Glue's live table list against_glue_source_registry:All seven are shadow tables. Nothing real was missing. That same register run reported
✗ Errors: 6.One claim I want to label honestly: attributing those six errors specifically to these seven tables is inferred, not proven — the per-table
✗lines were not captured, and a--dry-runre-check cannot reproduce them because it never issues theCREATE VIEW. The mechanism matches exactly and no non-shadow table was absent from the registry, so it is the strongly likely cause, but the 249/256/7 counts are the measured part.Implementation notes
__dbt_(?:tmp|backup)\d*$.ICEBERGtest, so a shadow table lacking Iceberg parameters isn't reported twice (once as an artifact, once as "not Iceberg").__dbt_tmp1, produced when a build is interrupted and retried) are covered. Registered views with__dbt_tmp1in their name have been observed in this catalog.int__dbt_tmp_usage_metrics— must still register, and there's a test for it.Scope — what this does not fix
This is not the fix for the bigger problem: Glue points the canonical name of a dbt-built Iceberg table at a
__dbt_tmp-<uuid>metadata location, because IcebergRENAMEdoes not move data. Re-measured immediately after a fresh register of three layers on 2026-09-09, 624 of 640 non-raw registered views (97.5%) sit on such a location, and they either 404 or silently return the temp table's accumulated snapshots — duplicated and partially missing rows, measured at 31x duplication onint__mitxonline__proctored_exam_grades.Fixing that means resolving the table's current snapshot from the Iceberg catalog rather than trusting Glue's
metadata_location, or refusing to register such a view at all — a design decision with its own tradeoffs, tracked separately. This PR removes only the junk views and the registration errors they cause. It does not make local row counts or fill rates trustworthy.How can this be tested?
To confirm the behaviour end to end against real Glue (needs AWS creds):
uv run ol-dbt local register --database ol_warehouse_production_stagingShadow tables now print as
⊘ <name> (dbt build artifact, not a real source)and are excluded from the registry. Re-run the count that surfaced the problem and expect no orphans:Existing views registered before this change are not retro-actively removed.
ol-dbt local cleanup-localdrops locally-registered views if you want a clean slate; otherwise the next--forceregister leaves the seven stale ones in place, since the code no longer enumerates them to update.Additional Context
Verified by negative control, not just by the tests passing: stubbing
_is_dbt_shadow_tabletoreturn Falsefails 10 of the 20 new tests. The parametrised cases use the seven real table names measured above rather than invented ones, so the test suite documents the actual catalog state that motivated the change.Pre-existing mypy errors.
mypysurfaces 6 errors inol_dbt_cli/lib/sql_parser.pyfrom this package. They are untouched here — the file is byte-identical tomain, andmypy --no-incrementalreproduces them onmain. They're invisible in a warm-cache run, which is why the pre-commit hook passes.Behaviour change worth a reviewer's eye: anyone who has come to rely on a
__dbt_tmpview being present locally will find it gone after the next register. That should be nobody — they 404 unpredictably — but it is a change in whatregisterproduces, not only in what it errors on.🤖 Generated with Claude Code