Skip to content

perf: prune partitions in the remaining report queries - #2351

Open
tlangton3 wants to merge 2 commits into
elementary-data:masterfrom
tlangton3:perf/partition-pruning-report-queries
Open

perf: prune partitions in the remaining report queries#2351
tlangton3 wants to merge 2 commits into
elementary-data:masterfrom
tlangton3:perf/partition-pruning-report-queries

Conversation

@tlangton3

@tlangton3 tlangton3 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

What

The report queries filter their largest tables with edr_datediff(edr_cast_as_timestamp(col), now, 'day') < days_back. On BigQuery, a function applied to the column defeats partition pruning, so these scan all history however small days_back is.

This is the same defect #1940 fixed for test_result_rows, applied to the remaining sites — and it generalises that fix so the pattern is in one place instead of copied per macro.

Measured

A 7-day lookback selecting a single column, on tables exactly as they are — no repartitioning needed to get this:

Table Before After
elementary_test_results (12M rows, 13GB) 1,368 MB 13.68 MB
dbt_run_results (17M rows, 41GB) 1,627 MB 63.56 MB
test_result_rows (26M rows, 41GB) 1,060 MB 101 MB

For scale, an unfiltered scan of elementary_test_results reads 1,279 MB: the current predicate reads more than no filter at all, because it also pays to read the column it filters on.

How

A dispatched days_back_filter macro. default__ renders exactly the predicate these queries have always used, so nothing changes off BigQuery. bigquery__ compares the column directly and adds a bound on the partition column.

Applied to both CTEs of current_tests_run_results_query, and to get_result_rows_agate — which lets that macro's bigquery__ override go away. #1940 had to duplicate the entire body just to change one where clause; with the dispatch in the predicate helper, the two copies collapse back into one.

About the redundant-looking bound

The BigQuery variant emits e.g. detected_at > X and created_at > X-1day. The second predicate is implied by the first — it exists only because BigQuery prunes on the raw partition column and nothing else. It is needed when the filtered column is not the partition column, and unavoidable for dbt_run_results, which stores generated_at and execute_completed_at as strings: those need a cast, and no cast predicate can ever prune.

It cannot exclude a row the real predicate wants, because created_at is stamped by insert_rows from edr_current_timestamp() at insert time and never read from the row payload — so it is always at or after the event timestamp it guards. Checked over 17.2M rows: created_at - generated_at >= 0s, created_at - execute_completed_at >= +5s, created_at - detected_at >= 0s across 12M and 26M row tables, zero violations of any.

Since those two timestamps are not read from the same clock — one warehouse-side, one client-side — the bound is given a day of slack rather than the same bound, so client drift cannot cost a row. Pruning is at day granularity, so that is at most one extra partition; on the figures above it measured as no difference at all, the real predicate being the binding constraint.

Not included

get_models_runs has the same defect and is the largest single win, but it reads the model_run_results view, which does not expose created_at — so it has no prunable column until elementary-data/dbt-data-reliability#1057 lands. Deliberately left for a follow-up rather than shipped broken.

get_source_freshness_results and can_upload_source_freshness carry the same shape. I have no source-freshness data to measure, so I have not touched them — happy to if you would like them included.

Testing

Rendering verified for each branch of the macro: timestamp column, string column (cast applied), the column being the partition column itself (bound correctly suppressed), a string-typed days_back, and the default path (byte-identical semantics to the predicate it replaces). Resulting predicates dry-run against real tables for the figures above. typos clean; the repo's Python hooks do not apply to .sql.

Summary by CodeRabbit

  • Performance

    • Improved filtering for recent test and run results, enabling more efficient date-range queries.
    • BigQuery queries can better use partitioned data, potentially reducing scan volume and improving response times.
  • Consistency

    • Standardized how result data is filtered across supported database environments.
    • Preserved existing options for filtering by valid result IDs and grouping returned rows.

`current_tests_run_results_query` filters both of its large CTEs with
`edr_datediff(edr_cast_as_timestamp(col), now, 'day') < days_back`. On BigQuery a
function applied to the column defeats partition pruning, so both scan all
history however small `days_back` is.

Adds a dispatched `days_back_filter` macro: the default implementation renders
exactly the predicate above, so nothing changes off BigQuery, while `bigquery__`
compares the column directly and adds a bound on the partition column.

Measured with a 7-day lookback, selecting a single column, on tables as they
already are — no repartitioning needed to get this:

    elementary_test_results (12M rows, 13GB)   1,368 MB -> 13.68 MB
    dbt_run_results         (17M rows, 41GB)   1,627 MB -> 63.56 MB

For reference, an unfiltered scan of elementary_test_results reads 1,279 MB: the
current predicate reads *more* than no filter at all, since it also pays to read
the column it filters on.

The bound on `created_at` looks redundant — it is implied by the first predicate —
but BigQuery only prunes on the raw partition column, and `execute_completed_at`
is stored as a string, so a cast is unavoidable there and no predicate on it can
prune. dbt_run_results is not even partitioned on the warehouse measured above;
the 26x comes from block-level pruning on `created_at` alone, and partitioning it
would improve that further.

The bound is also safe: `created_at` is written at or after both columns, so any
row satisfying the first predicate satisfies the bound. Verified over 17.2M rows —
`created_at - generated_at` >= 0s, `created_at - execute_completed_at` >= 5s, and
zero rows violating either.

The guard is given a day of slack rather than the same bound: `created_at` is
stamped by the warehouse at insert while the columns it guards come from the dbt
client, so the two are not the same clock. Pruning is at day granularity, so the
slack costs at most one extra partition — measured as no difference at all on the
figures above, since the real predicate is the binding constraint.
…ilter

elementary-data#1940 gave this macro a `bigquery__` override that duplicated the whole body just
to change its `where` clause. Now that `days_back_filter` dispatches on the
predicate itself, the two copies collapse back into one and the dispatch
disappears.

Behaviour is unchanged on every adapter: `default__days_back_filter` renders the
datediff form the original had, and `bigquery__days_back_filter` renders the
direct comparison the override had.

It also gains the `created_at` bound. test_result_rows declares no partitioning
today, so the direct comparison on `detected_at` was relying on block-level
pruning alone; the bound takes a 7-day lookback from 1,060 MB to 101 MB on a 26M
row / 41GB copy of that table, and improves further once it is partitioned.
@github-actions

Copy link
Copy Markdown
Contributor

👋 @tlangton3
Thank you for raising your pull request.
Please make sure to add tests and document all user-facing changes.
You can do this by editing the docs files in this pull request.

@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: cce27aff-2fa1-4c84-94ed-5f4bcb41ddde

📥 Commits

Reviewing files that changed from the base of the PR and between e34d853 and 73cf8fc.

📒 Files selected for processing (3)
  • elementary/monitor/dbt_project/macros/base_queries/current_tests_run_results_query.sql
  • elementary/monitor/dbt_project/macros/get_result_rows_agate.sql
  • elementary/monitor/dbt_project/macros/utils/days_back_filter.sql

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The change adds an adapter-dispatched days_back_filter macro. Result queries use it for date filtering, with BigQuery-specific partition pruning and string-column handling.

Changes

Days-back filtering

Layer / File(s) Summary
Add adapter-aware days-back filtering
elementary/monitor/dbt_project/macros/utils/days_back_filter.sql
Adds default and BigQuery implementations for timestamp windows, optional string casting, and partition bounds.
Apply filtering to result queries
elementary/monitor/dbt_project/macros/base_queries/current_tests_run_results_query.sql, elementary/monitor/dbt_project/macros/get_result_rows_agate.sql
Updates result queries to use days_back_filter and removes adapter-specific agate implementations.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 73cf8

The change preserves non-BigQuery filtering while adding BigQuery partition pruning, with reported scan reductions and completed rendering and dry-run checks. No merge-blocking risk is currently identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: improving report-query performance by enabling partition pruning. It matches the pull request objectives and changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

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