Crosshire
← Handbook
RunFreshness · volume · schema · distribution

11Observability

This route decides which properties of the data are watched continuously, what the thresholds are, and who is told when one breaks. It also decides how much is bought from Data Quality Monitoring and how much is four SQL files in the repository. The default here: buy the profiling, write the four checks, and never let an alert exist without a named recipient.

Stand 29.07.2026

Green jobs, wrong numbers

The route rests on one distinction. A team that cannot state it keeps buying dashboards of job status and keeps being told about bad numbers by the business.

#The failure class nothing catches

What
Monitoring watches the machinery. Observability watches the data. Monitoring answers did the job exit zero. Observability answers is what came out of it true. The interesting failures do not throw.
One run through this warehouse. The ECB publishes no rate on a Frankfurt bank holiday. The bronze.fx_rate load finds no file, writes nothing, exits zero. silver.fx_rate has nothing to insert, exits zero. The gold build finds no rate for date_sk and leaves fx_rate — and therefore amount_eur — NULL on every CHF line.
Four green jobs. Swiss revenue for the day is zero. Nobody notices until a controller asks why Switzerland collapsed on 1 August.
The four signals, and what each catches in this warehouse
SignalQuestionCheap checkCatches
Freshnessis it current?MAX(_tech_loaded_at) against a 26 h SLAan SAP extract that stopped being scheduled
Volumeis all of it there?daily count against a 7-day mediana CDC gap on sap_vbap; the webshop feed silently dry
Schemais it the same shape?information_schema diff against dwh_testa retyped SAP field truncating amount_eur
Distributionare the values sane?share of NULL, CANCELLED, UNKNOWN per daythe missing fx_rate above; a broken customer join
Four queries, no product. What they need is someone who owns each threshold.
Why here
The failure this prevents is discovery by the business. Once a controller finds a wrong number before the data team does, every later number is suspect, and correct output does not buy that back quickly. Observability is how the team stays first to know.
It also sets triage speed. With these four signals, which day, which country, which source is answered from history you already have. Without them, the incident starts by writing the query that would have caught it.
Doing it
  • Write the four checks as .sql files in the repo, on the same zero-rows-means-pass convention as the SQL test suite.
  • Schedule them as Databricks SQL alerts on gold.fct_order_line and the silver tables that feed it — not as a notebook someone runs.
  • Put a named owner and a runbook link in every alert body. An alert with neither is a notification.
  • Record each incident's detection path: did we find it, or did they. That ratio is this route's only honest score.
Embedding it
  • Break it in front of them: delete a day of bronze.fx_rate in dwh_test, run the pipeline, watch all four jobs go green. The silence is the lesson.
  • Have the team pick the thresholds and write down the reasoning. A threshold they chose is one they tune instead of mute.
  • Give each of the four checks to a different engineer. Four small owners beat one observability champion who leaves.
  • At every incident review ask only: which signal should have caught this? Add or tune exactly one check.
Defaults
  • Four signals on every gold table a report reads, before a fifth signal anywhere.
  • Checks live in the repo and deploy with the bundle. Nothing important is configured only in the UI.
  • Zero rows means pass, so one file is a CI test and a production monitor.
  • Every alert names a person and a runbook. Never a shared mailbox.
Gotchas
  • Treating job-status dashboards as observability. Complete, green, and silent about the fx_rate case — the pipeline did exactly what it was told, on data that was not there.
  • Alerting on everything on day one. The channel is muted within a fortnight, and a muted channel is worse than none because everyone believes it works.
  • Checks that live only in the monitoring UI: invisible to code review, absent from the diff when someone changes the fact grain, gone when the workspace is rebuilt.
  • Monitoring gold only. The fx_rate outage was visible one step earlier in silver.fx_rate, and nobody watched it because nothing reads it directly.

The four checks

Runnable against the canonical model, as they would be committed. Each returns zero rows when the warehouse is healthy, so each is simultaneously a monitor and a test.

#Freshness, on two clocks

What
Freshness is two numbers. When the pipeline last wrote and how recent the newest business event is fail differently, and most teams measure only the first.
sql
monitors/freshness_fct_order_line.sql — zero rows = fresh
-- Two clocks, deliberately. A full refresh that rewrites yesterday's rows
-- keeps _tech_loaded_at current while the newest order is 40 hours old.
SELECT
  'gold.fct_order_line' AS object_name,
  MAX(_tech_loaded_at)  AS last_write,
  MAX(order_ts)         AS newest_event,
  timestampdiff(HOUR, MAX(_tech_loaded_at), current_timestamp()) AS write_age_h,
  timestampdiff(HOUR, MAX(order_ts),        current_timestamp()) AS event_age_h
FROM dwh_prod.gold.fct_order_line
HAVING timestampdiff(HOUR, MAX(_tech_loaded_at), current_timestamp()) > 26
    OR timestampdiff(HOUR, MAX(order_ts),        current_timestamp()) > 26;
The SLA is 26 hours and the two extra hours are arithmetic, not slack. The load starts at 02:00 Europe/Berlin and runs up to ninety minutes; a 24-hour SLA fires every time it starts ten minutes late. Twenty-six covers the run window plus the hour that March's DST transition removes from the night.
Why here
This catches an extract that stopped being scheduled. Nobody disables a job on purpose — a workspace is migrated, a service principal's token rotated, a bundle target redeployed without the schedule block. The pipeline does not fail, it does not run, and a job-failure alert has nothing to fire on.
The event clock catches the other half. bronze.sap_kna1 is a daily full extract with no change timestamp, so the pipeline cannot distinguish 'nothing changed' from 'this is Tuesday's file again'. Only MAX(order_ts) on the fact side notices.
Doing it
  • Commit the check per gold table a report reads, with the SLA as a literal so the diff shows when someone loosens it.
  • Derive the SLA from the schedule: start time + p95 run duration + one hour of DST.
  • Pin the schedule's timezone explicitly (Europe/Berlin, not UTC) so schedule and SLA move together twice a year.
  • Add the same check on silver.fx_rate — no downstream reader, largest blast radius.
Embedding it
  • Ask the team to derive the 26 from their own run history rather than accepting it. Whoever runs that query owns the number afterwards.
  • Have them pause a job in dwh_test for a day and confirm the alert reaches a phone. An untested alert is a belief.
  • When the first weekend false positive lands, do not fix it — hand it to whoever is on the rota and review their fix.
Defaults
  • Two clocks on every fact: load recency and event recency.
  • SLA = schedule + p95 duration + 1 h, written down with that reasoning.
  • Schedules and freshness checks share one explicitly stated timezone.
  • Freshness on the bronze tables too — the earliest signal is the cheapest to act on.
Gotchas
  • Measuring only _tech_loaded_at. A full-refresh pipeline stamps every row on every run, so the table reports itself as minutes old forever — including the morning the source stopped delivering.
  • A 24-hour SLA on a daily job. It fires on ordinary jitter, the team learns the alert means nothing, and it is muted before the month it would have mattered.
  • The schedule in UTC and the SLA reasoned in local time. Twice a year they drift by an hour: a week of false alarms in March, an hour of blindness in October.
  • A freshness check that itself runs once a day. Worst case you learn about a stale table 24 hours late. Run it hourly — it is one aggregate over a clustered table.
  • One event-age SLA on a B2B order book. Saturday and Sunday produce almost no orders, so MAX(order_ts) is past sixty hours old at 08:00 every Monday: the check fires weekly, on healthy data, until someone mutes it. Reason the event clock in business days off gold.dim_date, or evaluate it only on business days — and leave the write clock at 26 hours, because the pipeline does run at the weekend.

#Volume against a rolling median

What
Row count is the highest-yield check per line of SQL in the warehouse. Median, not mean: one Black Friday drags a mean baseline for a week, and a median does not care.
sql
monitors/volume_fct_order_line.sql — yesterday vs the 7 days before it
-- The calendar is the spine, not the fact table. A day on which nothing
-- landed produces no group, so a GROUP BY over the fact alone cannot emit a
-- row for the total outage this check exists to catch.
WITH calendar AS (
  SELECT dd.date AS d
  FROM dwh_prod.gold.dim_date dd
  WHERE dd.date >= current_date() - INTERVAL 40 DAYS
    AND dd.date <  current_date()
),
daily AS (
  SELECT c.d, count(f.order_line_sk) AS line_count
  FROM calendar c
  LEFT JOIN (
         SELECT date_sk, order_line_sk
         FROM dwh_prod.gold.fct_order_line
         WHERE date_sk >= current_date() - INTERVAL 40 DAYS
           AND date_sk <  current_date()
       ) f
    ON f.date_sk = c.d
  GROUP BY c.d
),
baseline AS (
  SELECT t.d, t.line_count, median(h.line_count) AS median_7d
  FROM daily t
  JOIN daily h
    ON h.d >= t.d - INTERVAL 7 DAYS
   AND h.d <  t.d
  GROUP BY t.d, t.line_count
)
SELECT d, line_count, median_7d,
       round(line_count / nullif(median_7d, 0), 2) AS ratio
FROM baseline
WHERE d = current_date() - INTERVAL 1 DAY
  AND (line_count < 0.5 * median_7d OR line_count > 2.0 * median_7d);
Holidays have no arithmetic at all. This warehouse sells into DE, AT, CH and NL: 1 May is a public holiday in DE and AT, an ordinary working day in NL, and cantonal in CH — so 'is today a holiday in Switzerland' has no single answer. A dayofweek() rule cannot express that. A per-country holiday column on gold.dim_date can.
Why here
Volume catches failures that leave a table syntactically perfect. A CDC connector on bronze.sap_vbap that resumes from the wrong offset delivers a real, well-typed day missing four hours of items. Freshness green, schema unchanged, every expectation passing — because every row that arrived was valid.
The upper band matters as much. A ratio above 2.0 is usually a re-run that duplicated a day, and the fact table accepts it silently: Unity Catalog primary keys are informational, so nothing rejects the duplicate and revenue doubles.
Doing it
  • Set the band from history: compute the observed ratio over 90 days, then pick bounds that would have fired on the incidents you remember and nothing else.
  • Run the check on each bronze table as well as the fact — bronze is where a per-source outage is visible first.
  • Alert on the ratio, not the raw count. A count threshold needs rewriting every time the business grows.
  • Group by date_sk, never by _tech_loaded_at, so a late batch does not read as a spike.
  • Bound the fact scan on order_ts as well as date_sk. fct_order_line is clustered by (customer_sk, order_ts), so a date_sk-only window reads files an order_ts range would have skipped — free today, a per-run cost once the fact is years deep.
Embedding it
  • Give the team the 90-day ratio distribution and let them argue the bounds out loud. The argument is the transfer; the numbers are secondary.
  • Have them replay a real incident against the check before trusting it. If it would not have fired, the check is decoration.
  • Rotate who reviews the weekly false-positive list, or one person ends up tuning thresholds nobody else understands.
Defaults
  • Median over mean, ratio over absolute count, band derived from observed history.
  • Like-for-like baselines: same weekday, business days only, one shared calendar on dim_date.
  • Volume on bronze as well as gold.
  • Both directions banded. A doubling is a duplicate load and it is the expensive one.
Gotchas
  • A mean baseline. One promotional spike raises it for a week, so the real drop that follows sits inside the band and never fires.
  • Weekend and holiday false positives left unfixed. The check becomes noise, the noise gets muted, and the muting is invisible in code review because it happened in the alert UI.
  • Counting rows written instead of rows for the business day. A late batch lands two days of orders in one run; the load count spikes while every per-day count is fine.
  • Only banding the low side. Duplicate loads survive longest, because everyone's instinct is that more data is not a problem.
  • Grouping the fact by day and waiting for the missing day to appear. A day with no rows produces no group, so the total outage — the failure everyone assumes this check is for — returns zero rows and reads as healthy. Drive the check off gold.dim_date and LEFT JOIN the fact, so an absent day arrives as a count of 0 and trips the lower band.

#Slices and distribution

What
An aggregate is an average of the working and the broken. DE is roughly two thirds of this order book, so NL going to zero moves the total by a few percent and sits inside any sane band. Per slice, or it does not catch segment failure at all.
sql
monitors/volume_by_country.sql — the same test, one row per country
-- Same spine problem, one dimension worse: a country that goes to zero stops
-- producing rows at all. Build the country x day grid first, join the fact in.
WITH calendar AS (
  SELECT dd.date AS d
  FROM dwh_prod.gold.dim_date dd
  WHERE dd.date >= current_date() - INTERVAL 40 DAYS
    AND dd.date <  current_date()
),
countries AS (
  SELECT DISTINCT country
  FROM dwh_prod.gold.dim_customer
  WHERE _tech_is_current
),
grid AS (
  SELECT k.country, c.d
  FROM countries k CROSS JOIN calendar c
),
fact_lines AS (
  SELECT c.country, f.date_sk AS d, f.order_line_sk
  FROM dwh_prod.gold.fct_order_line f
  JOIN dwh_prod.gold.dim_customer  c
    ON c.customer_sk = f.customer_sk
   AND c._tech_is_current   -- customer_sk is stable across SCD2 versions;
                            -- without this predicate every version fans out
  WHERE f.date_sk >= current_date() - INTERVAL 40 DAYS
    AND f.date_sk <  current_date()
),
daily AS (
  SELECT g.country, g.d, count(l.order_line_sk) AS line_count
  FROM grid g
  LEFT JOIN fact_lines l
    ON l.country = g.country
   AND l.d       = g.d
  GROUP BY g.country, g.d
),
baseline AS (
  SELECT t.country, t.d, t.line_count, median(h.line_count) AS median_7d
  FROM daily t
  JOIN daily h
    ON h.country = t.country
   AND h.d >= t.d - INTERVAL 7 DAYS
   AND h.d <  t.d
  GROUP BY t.country, t.d, t.line_count
)
SELECT country, d, line_count, median_7d
FROM baseline
WHERE d = current_date() - INTERVAL 1 DAY
  AND (line_count < 0.5 * median_7d OR line_count > 2.0 * median_7d);
The other slice everyone wants is source system, and this model does not have it: gold.fct_order_line carries no source column. Source identity is the bronze table — SAP is bronze.sap_vbak / sap_vbap, the webshop is bronze.shop_order — so per-source volume is monitored there. Carrying source onto the fact is a data-model change, not a monitoring setting.
sql
monitors/distribution_fct_order_line.sql — shape, not count
-- Eight days side by side. A step change in any column is the alert;
-- pct_amount_null is the one that catches the missing fx_rate.
SELECT
  date_sk,
  count(*)                                                        AS line_count,
  round(100.0 * count_if(amount_eur IS NULL)      / count(*), 2)  AS pct_amount_null,
  round(100.0 * count_if(customer_sk = 'UNKNOWN') / count(*), 2)  AS pct_unknown_customer,
  round(100.0 * count_if(status = 'CANCELLED')    / count(*), 2)  AS pct_cancelled,
  round(100.0 * count_if(currency = 'CHF')        / count(*), 2)  AS pct_chf
FROM dwh_prod.gold.fct_order_line
WHERE date_sk >= current_date() - INTERVAL 8 DAYS
  AND date_sk <  current_date()   -- today is a partial day and always reads
                                  -- as a step change; it is not one
GROUP BY date_sk
ORDER BY date_sk;
Why here
Distribution catches the fx_rate incident from the first topic: pct_amount_null goes from 0 to the CHF share of the day, on the morning it happens rather than at month end. pct_unknown_customer catches the mirror failure — a customer load that stopped, so new orders resolve to the unknown member and revenue is attributed to nobody.
Slices also change what an alert costs to act on. 'Volume down 8%' sends someone into the whole pipeline. 'NL is at zero, DE and AT normal' names the country and the source in one sentence.
Doing it
  • Slice by country via dim_customer, and by status and currency on the fact itself.
  • Always add _tech_is_current (or an interval predicate on _tech_valid_from / _tech_valid_to) when a monitor joins a fact to an SCD2 dimension.
  • Monitor volume per bronze table so a webshop outage is visible before it reaches gold.
  • Alert on a step change in a percentage, not on its level. 3% unknown customers may be normal; 3% after a month at 0% is not.
Embedding it
  • Ask which slice would have made last quarter's worst incident obvious in one line. Build that one first.
  • Have someone break a single country in dwh_test and see whether the unsliced check notices. It will not, and that ends the argument about whether slices are worth the cost.
  • Make the SCD2 join predicate a review checklist item. Everyone gets it wrong once; the point is that review catches it, not a false alarm.
Defaults
  • One slice per dimension the business steers by — here country, and status.
  • Percentages compared against their own recent history, never against a fixed target.
  • Per-source monitoring on bronze, because the model deliberately does not carry source into gold.
  • Cap the slice count. Four countries is a check; four hundred customers is a noise generator.
Gotchas
  • Joining fct_order_line to dim_customer on customer_sk alone. The surrogate key is a hash of customer_id and is stable across SCD2 versions, so the join fans out one row per version — and the monitor reports a volume spike that is a defect in the monitor.
  • Slicing by a high-cardinality column. Per-customer checks fire somewhere every day, which teaches the team that this channel is always red.
  • Assuming an aggregate inside its band is healthy. Any slice smaller than the band width is invisible to the total: NL can go to zero without moving the number you are watching.
  • Alerting on an absolute percentage. It is right for one quarter, drifts with the business, and is then rewritten under pressure during an incident.
  • Expecting a fact-driven GROUP BY to report a slice that vanished. NL at zero emits no NL rows, so the country disappears from the result instead of appearing at zero, and the alert built for exactly this case never fires. The countries x days grid has to exist before the fact is joined onto it.

#Schema drift, diffed against test

What
Schema drift is usually diffed against a snapshot someone remembered to take. A better reference already exists in the estate: the schema CI actually tested. Diff dwh_prod against dwh_test, and zero rows means production is the shape the suite ran against.
sql
monitors/schema_drift.sql — symmetric, because one direction sees half
WITH prod AS (
  SELECT table_schema, table_name, column_name, ordinal_position,
         full_data_type, is_nullable
  FROM dwh_prod.information_schema.columns
  WHERE table_schema IN ('bronze', 'silver', 'gold')
),
test AS (
  SELECT table_schema, table_name, column_name, ordinal_position,
         full_data_type, is_nullable
  FROM dwh_test.information_schema.columns
  WHERE table_schema IN ('bronze', 'silver', 'gold')
)
SELECT 'only_in_prod' AS side, * FROM (SELECT * FROM prod EXCEPT SELECT * FROM test)
UNION ALL
SELECT 'only_in_test' AS side, * FROM (SELECT * FROM test EXCEPT SELECT * FROM prod);
full_data_type, not data_type: the latter reports DECIMAL for both DECIMAL(18,2) and DECIMAL(12,2), so a narrowing of amount_eur passes the diff untouched. ordinal_position is in the list to catch a positional insert about to write currency into status.
Why here
The concrete case is SAP. A field is added to KNA1 or VBAP by an ERP team who have never heard of this warehouse. CDC adds the column to bronze.sap_vbap and nothing complains. On bronze.shop_order, Autoloader's default addNewColumns evolution stops the stream with an UnknownFieldException and picks the column up on the next start — which under a job retry reads as a transient failure that fixed itself, and is the only notification anyone gets. Either way silver ignores the new column, because the transform names its columns. Six months later someone wants that field and finds it has been arriving unmapped since spring.
The expensive version is a type change. A source field that widens upstream while the target stays narrow does not fail in one recognisable way: losing scale rounds silently, while a value that no longer fits the precision raises under ANSI mode — the serverless default this warehouse tests against — and returns NULL wherever ANSI is off. Two endings, one consequence: amount_eur is wrong for exactly the largest orders, the ones a spot check is least likely to include.
Doing it
  • Run the diff after every deployment and nightly, and treat a non-empty result as a deployment defect until proven otherwise.
  • Include ordinal_position and full_data_type; exclude nothing you cannot name a reason for.
  • Use INSERT INTO … BY NAME wherever a target is written from a SELECT, so a reordered column fails loudly instead of shifting values.
  • Add the comment column to the diff — grain and ownership live in comments, so a dropped comment is a dropped contract.
Embedding it
  • Have the team add a column in dwh_test and watch the diff catch it, then drop it and watch the other direction catch that. The symmetry stops being theoretical in two minutes.
  • Ask in review where the source-owner conversation is recorded. Drift is an organisational signal: someone changed something and did not tell you.
  • Let them find the permission gotcha themselves by running the monitor as the service principal rather than as themselves.
Defaults
  • Diff prod against test, not against a snapshot file somebody has to maintain.
  • Symmetric EXCEPT, always. One direction reports additions and is blind to removals.
  • full_data_type over data_type; ordinal_position included.
  • Writes by name, never by position.
Gotchas
  • information_schema shows only objects the caller has privileges on. A monitor running as a service principal without SELECT on a schema returns zero rows — indistinguishable from healthy. Assert the row count of both CTEs, not just the diff.
  • Diffing data_type instead of full_data_type. DECIMAL(18,2) narrowed to DECIMAL(12,2) is invisible, and the symptom is wrong totals confined to the largest orders.
  • Ignoring ordinal_position because 'nothing depends on column order'. Positional INSERT … SELECT * does, and the failure is currency landing in status: same type, plausible values, no error.
  • Autoloader schema evolution treated as a solution rather than an event. It keeps the pipeline running, which is right, and it means new columns arrive with nobody deciding whether they belong in silver.

What the platform does for you

Data Quality Monitoring covers the profiling and the statistical half. It does not replace the four checks — it makes them the exception rather than the workload.

#Data Quality Monitoring

What
Lakehouse Monitoring is now Data Quality Monitoring, and the rename split it. The old product is the data profiling half, per table. The new half is anomaly detection at catalog or schema level, judging freshness and completeness from each table's own history rather than from thresholds you write.
Profile types — pick by what the table is, not by preference
ProfileForHere
Snapshotcurrent state; metrics over the whole table each rungold.dim_customer, gold.dim_product
Time seriesa timestamp column defines windows; metrics per windowgold.fct_order_line on order_ts
Inferencemodel input/output logs, prediction driftnot used by this warehouse
python
A time-series monitor on the fact table
from databricks.sdk import WorkspaceClient
from databricks.sdk.service.catalog import MonitorTimeSeries

w = WorkspaceClient()

w.quality_monitors.create(
    table_name="dwh_prod.gold.fct_order_line",
    output_schema_name="dwh_prod.gold",
    assets_dir="/Workspace/Shared/dwh/monitors/fct_order_line",
    time_series=MonitorTimeSeries(
        timestamp_col="order_ts",
        granularities=["1 day"],
    ),
    # slicing is evaluated on the monitored table, so only its own columns
    slicing_exprs=["status", "currency", "region_sk"],
)
The monitor writes ordinary Delta tables into the output schema you nominate: profile metrics and drift metrics. That is why it is worth adopting. Alerting, dashboards and your own reconciliation queries are all SQL over Delta, so the platform's metrics and your four checks land in one alerting path instead of two consoles.
Anomaly detection adds intelligent scanning: it sweeps every table in the schema but prioritises the important ones, and judges freshness and completeness from each table's own history rather than from thresholds you write. That is what keeps a schema-wide sweep affordable. When it fires, the alert is an entry point into Unity Catalog lineage — upstream to what fed the anomaly, downstream to who is about to read it. External lineage is GA, so that chain reaches the SAP source and the Power BI report instead of stopping at the platform edge.
Why here
Hand-written checks scale linearly with tables. Forty tables is forty thresholds, and the fortieth is written badly by someone in a hurry. Anomaly detection covers the long tail nobody would have got round to instrumenting; the hand-written four cover the tables where you know exactly what wrong looks like.
The division of labour is the decision this topic makes: the platform learns what normal looks like; you assert what correct means. No amount of profiling will discover business rule 2 — that amount_eur must equal ROUND(quantity * unit_price * fx_rate, 2). That is an assertion, and it belongs in the SQL test suite.
Doing it
  • Enable anomaly detection at schema level on gold; add table-level profiling only to the fact and the dimensions a report reads.
  • Time series on gold.fct_order_line keyed on order_ts; snapshot on the dimensions.
  • Route the platform's alerts into the same channel as the four checks, so on-call has one place to look.
  • Check the SDK surface against your installed version before copying the snippet — the API names predate the rename and lag it.
Embedding it
  • Have the team read the drift-metrics table as SQL in week one. Once they see it is a Delta table it stops being a black box and starts being data they can join.
  • Make the team own the list of business rules the platform cannot cover. Writing that list is the exercise.
  • Walk one real alert through lineage to a named report owner in front of them, and let them do the second one.
Defaults
  • Anomaly detection schema-wide; profiling on the tables you can name a consumer for.
  • Metrics tables treated as first-class data — queried, joined, alerted on in SQL.
  • Platform detects drift; the repo asserts rules. Never expect the first to do the second.
  • One alerting channel for platform and hand-written checks alike.
Gotchas
  • Expecting anomaly detection to catch a wrong business rule. It learns what this table normally looks like, so a conversion that has been wrong since go-live is normal and will never be flagged.
  • Profiling every table because it is one checkbox. The metrics tables and the compute are real cost, and alert volume from tables nobody reads is what teaches people to ignore the channel.
  • Monitoring a table whose grain is about to change. Your own deployment invalidates the learned baseline, the monitor alarms on a correct change, and the reflex is to disable it rather than reset it.
  • Assuming lineage completeness during root cause. Records are emitted only where lineage can be inferred, so a missing edge is not evidence that nothing reads the table.

Straight to the documentation

The generic platform material lives in the official docs and is not restated here. These are the pages worth having open while you work through this route.

  • Data quality monitoringthe post-rename overview — read it to see which half of the old product you are actually enabling
  • INFORMATION_SCHEMA.COLUMNSthe full column list, and the privilege rule that makes an empty result ambiguous
  • Databricks SQL alertshow a zero-rows-means-pass query becomes a scheduled alert with a destination
  • External lineagewhat it takes for root cause to reach past SAP and Power BI instead of stopping at the platform edge
  • Use liquid clustering for tablesfct_order_line is clustered by (customer_sk, order_ts) — which is why a monitor windowed on date_sk alone prunes less than it looks like it should
Platform facts on this page verified 29.07.2026 against the official documentation. Volatile claims are anchored to the currency register.