Skip to content

Configurable DBQL lookback and row cap for the Teradata profiler - #2644

Open
dey-abhishek wants to merge 18 commits into
mainfrom
feat/profiler/configurable-lookback
Open

Configurable DBQL lookback and row cap for the Teradata profiler#2644
dey-abhishek wants to merge 18 commits into
mainfrom
feat/profiler/configurable-lookback

Conversation

@dey-abhishek

@dey-abhishek dey-abhishek commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Changes

What does this PR do?

Makes the Teradata profiler's look-back windows and the DBQL row cap configurable via execution-time bind parameters, with defaults that preserve (or lightly update) the previous behavior. Previously each of these was hardcoded in SQL:

  • lookback_days (default 30) — core DBQL extract: CollectTimeStamp >= CURRENT_DATE - :lookback_days (raised from the previous hardcoded 7-day window)
  • pdcr_lookback_days (default 180) — PDCR history extracts (PDCRINFO.DBQLogTbl_Hst): LogDate >= date - :pdcr_lookback_days
  • sys_usage_lookback_days (default 60) — system usage aggregation (DBC.ResUsageSpma): TheDate >= date - :sys_usage_lookback_days
  • sys_nodes_lookback_days (default 30) — node hardware inventory (DBC.ResUsageSpma): TheDate >= date - :sys_nodes_lookback_days
  • max_rows (default 100000) — core DBQL row cap: QUALIFY ROW_NUMBER() OVER (ORDER BY TotalFirstRespTime DESC) <= :max_rows

The windows are intentionally independent: DBC.DBQLogTbl is the short-lived live table, while PDCRINFO retains far more history, so collapsing them onto one value would either shrink the PDCR extract or overrun the live table's retention. All are prompted for by configure-database-profiler and stored under the source's profiler section in the user-owned credentials file; the packaged defaults keep existing setups working.

Scope note: Teradata result-set streaming was split out of this PR into #2649 so this PR is parameterization-only. On this branch Teradata buffers via fetch(), exactly as main does today; streaming lands separately.

Relevant implementation details

  • Execution-time bind parameters (not string interpolation). DatabaseConnector.fetch()/stream() take a parameters mapping; the SQLAlchemy-based connectors bind :name placeholders (translated to the driver's paramstyle), so values are never spliced into SQL text. TOP can't take a bind parameter, so the row cap is expressed with QUALIFY ROW_NUMBER(). Parameters are bound globally to every step, so the core DBQL extract, both PDCR steps, and the two ResUsageSpma queries pick up their windows automatically.
  • Resolution precedence: pipeline defaults < credentials file < explicit overrides. Only parameters the pipeline declares are read from credentials, and the lookup is scoped to the profiled source so a shared credentials file with multiple sources can't leak one source's settings into another.
  • Configured values are validated >= 1 so 0/negative can't silently produce an empty (or future-dated) extract.

Caveats/things to watch out for when reviewing:

  • The raw-DBAPI connectors (mssql/redshift) reject bind parameters with NotImplementedError until binding is implemented for them; no current pipeline passes them any.
  • A single Teradata run extracts the core DBQL workload and additionally attempts the PDCR extraction; the PDCR steps are optional: true, so when PDCRINFO is absent they are marked ABSENT and tolerated and the run still succeeds.

Functionality

  • documented the configurable profiler parameters (Teradata profiler docs)
  • added new CLI command
  • modified existing command: databricks labs lakebridge configure-database-profiler

Tests

  • added/updated unit tests — bind-parameter resolution + source scoping, and the Teradata configurator prompts for all five profiler settings
  • make fmt clean (black/ruff/mypy, pylint 10.00/10) and make test passing
  • validated end-to-end against a live Teradata box: all steps complete against the parameterized SQL, with PDCR steps skipped automatically where PDCRINFO is absent

Add ${name} substitution variables to profiler pipelines, resolved with precedence: packaged pipeline defaults < user credentials file < explicit overrides. Values are validated against a safe character set before being interpolated into SQL sent to the source. The Teradata core DBQL extract now uses ${lookback_days} and ${max_rows}, prompted for during configure-database-profiler and stored under the source's profiler section in the credentials file so they survive upgrades. Defaults (7 days, 100000 rows) preserve prior behavior.
@dey-abhishek
dey-abhishek requested a review from a team as a code owner August 25, 2026 16:13
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@dey-abhishek dey-abhishek self-assigned this Aug 25, 2026
@dey-abhishek dey-abhishek added feat/profiler Issues related to profilers enhancement New feature or request labels Aug 25, 2026
@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.27027% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.57%. Comparing base (5e15b97) to head (239b491).

Files with missing lines Patch % Lines
...ks/labs/lakebridge/connections/database_manager.py 53.84% 5 Missing and 1 partial ⚠️
...databricks/labs/lakebridge/assessments/pipeline.py 78.94% 3 Missing and 1 partial ⚠️
...abs/lakebridge/assessments/configure_assessment.py 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2644      +/-   ##
==========================================
+ Coverage   71.25%   71.57%   +0.32%     
==========================================
  Files         112      112              
  Lines       10119    10144      +25     
  Branches     1111     1113       +2     
==========================================
+ Hits         7210     7261      +51     
+ Misses       2695     2657      -38     
- Partials      214      226      +12     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

✅ 181/181 passed, 8 flaky, 2 skipped, 2h47m38s total

Flaky tests:

  • 🤪 test_installs_and_runs_local_bladebridge (13.968s)
  • 🤪 test_installs_and_runs_pypi_bladebridge (23.003s)
  • 🤪 test_oracle_catalog_read_schema_happy (10.006s)
  • 🤪 test_transpiles_informatica_to_sparksql_non_interactive[True] (25.207s)
  • 🤪 test_transpiles_informatica_to_sparksql (27.03s)
  • 🤪 test_transpile_teradata_sql_non_interactive[True] (6.888s)
  • 🤪 test_transpile_teradata_sql (7.964s)
  • 🤪 test_transpile_teradata_sql_non_interactive[False] (23.994s)

Running from acceptance #5433

@asnare asnare left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few immediate topics that we need to address before getting into the finer details.

  1. Before we implement or support this, the Teradata profiler needs to be updated to support the streaming interface. Without this we end up with unbounded memory use and allowing users to configure the look-back will only make that more problematic than it already is. (This could be a loose thread that unravels somewhat… we will need to ensure that the streamed batches all have the same schema, and verify that every query has paired DDL in place.)

  2. Sorry, but we can't do variable substitution in an adhoc manner using a custom implementation: it's unsafe and will invariably be duplicated down the track into more places. Implementations like this are always brittle: only database-specific drivers (from the vendor) know how to do this safely and properly. (Custom interpolation also prevents us from improving our quality checks on SQL down the track.) The only safe way to allow for interpolation like this is to use prepared statements with execution-time binding. (In some cases this will lead to changes to the queries because variables can't be bound into all locations of the query, but that's a compromise we must accept.)

Addresses review feedback: replace the custom ${name} string substitution with driver
execution-time bind parameters, so values are never spliced into SQL text. fetch()/stream()
now accept a parameters mapping; the SQLAlchemy-based connectors bind via :name placeholders
(translated to the driver paramstyle), and the raw-DBAPI connectors reject parameters until
binding is implemented for them.

The Teradata DBQL core extract is rewritten so both values sit in bindable positions:
lookback via CURRENT_DATE - :lookback_days, and the row cap via
QUALIFY ROW_NUMBER() OVER (ORDER BY TotalFirstRespTime DESC) <= :max_rows (TOP cannot be bound).

Renames the pipeline 'variables' concept to 'parameters'. Validated end-to-end against a live
Teradata box: the core extract step completes and returns rows.
Implement the streaming interface for TeradataConnector so profiler SQL steps pull results in
bounded fetchmany() batches rather than materializing the whole result set in memory. Each batch
is built as an Arrow table with a schema derived once from the cursor description, so every batch
of a query shares one schema regardless of which columns are all-NULL in a given batch (batches
are appended positionally into the DDL-pre-created DuckDB table). Validated against a live
Teradata box: the streamed DuckDB output is schema-identical to the previous buffered path, with
no rows lost or altered.
…ack' into feat/profiler/configurable-lookback
@dey-abhishek

dey-abhishek commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review — both points addressed. @asnare

1. Streaming prerequisite. Implemented the streaming interface for TeradataConnector: SQL steps now pull results in bounded fetchmany() batches instead of buffering the whole set. Each batch is built as an Arrow table with a schema derived once from the cursor description, so every batch of a query shares one schema even when a column is all-NULL in a given batch (they're appended positionally into the DDL-pre-created DuckDB table). The Teradata steps already have paired DDL, so that requirement holds.

Validated end-to-end against a live Teradata box: all steps complete, and the streamed DuckDB extract is schema-identical to the previous buffered path with no rows lost or altered (the fetch-path row set is a strict subset of the streamed one, differing only by live query-log growth between runs).

2. Custom substitution → execution-time bind parameters. Removed the ${…} interpolation entirely. DatabaseConnector.fetch()/stream() now take a parameters mapping; the SQLAlchemy-based connectors bind :name placeholders (translated to the driver's paramstyle), so values are never spliced into SQL text. The DBQL core extract became:

  • lookback → CollectTimeStamp >= CURRENT_DATE - :lookback_days
  • row cap → QUALIFY ROW_NUMBER() OVER (ORDER BY TotalFirstRespTime DESC) <= :max_rows

TOP can't take a bind parameter, so the cap moved to QUALIFY

dey-abhishek and others added 2 commits August 26, 2026 08:53
- Scope profiler bind parameters to the profiled source (PipelineClass now takes source_system)
  instead of returning the first 'profiler' block in a shared credentials file.
- Validate configured lookback_days/max_rows are >= 1 so 0/negative can't yield a silent
  empty or future-dated extract.
- Pass bind parameters to source_ddl steps too, for parity with sql steps.
- Carry Teradata DECIMAL columns as exact text rather than float64 to avoid rounding counters
  beyond 2**53; DuckDB casts to the declared column type.
- Normalize tz-aware datetimes to UTC-naive (and strip tzinfo from times) before building Arrow
  batches, so a TIMESTAMP WITH TIME ZONE column can't crash the streamed step.
@dey-abhishek
dey-abhishek requested a review from asnare August 26, 2026 03:25
dey-abhishek and others added 10 commits August 26, 2026 09:01
…ack' into feat/profiler/configurable-lookback
The PDCR history extracts and the ResUsageSpma system usage/node queries had hardcoded lookback windows (180/60/30 days) while only the core DBQL extract honored a configurable window. Add pdcr_lookback_days (180), sys_usage_lookback_days (60), and sys_nodes_lookback_days (30) as bind parameters alongside the existing lookback_days/max_rows, wired through pipeline_config.yml and the Teradata configurator prompts. Also refresh the docs: correct the parameter reference and drop the obsolete --variant flag (Teradata core + PDCR now extract automatically in one run, with PDCR steps skipped when PDCRINFO is absent).
Raise the packaged lookback_days default from 7 to 30 (pipeline_config.yml and the configure-database-profiler prompt), and refresh the docs accordingly. Still overridable via the credentials profiler section.
The Teradata Arrow-batch streaming (TeradataConnector.stream/_arrow_batches, the Arrow helper functions, and their tests) is split out to feat/profiler/teradata-streaming so this branch carries only the profiler parameterization. The execution-time bind-parameter plumbing stays: fetch/stream still accept a parameters mapping, and Snowflake streaming (already in main) is unaffected. Teradata now buffers via fetch() as it does on main.
…ack' into feat/profiler/configurable-lookback
The teradata.mdx updates (parameter reference and the --variant correction) move out of the parameterization branch; docs are handled on the streaming branch (#2649). This branch is now code-only.
Add the profiler settings prompts to the configure-database-profiler example and document the five bind parameters (lookback_days, pdcr_lookback_days, sys_usage_lookback_days, sys_nodes_lookback_days, max_rows) with their SQL predicates and defaults. The --variant/Workload-Extraction doc corrections are handled separately on the streaming branch (#2649).
…rable-lookback

# Conflicts:
#	src/databricks/labs/lakebridge/resources/assessments/teradata/pdcr/td_pdcr_info_agg_extract.sql
#	src/databricks/labs/lakebridge/resources/assessments/teradata/pipeline_config.yml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request feat/profiler Issues related to profilers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants