Feature/validate upstream tables - #1309
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new dataset-level quality check, validate_upstream_table, to validate ingestion correctness by comparing an aggregate metric (row count by default, or other curated/built-in aggregates) computed on the checked DataFrame against the same metric computed on an upstream reference (DataFrame via ref_df_name or Unity Catalog table via ref_table), with optional absolute/relative tolerances and independent row filters.
Changes:
- Added
validate_upstream_tabletocheck_funcs.py, including parameter validation, aggregate evaluation on both sides, and tolerance/null-safe comparison logic. - Added unit and integration tests covering parameter validation, tolerance behavior, row filtering,
'*'/count(*) usage, and ref-table vs ref-DataFrame paths. - Documented the new check in the quality checks reference, including YAML and Python usage examples.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/databricks/labs/dqx/check_funcs.py |
Adds the new dataset-level upstream aggregate comparison check implementation. |
tests/unit/test_dataset_checks.py |
Adds rule-build-time parameter validation tests for the new check. |
tests/integration/test_dataset_checks.py |
Adds end-to-end Spark integration tests for the new check’s runtime behavior. |
docs/dqx/docs/reference/quality_checks.mdx |
Documents the new check and adds usage examples. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ghanse
left a comment
There was a problem hiding this comment.
Headed in the right direction. Left some comments. Do you also want to add a similar check for a custom SQL query?
mwojtyczka
left a comment
There was a problem hiding this comment.
Review of validate_upstream_table. Useful check and largely correct; main themes are: it reimplements existing aggregate machinery (_is_aggr_compare + _get_ref_df) rather than composing it, and a few edges around filter validation, the empty-upstream message, and the exact-equality default. Inline below.
Cleared: rel_tolerance has no div-by-zero (guarded by max()); the apply closure captures spark/ref_dfs correctly (standard dataset-check pattern); .limit(1) matches _is_aggr_compare; behavioral coverage lives in the integration tests (dataset checks can't be unit-tested without Spark).
mwojtyczka
left a comment
There was a problem hiding this comment.
Going in the right direction, left some comments
…ng functions as _is_aggr_compare and _get_ref_df, avoiding re-implementing existing logic
mwojtyczka
left a comment
There was a problem hiding this comment.
LGTM. Just one request to rename the function to aggr_matches_dataset to make it more generic and update docs and docs strings accordingly
Hi, @mwojtyczka! One thing to point out: Should we implement a group by clause in this check? _is_aggr_compare function already let us apply it in the input Dataframe. Could make sense because in many situations we need to make a per-group reconciliation (date, region,source_system,etc). |
yes makes sense |
|
@STEFANOVIVAS pls also add the check to |
Code review —
|
mwojtyczka
left a comment
There was a problem hiding this comment.
Code looks good, left comments regading tests
- Wire aggr_matches_dataset into the all-dataset-checks coverage: add it to all_dataset_checks.yaml and both all-checks integration tests (test_apply_checks_all_checks_as_yaml already loads the YAML; test_apply_checks_all_checks_using_classes gains the class-based check plus ref_dfs), exercising the declarative/YAML round-trip like the sibling dataset checks. - Add count_distinct + group_by integration tests (match, mismatch, and a NULL group key) to lock in grouped distinct-count behaviour for this check; the NULL-key case asserts flagging behaviour rather than exact message text since the window-incompatible join is not null-safe (documented). - Add performance benchmarks: dataset-wide count comparison and the count_distinct + group_by two-stage join path. Co-authored-by: Isaac
I fixed these |
# Conflicts: # src/databricks/labs/dqx/check_funcs.py
The MCP suite (tests/integration_mcp) only ran on PRs touching mcp-server/, src/databricks/labs/dqx/, or the mcp workflow — so nightly never exercised it. Add an mcp-tests job to nightly.yml mirroring the mcp.yml PR job (setup-env, prebuild-wheel, install Databricks CLI, acceptance harness with the integration_mcp codegen), minus the PR-only fork gate since nightly is schedule-triggered. No coverage upload (the MCP server/runner execute remotely). Co-authored-by: Isaac
|
Heads-up: I pushed a small CI addition to this branch ( It's unrelated to the |
Changes
aggr_matches_dataset— a new dataset-level check that compares an aggregate metric (row count by default, or any curated/built-in aggregate) computed on the checked DataFrame against the same aggregate computed on a reference (upstream) table or DataFrame, with optional abs_tolerance/rel_tolerance and independent row_filter/ref_row_filter on each side.Linked issues
Resolves #1046
Tests
Documentation and Demos