The reliability, ownership, timeout hygiene and cost of Jobs and Lakeflow pipelines. Failures and wasted DBUs, all-purpose placement, missing timeouts, queueing, cold starts, retries and orphans.
War storyThe nightly job that paid the interactive rateA jobs bill up a third month-over-month, and nobody added a job.system.lakeflow.jobsThe catalog of Jobs (workflow definitions), one SCD2 change-history table.
(workspace_id, job_id) per configuration change. You must dedupe to the latest row with QUALIFY ROW_NUMBER() OVER (PARTITION BY workspace_id, job_id ORDER BY change_time DESC) = 1 or job counts inflate.workspace_id, job_id (unique only *within* a workspace), name, change_time (SCD2 ordering key), delete_time (non-NULL = user-deleted; filter IS NULL for active jobs), timeout_seconds (job-level timeout), paused, create_time, creator_user_name / run_as_user_name (identity), run_as, creator_id, health_rules (array/struct of configured health rules).SELECT on the system catalog (an admin must GRANT access to the system.lakeflow schema). Several columns are late-populated: creator_user_name, run_as_user_name, health_rules not populated before ~late-Nov-2025; timeout_seconds not before ~early-Dec-2025 — so on short-history accounts these are NULL for every job and must degrade to "not assessed," never read as a finding. FedRAMP/redacted workspaces emit __REDACTED__ for identities.system.lakeflow.job_tasksThe per-task definition within each job, also SCD2.
(workspace_id, job_id, task_key) per change. task_key is unique only within a job. Dedupe to latest by change_time.workspace_id, job_id, task_key, timeout_seconds (task-level timeout), change_time, delete_time.SELECT on system needed. timeout_seconds late-populated (~early-Dec-2025) → NULL degrades to "not assessed."system.lakeflow.job_run_timelineThe run-level execution log — the reliability workhorse of this domain.
COUNT(*) ≠ COUNT(DISTINCT run_id). The end row (final slice) is the only one carrying result_state, termination_code, and the final durations — every reliability query filters result_state IS NOT NULL to isolate it. Retries reuse the same run_id, appearing as extra end rows.workspace_id, job_id, run_id, run_type (JOB_RUN / SUBMIT_RUN / WORKFLOW_RUN), trigger_type (incl. RETRY_ON_FAILURE), result_state (SUCCEEDED / FAILED / ERROR / TIMED_OUT / SKIPPED / CANCELLED / BLOCKED), termination_code (root-cause code, e.g. WORKSPACE_RUN_LIMIT_EXCEEDED, MAX_JOB_QUEUE_SIZE_EXCEEDED, CLUSTER_ERROR, STORAGE_ACCESS_ERROR), termination_type (coarser class — value enum unverified), period_start_time / period_end_time (equal when a run never began executing), and phase durations queue_duration_seconds, setup_duration_seconds, execution_duration_seconds, cleanup_duration_seconds, run_duration_seconds.termination_code not populated before ~Aug-2024; queue_duration_seconds, the phase *_duration_seconds, and termination_type not before ~late-Nov/early-Dec-2025. queue_duration_seconds exists only here, not on job_task_run_timeline. Empty if the account runs no jobs.system.lakeflow.job_task_run_timelineTask-level execution log, one grain finer than job_run_timeline.
result_state IS NOT NULL) carries final task state and execution_duration_seconds. Joins up to the run via job_run_id = job_run_timeline.run_id (+ workspace_id).workspace_id, job_id, run_id / job_run_id, task_key, result_state, execution_duration_seconds, compute_ids (array of cluster ids the task ran on), period_start_time / period_end_time.execution_duration_seconds late-populated (~late-Nov-2025). compute_ids may be empty on older rows; EXPLODE silently drops NULL/empty arrays (report as a separate "not assessed" bucket).system.lakeflow.pipelinesThe catalog of Lakeflow Declarative Pipelines (DLT), SCD2.
(workspace_id, pipeline_id) per change. Dedupe to latest by change_time.workspace_id, pipeline_id (unique only within a workspace), name, pipeline_type, created_by, run_as, change_time, delete_time, and settings (nested: serverless, development, continuous, photon, edition [CORE/PRO/ADVANCED — the *product* edition, not the billing tier], channel).TABLE_OR_VIEW_NOT_FOUND. UC required. Whether settings is a STRUCT (settings.serverless) or MAP (settings['serverless']) is unverified — dot-access may error on some accounts.system.lakeflow.pipeline_update_timelineExecution log of pipeline updates (refreshes).
result_state. request_id groups retried/restarted updates.workspace_id, pipeline_id, update_id, request_id, update_type (FULL_REFRESH / REFRESH / VALIDATE), trigger_type (incl. RETRY_ON_FAILURE), result_state (COMPLETED / FAILED / CANCELED — one L), period_start_time / period_end_time (active-second proxy).system.billing.usageThe metered-usage fact table — used here to attach dollars/DBUs to jobs and pipelines.
record_type. record_type (ORIGINAL / RETRACTION / RESTATEMENT) already nets out to a correct total when summed.workspace_id, usage_date, usage_unit (filter = 'DBU' so bytes/hours/tokens never blend in), usage_quantity, sku_name, cloud, usage_end_time, and the usage_metadata struct: job_id (no run_id — DBUs attribute to the job, not the individual run), dlt_pipeline_id, dlt_update_id, dlt_maintenance_id (maintenance/housekeeping DBUs, summed separately).SELECT on system needed. All joins use (workspace_id, job_id) or (workspace_id, dlt_pipeline_id) because those ids are unique only within a workspace.system.billing.list_pricesList (rack-rate) price reference for estimating dollars.
(sku_name, cloud, usage_unit, currency_code) per price-effectivity window.sku_name, cloud, currency_code, usage_unit, price_start_time, price_end_time (NULL = currently effective, open interval), and pricing.effective_list.default (the list rate — an estimate only, not the account's negotiated/discounted price).usage_end_time BETWEEN price_start_time AND price_end_time.system.compute.clustersCluster inventory, used cross-domain to classify the compute a task ran on.
(workspace_id, cluster_id), SCD2; dedupe to latest by change_time.workspace_id, cluster_id, cluster_source (UI / API = all-purpose, vs job-created), change_time.job_task_run_timeline.compute_ids; confirm compute_ids is populated on the target workspace.system.* schemas aren’t uniformly available — several (e.g. system.query, system.lakeflow) are still in preview or must be enabled per-metastore. A query that returns nothing usually means its schema isn’t enabled for your account yet.system.billing.list_prices) unless a query explicitly joins account_prices.One row per job that had at least one failed run in the trailing window, with its distinct/failed run counts, the latest failure's termination code, total job DBUs, and a wasted-DBU proxy (job DBUs scaled by failed-run share).
system.lakeflow.job_run_timelinesystem.billing.usageOne row = a job with at least one failed run in the window. The column that matters is wasted_dbus_proxy - the job's net DBUs scaled by the share of its runs that failed; est_usd_list turns that into a rough list-price dollar figure. last_failed_termination_code tells you why the most recent failure happened.
RequiresSELECT on system.lakeflow, system.billing; GA
Failing jobs still burn compute — a job that fails after 55 minutes bills the full 55 minutes of DBUs before it errors out. This query surfaces the biggest DBU-burning failing jobs so an admin can prioritize reliability fixes by dollar impact rather than by raw failure count, and pairs each with the latest failure's root-cause code to point at the fix (quota limits, cluster errors, storage access). The wasted_dbus_proxy is an honest prioritization signal, not an exact refundable amount, so it ranks remediation effort where it recovers the most compute spend.
WITH end_rows AS (
SELECT workspace_id, job_id, run_id, result_state, termination_code, period_start_time
FROM system.lakeflow.job_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp()) -- drop incomplete current day
AND result_state IS NOT NULL -- end row only
),
job_runs AS (
SELECT workspace_id, job_id,
COUNT(DISTINCT run_id) AS distinct_runs,
COUNT(DISTINCT CASE WHEN result_state IN ('FAILED','ERROR','TIMED_OUT')
THEN run_id END) AS failed_runs
FROM end_rows
GROUP BY workspace_id, job_id
),
-- ARGMAX, not MAX: the termination_code of the LATEST failed run, per (workspace_id, job_id).
-- MAX(termination_code) would pick the lexicographically-largest code, not the most recent.
last_fail AS (
SELECT workspace_id, job_id, termination_code AS last_failed_termination_code
FROM end_rows
WHERE result_state IN ('FAILED','ERROR','TIMED_OUT')
QUALIFY ROW_NUMBER() OVER (
PARTITION BY workspace_id, job_id ORDER BY period_start_time DESC
) = 1
),
-- List price effective at each usage row's time, keyed by sku_name+cloud+usage_unit.
price AS (
SELECT sku_name, cloud, usage_unit, price_start_time, price_end_time,
CAST(pricing.default AS DOUBLE) AS list_rate
FROM system.billing.list_prices
),
-- DBUs billed to each job over the window. usage_metadata.job_id is unique only within a
-- workspace, so we attribute on (workspace_id, job_id). Net across ALL record_types; DBU only.
-- Pre-aggregated by exactly (workspace_id, job_id) so the LEFT JOIN below stays strictly 1:1.
job_dbus AS (
SELECT u.workspace_id,
u.usage_metadata.job_id AS job_id,
SUM(u.usage_quantity) AS net_usage_quantity,
SUM(u.usage_quantity * COALESCE(p.list_rate, 0)) AS est_usd_list
FROM system.billing.usage u
LEFT JOIN price p
ON u.sku_name = p.sku_name AND u.cloud = p.cloud AND u.usage_unit = p.usage_unit
AND u.usage_end_time >= p.price_start_time
AND (p.price_end_time IS NULL OR u.usage_end_time < p.price_end_time)
WHERE u.usage_date >= dateadd(day, -:period_days, current_date())
AND u.usage_date < current_date()
AND upper(u.usage_unit) = 'DBU'
AND u.usage_metadata.job_id IS NOT NULL
GROUP BY u.workspace_id, u.usage_metadata.job_id
)
SELECT r.workspace_id,
r.job_id,
r.distinct_runs,
r.failed_runs,
lf.last_failed_termination_code,
COALESCE(d.net_usage_quantity, 0) AS net_job_dbus,
-- Wasted-DBU proxy: job DBUs scaled by the share of runs that failed. A proxy,
-- not exact run-level waste (usage_metadata has no run_id) - see caveats.
CASE WHEN r.distinct_runs > 0
THEN COALESCE(d.net_usage_quantity, 0) * (r.failed_runs / r.distinct_runs)
ELSE 0 END AS wasted_dbus_proxy,
COALESCE(d.net_usage_quantity, 0) AS net_dbus,
COALESCE(d.est_usd_list, 0) AS est_usd_list,
-- status: worst-first band on failed-run count and wasted-DBU magnitude (field heuristic;
-- :warn_failed_runs/:crit_failed_runs and :warn_wasted_dbus/:crit_wasted_dbus).
CASE
WHEN r.failed_runs IS NULL THEN 'NOT_ASSESSED'
WHEN r.failed_runs >= :crit_failed_runs
OR (CASE WHEN r.distinct_runs > 0
THEN COALESCE(d.net_usage_quantity, 0) * (r.failed_runs / r.distinct_runs)
ELSE 0 END) >= :crit_wasted_dbus
THEN 'CRITICAL'
WHEN r.failed_runs >= :warn_failed_runs
OR (CASE WHEN r.distinct_runs > 0
THEN COALESCE(d.net_usage_quantity, 0) * (r.failed_runs / r.distinct_runs)
ELSE 0 END) >= :warn_wasted_dbus
THEN 'WARN'
ELSE 'OK'
END AS status
FROM job_runs r
LEFT JOIN last_fail lf
ON r.workspace_id = lf.workspace_id AND r.job_id = lf.job_id
LEFT JOIN job_dbus d
ON r.workspace_id = d.workspace_id AND r.job_id = d.job_id
WHERE r.failed_runs > 0
ORDER BY wasted_dbus_proxy DESC, failed_runs DESC:period_daysdefault 30rolling window in days:warn_failed_runsdefault 3failed runs for a job in the window that flags WARN:crit_failed_runsdefault 10failed runs that flags CRITICAL:warn_wasted_dbusdefault 10wasted-DBU proxy that flags WARN:crit_wasted_dbusdefault 50wasted-DBU proxy that flags CRITICALnet_dbus) and a list-price USD estimate (est_usd_list) per job, so the finding now shows the waste in DBUs and directional dollars — the estimate prices DBUs at list, not your negotiated rate.| Column | How to read it |
|---|---|
| workspace_id | The workspace the job belongs to; part of every join key because job_id is unique only within a workspace. |
| job_id | The Databricks job (workflow) identifier, passed through unmasked; unique only within its workspace. |
| distinct_runs | Integer count of distinct run_ids that reached an end row (result_state populated) in the window — the denominator of the failed share. |
| failed_runs | Integer count of distinct run_ids whose end state was FAILED, ERROR, or TIMED_OUT. Always >= 1 in this result set. |
| last_failed_termination_code | The termination_code of the most recent failed run (by period_start_time), e.g. CLUSTER_ERROR or WORKSPACE_RUN_LIMIT_EXCEEDED. NULL means the code was not populated (pre-Aug-2024 or short-history) — read as 'unknown', not 'no cause'. |
| net_job_dbus | Total DBUs (not dollars) billed to this job over the window, netted across all record_types and filtered to usage_unit='DBU'. COALESCE'd to 0 when no billing row matched. |
| wasted_dbus_proxy | Proxy DBUs (not dollars) attributed to failed runs = net_job_dbus * (failed_runs / distinct_runs). A prioritization signal, not exact run-level waste, since billing has no run_id. Rank on this. |
| net_dbus | Exact billed DBUs (usage_unit='DBU') attributed to this job (workspace_id + job_id) over the window, netted across all record_types (COALESCE 0 when no billing row matched). DBUs, NOT dollars. Same total as net_job_dbus above (both are the job’s billed DBUs). |
| est_usd_list | List-price USD ESTIMATE = net_dbus × list_prices.pricing.default (‘est · at list’). NOT your negotiated invoice rate (not in any system table) and excludes cloud infra/egress $ — directional only, never a real dollar figure. |
few failed_runs and a low wasted_dbus_proxy relative to :warn_wasted_dbus - field heuristic; tune :warn_failed_runs/:warn_wasted_dbus for your account.
failed_runs at/above :warn_failed_runs or wasted_dbus_proxy at/above :warn_wasted_dbus (WARN); either at/above :crit_failed_runs / :crit_wasted_dbus (CRITICAL) - field heuristic.
| workspace_id | job_id | distinct_runs | failed_runs | last_failed_termination_code | net_job_dbus | wasted_dbus_proxy | net_dbus | est_usd_list |
|---|---|---|---|---|---|---|---|---|
| 1234567890123456 | 884422 | 100 | 25 | CLUSTER_ERROR | 800 | 200 | 800 | 400.00 |
| 1234567890123456 | 775511 | 10 | 10 | STORAGE_ACCESS_ERROR | 1500 | 1500 | 1500 | 750.00 |
| 9876543210987654 | 660033 | 48 | 6 | WORKSPACE_RUN_LIMIT_EXCEEDED | 1200 | 150 | 1200 | 600.00 |
Rank the returned jobs by wasted_dbus_proxy, open the top few in the Jobs UI, and use last_failed_termination_code to drive the fix (raise concurrency/queue limits for quota codes, fix compute/storage config for CLUSTER_ERROR/STORAGE_ACCESS_ERROR); target the biggest DBU-burning failing jobs first, treating the proxy as a prioritization signal not an exact billed refund.
Feeds into: Job Health page — top failing jobs ranked by wasted DBUs (reliability-1)
Per-job, per-cluster task-run counts over the trailing 30 days, tagged with the cluster's source so you can spot job tasks running on all-purpose (UI/API) compute instead of cheaper job clusters.
system.lakeflow.job_task_run_timelinesystem.compute.clustersOne row = one (job, all-purpose cluster) placement over the window. cluster_source is the column that matters - UI or API means the job ran on an interactive/all-purpose cluster (the anti-pattern) instead of a cheaper jobs cluster. est_usd_list_share is a naive even split of the shared cluster's list-price cost across the jobs on it.
RequiresSELECT on system.lakeflow, system.compute, system.billing; GA
All-purpose (interactive) compute is billed at a materially higher DBU SKU than jobs compute for the same work, so any scheduled job task pinned to a UI- or API-created cluster is quietly overpaying every run. Grouping task-run counts by job and cluster surfaces exactly which jobs are the offenders and how often they run there, letting you prioritize migrating high-frequency jobs onto job clusters (or serverless) for a direct, recurring bill reduction. A NULL cluster_source (unmatched compute_id) is not proof of good behavior — it means the cluster could not be classified and needs manual review.
WITH task_compute AS (
SELECT workspace_id, job_id, run_id, task_key, EXPLODE(compute_ids) AS compute_id
FROM system.lakeflow.job_task_run_timeline
WHERE period_start_time >= dateadd(DAY, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp())
AND result_state IS NOT NULL
AND compute_ids IS NOT NULL
),
-- Runs dropped because compute_ids was NULL/empty -> reported as NOT_ASSESSED, never as zero.
dropped AS (
SELECT workspace_id, COUNT(DISTINCT run_id) AS dropped_runs
FROM system.lakeflow.job_task_run_timeline
WHERE period_start_time >= dateadd(DAY, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp())
AND result_state IS NOT NULL
AND compute_ids IS NULL
GROUP BY workspace_id
),
-- distinct jobs sharing each cluster in the window (denominator for the naive split).
cluster_jobs AS (
SELECT workspace_id, compute_id, COUNT(DISTINCT job_id) AS jobs_sharing_cluster
FROM task_compute
GROUP BY workspace_id, compute_id
),
price AS (
SELECT sku_name, cloud, usage_unit, price_start_time, price_end_time,
CAST(pricing.default AS DOUBLE) AS list_rate
FROM system.billing.list_prices
),
cost_rollup AS (
-- Pre-aggregated cost per all-purpose cluster over the SAME window as the finding.
SELECT u.workspace_id,
u.usage_metadata.cluster_id AS cluster_id,
SUM(u.usage_quantity) AS net_dbus,
SUM(u.usage_quantity * COALESCE(p.list_rate, 0)) AS est_usd_list
FROM system.billing.usage u
LEFT JOIN price p
ON u.sku_name = p.sku_name AND u.cloud = p.cloud AND u.usage_unit = p.usage_unit
AND u.usage_end_time >= p.price_start_time
AND (p.price_end_time IS NULL OR u.usage_end_time < p.price_end_time)
WHERE upper(u.usage_unit) = 'DBU'
AND u.usage_metadata.cluster_id IS NOT NULL
AND u.usage_date >= dateadd(DAY, -:period_days, current_date())
AND u.usage_date < current_date()
GROUP BY u.workspace_id, u.usage_metadata.cluster_id
),
finding AS (
SELECT tc.workspace_id,
CAST(tc.job_id AS STRING) AS job_id,
CAST(tc.compute_id AS STRING) AS compute_id,
c.cluster_source,
COUNT(DISTINCT tc.run_id) AS task_runs,
COALESCE(MAX(cr.net_dbus), 0) AS net_dbus,
COALESCE(MAX(cr.est_usd_list), 0) AS est_usd_list,
MAX(cj.jobs_sharing_cluster) AS jobs_sharing_cluster
FROM task_compute tc
LEFT JOIN (
SELECT workspace_id, cluster_id, cluster_source
FROM system.compute.clusters
QUALIFY ROW_NUMBER() OVER (PARTITION BY workspace_id, cluster_id ORDER BY change_time DESC) = 1
) c
ON tc.workspace_id = c.workspace_id AND tc.compute_id = c.cluster_id
LEFT JOIN cost_rollup cr
ON tc.workspace_id = cr.workspace_id AND tc.compute_id = cr.cluster_id
LEFT JOIN cluster_jobs cj
ON tc.workspace_id = cj.workspace_id AND tc.compute_id = cj.compute_id
GROUP BY tc.workspace_id, tc.job_id, tc.compute_id, c.cluster_source
)
SELECT
workspace_id, job_id, compute_id, cluster_source, task_runs,
net_dbus, est_usd_list, jobs_sharing_cluster,
-- naive even split of the shared cluster's list-price cost across the jobs on it.
est_usd_list / NULLIF(jobs_sharing_cluster, 0) AS est_usd_list_share,
CASE
WHEN cluster_source IS NULL THEN 'NOT_ASSESSED'
WHEN cluster_source IN ('UI', 'API')
AND est_usd_list / NULLIF(jobs_sharing_cluster, 0) >= :crit_share_usd THEN 'CRITICAL'
WHEN cluster_source IN ('UI', 'API') THEN 'WARN'
ELSE 'OK'
END AS status
FROM finding
UNION ALL
SELECT
workspace_id,
CAST(NULL AS STRING) AS job_id,
CAST(NULL AS STRING) AS compute_id,
'null compute_ids' AS cluster_source,
dropped_runs AS task_runs,
CAST(NULL AS DOUBLE) AS net_dbus,
CAST(NULL AS DOUBLE) AS est_usd_list,
CAST(NULL AS BIGINT) AS jobs_sharing_cluster,
CAST(NULL AS DOUBLE) AS est_usd_list_share,
'NOT_ASSESSED' AS status
FROM dropped
ORDER BY est_usd_list_share DESC
LIMIT :top_n:period_daysdefault 30rolling window in days:crit_share_usddefault 100naive per-job list-$ share on an all-purpose cluster that flags CRITICAL:top_ndefault 500row capnet_dbus) and a list-price USD estimate (est_usd_list) per cluster, so the finding now shows the waste in DBUs and directional dollars — the estimate prices DBUs at list, not your negotiated rate.| Column | How to read it |
|---|---|
| workspace_id | The workspace the job and cluster belong to. All ids below are unique only within this workspace, so always read a row as the (workspace_id, job_id, compute_id) tuple. |
| job_id | The job (workflow) whose task ran on this cluster. Workspace-scoped; join back to system.lakeflow.jobs on (workspace_id, job_id) to get the job name. |
| compute_id | The cluster id (one element of the task's compute_ids array) that the task executed on. Matches system.compute.clusters.cluster_id within the workspace. |
| cluster_source | How the cluster was created: 'UI' or 'API' = all-purpose (the higher-cost anti-pattern to migrate); job-created sources = expected. NULL = the compute_id did not match a deduped cluster row (not assessed — investigate, do not treat as compliant). |
| task_runs | COUNT(DISTINCT run_id): the number of distinct job runs whose tasks used this cluster in the window. A high value on a UI/API cluster is a frequently-repeating overspend, not a one-off. |
| net_dbus | Exact billed DBUs (usage_unit='DBU') attributed to this all-purpose cluster (workspace_id + compute_id) over the fixed 30-day window, netted across all record_types (COALESCE 0 when no billing row matched). DBUs, NOT dollars. FULL cluster DBUs — a shared all-purpose cluster serving several jobs shows its whole spend on each job row (billing has no clean per-job split of an interactive cluster). |
| est_usd_list | List-price USD ESTIMATE = net_dbus × list_prices.pricing.default (‘est · at list’). NOT your negotiated invoice rate (not in any system table) and excludes cloud infra/egress $ — directional only, never a real dollar figure. |
cluster_source = JOB, i.e. the task ran on dedicated jobs compute - est_usd_list_share is then informational only (field heuristic).
cluster_source IN ('UI','API') (WARN), especially with est_usd_list_share at/above :crit_share_usd (CRITICAL) - a job pinned to an always-on interactive cluster (field heuristic). The trailing NOT_ASSESSED summary row counts runs dropped for null compute_ids; do not read those as "clean".
| workspace_id | job_id | compute_id | cluster_source | task_runs | net_dbus | est_usd_list |
|---|---|---|---|---|---|---|
| 1234567890123456 | 884422 | 0715-091234-ab12cd34 | UI | 612 | 14200 | 7100.00 |
| 1234567890123456 | 775533 | 0715-090011-jobclu99 | JOB | 1440 | 30500 | 15250.00 |
| 9876543210987654 | 660011 | 0715-084455-zz99yy88 | API | 48 | 2600 | 1300.00 |
Filter the output to cluster_source IN ('UI','API'), rank the resulting (workspace_id, job_id, compute_id) rows by task_runs, and migrate the highest-frequency offenders off all-purpose clusters onto job clusters or serverless; separately triage NULL cluster_source rows to confirm the compute could not be classified before assuming compliance.
Feeds into: jobs-on-all-purpose
A 30-day census of completed job runs bucketed by workspace, launch type, trigger, final outcome, and root-cause code, with raw-row, distinct-run, and failed-row counts for each bucket.
system.lakeflow.job_run_timelineOne row = a (workspace, run_type, trigger_type, result_state, termination_code) combination in the window. The column that matters is failed_run_rows - how many end rows landed in FAILED/ERROR/TIMED_OUT for that combination; distinct_runs corrects for retries repeating a run_id.
RequiresSELECT on system.lakeflow; GA
This is the baseline reliability posture for the whole job fleet - it tells an admin which trigger types and which error codes actually dominate failures, so remediation effort goes where the failures concentrate rather than where they are merely visible. A spike in `WORKSPACE_RUN_LIMIT_EXCEEDED` or `MAX_JOB_QUEUE_SIZE_EXCEEDED` signals a capacity or concurrency ceiling that is silently killing runs, while `CLUSTER_ERROR` or `STORAGE_ACCESS_ERROR` points at infrastructure or permissions. Heavy `RETRY_ON_FAILURE` volume is the early warning of flapping jobs that burn compute passing on the second or third attempt. Left unread, these failures cost missed SLAs, wasted DBUs on doomed retries, and on-call time chasing symptoms instead of the dominant cause.
WITH end_rows AS (
SELECT workspace_id, job_id, run_id, run_type, trigger_type,
result_state, termination_code, period_start_time, period_end_time
FROM system.lakeflow.job_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp()) -- drop incomplete current day
AND result_state IS NOT NULL -- end row only
)
SELECT workspace_id, run_type, trigger_type, result_state, termination_code,
COUNT(*) AS run_rows,
COUNT(DISTINCT run_id) AS distinct_runs,
SUM(CASE WHEN result_state IN ('FAILED','ERROR','TIMED_OUT') THEN 1 ELSE 0 END) AS failed_run_rows,
-- status: worst-first band on failed run rows per group (field heuristic; :warn_failed_run_rows / :crit_failed_run_rows).
CASE
WHEN SUM(CASE WHEN result_state IN ('FAILED','ERROR','TIMED_OUT') THEN 1 ELSE 0 END) >= :crit_failed_run_rows THEN 'CRITICAL'
WHEN SUM(CASE WHEN result_state IN ('FAILED','ERROR','TIMED_OUT') THEN 1 ELSE 0 END) >= :warn_failed_run_rows THEN 'WARN'
ELSE 'OK'
END AS status
FROM end_rows
GROUP BY workspace_id, run_type, trigger_type, result_state, termination_code
ORDER BY failed_run_rows DESC:period_daysdefault 30rolling window in days:warn_failed_run_rowsdefault 5failed run rows in a group that flags WARN:crit_failed_run_rowsdefault 20failed run rows that flags CRITICALFAILED, ERROR, TIMED_OUT - while intentionally leaving SKIPPED, CANCELLED, and BLOCKED out of the failed tally.termination_code and trigger_type combinations dominate, so quota/limit hits and retry storms stand out from routine successes.| Column | How to read it |
|---|---|
| workspace_id | The workspace the runs executed in. Ids and run counts are scoped here - compare failures workspace by workspace. |
| run_type | How the run was launched: `JOB_RUN` (a scheduled/triggered job), `SUBMIT_RUN` (one-off API submit), or `WORKFLOW_RUN` (a run inside a parent workflow). |
| trigger_type | What triggered this run, including `RETRY_ON_FAILURE` - a high failed count on that trigger means jobs are repeatedly failing and retrying. |
| result_state | The final outcome of the run: `SUCCEEDED`, `FAILED`, `ERROR`, `TIMED_OUT`, `SKIPPED`, `CANCELLED`, or `BLOCKED`. The first three are the 'failed' set. |
| termination_code | The root-cause code for how the run ended (e.g. `WORKSPACE_RUN_LIMIT_EXCEEDED`, `MAX_JOB_QUEUE_SIZE_EXCEEDED`, `CLUSTER_ERROR`, `STORAGE_ACCESS_ERROR`). NULL here means the code was not recorded, not that there was no cause. |
| run_rows | Raw number of end rows in this group. Inflated by hourly slicing and retries - use it only relative to `distinct_runs`, not as a run count. |
| distinct_runs | The number of actual distinct runs in this group. This is the honest 'how many runs' figure. |
| failed_run_rows | End rows in this group that landed in the failed set. Because groups are split by `result_state`, this equals `run_rows` on a failed row and is 0 on a non-failed row - a convenience flag for filtering to failures. |
failed_run_rows low relative to distinct_runs, spread across many termination_codes rather than piled on one - field heuristic; tune :warn_failed_run_rows for your account.
failed_run_rows at/above :warn_failed_run_rows (WARN) or :crit_failed_run_rows (CRITICAL) for a single termination_code - field heuristic; one code dominating points at a systemic cause, not one-off flakiness.
| workspace_id | run_type | trigger_type | result_state | termination_code | run_rows | distinct_runs | failed_run_rows |
|---|---|---|---|---|---|---|---|
| 1234567890123456 | JOB_RUN | RETRY_ON_FAILURE | FAILED | CLUSTER_ERROR | 47 | 39 | 47 |
| 1234567890123456 | JOB_RUN | PERIODIC | TIMED_OUT | MAX_JOB_QUEUE_SIZE_EXCEEDED | 12 | 12 | 12 |
| 9876543210987654 | SUBMIT_RUN | ONE_TIME | SUCCEEDED | SUCCESS | 310 | 298 | 0 |
Read the rows where `result_state IN ('FAILED','ERROR','TIMED_OUT')`, rank the `termination_code` values by `distinct_runs` to find the dominant root causes, then drill into the workspaces and trigger types (especially `RETRY_ON_FAILURE`) that concentrate the failures — and route each top code to its owner (quota/limit codes to capacity planning, `CLUSTER_ERROR`/`STORAGE_ACCESS_ERROR` to platform).
Feeds into: failed runs; termination taxonomy; workload mix & hours
Per-workspace counts of active jobs that have a configured health rule versus none, with a separate bucket for jobs where the health_rules column has not been populated yet.
system.lakeflow.jobsOne row = a workspace. The columns that matter are jobs_with_health_rule vs active_jobs (coverage), and jobs_health_rules_null, which tells you whether the column is populated at all on this account before you trust the coverage number.
RequiresSELECT on system.lakeflow; GA (health_rules was added late Nov 2025)
Health rules are how a job self-reports when a run fails, runs too long, or misses an SLA — without them, a job can silently degrade or hang and no one is paged. This query tells an admin how many active jobs across each workspace have zero automated failure/duration alerting, which is a governance and reliability gap that directly precedes missed data deliveries and runaway compute. Crucially, it separates "confidently no rule" from "column not yet populated," so on short-history accounts you do not chase a false gap. Closing the uncovered count is a concrete step toward provable operational oversight.
WITH latest_jobs AS (
SELECT workspace_id, job_id, name, health_rules, delete_time
FROM system.lakeflow.jobs
QUALIFY ROW_NUMBER() OVER (
PARTITION BY workspace_id, job_id ORDER BY change_time DESC
) = 1
)
SELECT workspace_id,
COUNT(*) AS active_jobs,
-- NULL = column not yet populated (unknown), reported as a separate not-assessed bucket
SUM(CASE WHEN health_rules IS NULL THEN 1 ELSE 0 END) AS jobs_health_rules_null,
-- confident "has a health rule": non-null AND a non-empty rule set
SUM(CASE WHEN health_rules IS NOT NULL AND CARDINALITY(health_rules) > 0
THEN 1 ELSE 0 END) AS jobs_with_health_rule,
-- confident "no health rule": non-null but empty rule set (populated window, no rule)
SUM(CASE WHEN health_rules IS NOT NULL AND CARDINALITY(health_rules) = 0
THEN 1 ELSE 0 END) AS jobs_no_health_rule,
-- status: worst-first band on health-rule coverage pct (field heuristic; :warn_coverage_pct / :crit_coverage_pct).
CASE
WHEN SUM(CASE WHEN health_rules IS NULL THEN 1 ELSE 0 END) = COUNT(*) THEN 'NOT_ASSESSED'
WHEN 100.0 * SUM(CASE WHEN health_rules IS NOT NULL AND CARDINALITY(health_rules) > 0 THEN 1 ELSE 0 END)
/ NULLIF(COUNT(*) - SUM(CASE WHEN health_rules IS NULL THEN 1 ELSE 0 END), 0) < :crit_coverage_pct THEN 'CRITICAL'
WHEN 100.0 * SUM(CASE WHEN health_rules IS NOT NULL AND CARDINALITY(health_rules) > 0 THEN 1 ELSE 0 END)
/ NULLIF(COUNT(*) - SUM(CASE WHEN health_rules IS NULL THEN 1 ELSE 0 END), 0) < :warn_coverage_pct THEN 'WARN'
ELSE 'OK'
END AS status
FROM latest_jobs
WHERE delete_time IS NULL
GROUP BY workspace_id
ORDER BY CASE status WHEN 'CRITICAL' THEN 1 WHEN 'NOT_ASSESSED' THEN 2 WHEN 'WARN' THEN 3 ELSE 4 END, active_jobs DESC:warn_coverage_pctdefault 50percent of active jobs with a health rule below which a workspace flags WARN:crit_coverage_pctdefault 20percent below which it flags CRITICALhealth_rules column is empty/unpopulated.jobs_health_rules_null) bucket separate on purpose so a fully-blank column on a short-history account reads as "unknown," never as "zero coverage."| Column | How to read it |
|---|---|
| workspace_id | The workspace the counts belong to; all job ids are scoped to it. One row per workspace. |
| active_jobs | Total active, non-deleted job definitions in the workspace after SCD2 dedupe. This is the denominator; the three buckets below sum to it. |
| jobs_health_rules_null | Jobs whose health_rules column is NULL — treat as "not assessed, column not yet populated," NOT as "no rule configured." If this equals active_jobs, the account is short-history and coverage cannot be judged. |
| jobs_with_health_rule | Confident coverage: jobs with a non-null, non-empty health-rule set (CARDINALITY > 0). These have automated failure/duration alerting configured. |
| jobs_no_health_rule | Confident gap: jobs that exist in a populated window but carry an empty rule set (CARDINALITY = 0) — genuinely no health rule. These are the actionable remediation targets. |
jobs_health_rules_null is 0 (column populated) and jobs_with_health_rule / active_jobs stays above :warn_coverage_pct percent - field heuristic; tune for your account.
jobs_health_rules_null > 0 (coverage is not assessable yet - column not populated), or coverage falls below :warn_coverage_pct percent (WARN) / :crit_coverage_pct percent (CRITICAL) - field heuristic.
| workspace_id | active_jobs | jobs_health_rules_null | jobs_with_health_rule | jobs_no_health_rule |
|---|---|---|---|---|
| 1234567890123456 | 120 | 0 | 38 | 82 |
| 2345678901234567 | 64 | 64 | 0 | 0 |
| 3456789012345678 | 15 | 3 | 11 | 1 |
For each workspace, close the coverage gap: take the jobs_no_health_rule count and attach health rules (failure/duration alerting) to those jobs. Where jobs_health_rules_null dominates, do not act on it as a gap — wait for the health_rules column to populate before assessing coverage.
Feeds into: Job Health page — health-rule coverage across active jobs (reliability-3)
Per-workspace counts of active jobs whose creator or run-as identity is missing, whose creator and run-as differ (a handoff or service-principal pattern), or that look orphaned because no known identity runs them.
system.lakeflow.jobsOne row = a workspace. The columns that matter are jobs_owner_mismatch (creator and run-as identity both known and different - a handoff or service-principal pattern worth a look) and jobs_orphan_runas_unknown (run-as identity unknown while the creator is known - nobody clearly owns execution).
RequiresSELECT on system.lakeflow; GA (creator_user_name/run_as_user_name were added late Nov 2025)
Ownership accountability is a governance control: a job whose run-as identity is unknown has no clear responsible party, and a job whose creator and run-as differ is a legitimate but review-worthy handoff or service-principal pattern that can outlive the person who owns it. When an owner leaves or a service principal is decommissioned, these jobs silently keep running (or silently break) with no one accountable. This is a governance posture signal, not a cost figure — it tells you where to tighten ownership before an audit or an offboarding does it for you.
WITH latest_jobs AS (
SELECT workspace_id, job_id, name, creator_user_name, run_as_user_name, delete_time
FROM system.lakeflow.jobs
QUALIFY ROW_NUMBER() OVER (
PARTITION BY workspace_id, job_id ORDER BY change_time DESC
) = 1
),
-- normalise unavailable / redacted identity placeholders to NULL so they are never
-- counted as a real owner or a real mismatch.
norm AS (
SELECT workspace_id, job_id, name,
CASE WHEN creator_user_name IS NULL
OR trim(creator_user_name) = ''
OR upper(creator_user_name) = '__REDACTED__'
THEN NULL ELSE creator_user_name END AS creator_user_name,
CASE WHEN run_as_user_name IS NULL
OR trim(run_as_user_name) = ''
OR upper(run_as_user_name) = '__REDACTED__'
THEN NULL ELSE run_as_user_name END AS run_as_user_name
FROM latest_jobs
WHERE delete_time IS NULL
)
SELECT workspace_id,
COUNT(*) AS active_jobs,
SUM(CASE WHEN creator_user_name IS NULL THEN 1 ELSE 0 END) AS jobs_creator_null,
SUM(CASE WHEN run_as_user_name IS NULL THEN 1 ELSE 0 END) AS jobs_runas_null,
-- confident mismatch: both identities known & different (handoff / SP run-as)
SUM(CASE WHEN creator_user_name IS NOT NULL
AND run_as_user_name IS NOT NULL
AND creator_user_name <> run_as_user_name
THEN 1 ELSE 0 END) AS jobs_owner_mismatch,
-- orphan candidate: run-as identity unknown but creator known (no one clearly runs it)
SUM(CASE WHEN run_as_user_name IS NULL AND creator_user_name IS NOT NULL
THEN 1 ELSE 0 END) AS jobs_orphan_runas_unknown,
-- status: worst-first band on combined ownership-risk job count (field heuristic;
-- :warn_orphan_jobs / :crit_orphan_jobs).
CASE
WHEN COUNT(*) = SUM(CASE WHEN creator_user_name IS NULL THEN 1 ELSE 0 END) THEN 'NOT_ASSESSED'
WHEN (SUM(CASE WHEN creator_user_name IS NOT NULL AND run_as_user_name IS NOT NULL AND creator_user_name <> run_as_user_name THEN 1 ELSE 0 END)
+ SUM(CASE WHEN run_as_user_name IS NULL AND creator_user_name IS NOT NULL THEN 1 ELSE 0 END)) >= :crit_orphan_jobs THEN 'CRITICAL'
WHEN (SUM(CASE WHEN creator_user_name IS NOT NULL AND run_as_user_name IS NOT NULL AND creator_user_name <> run_as_user_name THEN 1 ELSE 0 END)
+ SUM(CASE WHEN run_as_user_name IS NULL AND creator_user_name IS NOT NULL THEN 1 ELSE 0 END)) >= :warn_orphan_jobs THEN 'WARN'
ELSE 'OK'
END AS status
FROM norm
GROUP BY workspace_id
ORDER BY (jobs_owner_mismatch + jobs_orphan_runas_unknown) DESC:warn_orphan_jobsdefault 5combined mismatch+orphan job count in a workspace that flags WARN:crit_orphan_jobsdefault 20that flags CRITICALjobs_creator_null) or unknown run-as identity (jobs_runas_null) so a workspace whose identity columns simply haven't populated yet degrades to not assessed rather than reading as all-orphaned.| Column | How to read it |
|---|---|
| workspace_id | The workspace the counts below belong to; all job ids and identities are scoped to it, so numbers never blend across workspaces. |
| active_jobs | Total active (non-deleted, latest-version) jobs in the workspace — the denominator for every other count. |
| jobs_creator_null | How many active jobs have no usable creator identity (NULL, blank, or a redacted/__REDACTED__ placeholder). If this equals active_jobs, the creator column is simply not populated on this account — treat as not assessed, not a finding. |
| jobs_runas_null | How many active jobs have no usable run-as identity, under the same normalization. Equal to active_jobs means the column is unpopulated (not assessed), not that every job is orphaned. |
| jobs_owner_mismatch | Confident mismatches: jobs where both creator and run-as are known, non-redacted, and different. A legitimate handoff or service-principal run-as pattern that is worth reviewing, not automatically a problem. |
| jobs_orphan_runas_unknown | Orphan candidates: jobs with a known creator but an unknown run-as identity — no one clearly runs them. This is a subset of jobs_runas_null (the portion that also has a known creator). |
jobs_owner_mismatch and jobs_orphan_runas_unknown both low relative to active_jobs, and jobs_creator_null/jobs_runas_null near 0 so the picture is trustworthy - field heuristic; tune :warn_orphan_jobs for your account.
(jobs_owner_mismatch + jobs_orphan_runas_unknown) at/above :warn_orphan_jobs (WARN) or :crit_orphan_jobs (CRITICAL) - field heuristic; this is a governance posture signal, not a cost figure.
| workspace_id | active_jobs | jobs_creator_null | jobs_runas_null | jobs_owner_mismatch | jobs_orphan_runas_unknown |
|---|---|---|---|---|---|
| 1234567890123456 | 120 | 0 | 0 | 8 | 0 |
| 2233445566778899 | 45 | 45 | 45 | 0 | 0 |
| 3344556677889900 | 60 | 2 | 5 | 6 | 3 |
Review the workspaces with non-zero jobs_orphan_runas_unknown and jobs_owner_mismatch first: reassign or confirm a valid run-as (service principal) for each orphan candidate, and validate that each mismatch is an intended handoff. Where jobs_creator_null / jobs_runas_null equals active_jobs, mark the workspace not assessed until the identity columns populate rather than acting on it.
Feeds into: Job Health page — job ownership / creator-vs-run_as mismatch & orphan candidates (governance-1)
Per-job queue-wait time (total seconds and approximate p95 per run) over the trailing window, with a separate counter of runs whose queue time is unknown, so admins can find jobs that sit waiting on capacity before they start.
system.lakeflow.job_run_timelineOne row = a job in the window. The column that matters is queue_s_p95 - the 95th-percentile seconds a run waited before starting; queue_s_total is the sum across all its runs. Consistently high p95 on the same job means it is waiting on capacity, not a one-off traffic spike.
RequiresSELECT on system.lakeflow; GA (queue_duration_seconds was added late Nov 2025)
Queue time is jobs sitting idle waiting on capacity or concurrency limits before they even start — latency your users feel and, indirectly, SLAs you miss, with no code bug to blame. Ranking jobs by total and p95 queue wait tells an admin exactly which workflows are starved and are candidates for higher concurrency limits or dedicated job compute. Because a NULL queue value means 'unknown,' not 'no wait,' the separate `runs_queue_null` bucket keeps a short-history account (where the column isn't populated yet) from being misread as a clean bill of health.
WITH end_rows AS (
SELECT workspace_id, job_id, run_id, queue_duration_seconds
FROM system.lakeflow.job_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp()) -- drop incomplete current day
AND result_state IS NOT NULL -- end row only
)
SELECT workspace_id, job_id,
COUNT(DISTINCT run_id) AS distinct_runs,
SUM(queue_duration_seconds) AS queue_s_total,
percentile_approx(queue_duration_seconds, 0.95) AS queue_s_p95,
SUM(CASE WHEN queue_duration_seconds IS NULL THEN 1 ELSE 0 END) AS runs_queue_null,
-- status: worst-first band on p95 queue seconds (field heuristic; :warn_queue_p95_s / :crit_queue_p95_s).
CASE
WHEN percentile_approx(queue_duration_seconds, 0.95) IS NULL THEN 'NOT_ASSESSED'
WHEN percentile_approx(queue_duration_seconds, 0.95) >= :crit_queue_p95_s THEN 'CRITICAL'
WHEN percentile_approx(queue_duration_seconds, 0.95) >= :warn_queue_p95_s THEN 'WARN'
ELSE 'OK'
END AS status
FROM end_rows
GROUP BY workspace_id, job_id
ORDER BY queue_s_p95 DESC:period_daysdefault 30rolling window in days:warn_queue_p95_sdefault 6095th-percentile queue seconds that flags WARN:crit_queue_p95_sdefault 300that flags CRITICALqueue_s_total) and a near-worst-case per-run wait (queue_s_p95) so occasional long stalls surface even when the total looks small.runs_queue_null), so a job with a not-yet-populated column reads as 'not assessed' rather than as having zero wait.| Column | How to read it |
|---|---|
| workspace_id | The workspace the job belongs to. Part of the identifying key because `job_id` is only unique within a workspace. |
| job_id | The job (workflow) identifier, unique only within its `workspace_id`. Together they name one job across all its runs in the window. |
| distinct_runs | Count of distinct completed runs for this job in the window (end rows only). The denominator for judging how representative the queue figures are. |
| queue_s_total | Total seconds this job's runs spent queued (waiting on capacity) over the window. NULL/empty means not populated, not zero. Bigger = more cumulative waiting. |
| queue_s_p95 | Approximate 95th-percentile per-run queue wait in seconds — the near-worst-case delay a run of this job sees before it starts. Use this to spot jobs with occasional long waits even if the total looks modest. |
| runs_queue_null | How many of this job's end rows had a NULL `queue_duration_seconds` (column not yet populated). If this equals `distinct_runs`, treat the job as 'not assessed — column not populated,' not as zero queue time. |
queue_s_p95 below :warn_queue_p95_s seconds - field heuristic; tune :warn_queue_p95_s for your account.
queue_s_p95 at/above :warn_queue_p95_s (WARN) or :crit_queue_p95_s (CRITICAL) seconds - field heuristic; also check runs_queue_null before trusting a "0" queue time on a short-history account.
| workspace_id | job_id | distinct_runs | queue_s_total | queue_s_p95 | runs_queue_null |
|---|---|---|---|---|---|
| 1234567890123456 | 884422113355 | 120 | 5400 | 180 | 0 |
| 1234567890123456 | 775533991122 | 48 | 60 | 3 | 0 |
| 9876543210987654 | 445566778899 | 30 | NULL | NULL | 30 |
Sort by `queue_s_p95` (ignoring jobs where `runs_queue_null` equals `distinct_runs` — those are "not assessed"). For the top queue-heavy jobs, raise the workspace/job concurrency limit or move them onto dedicated job compute so they stop waiting on capacity, then re-check next period.
Feeds into: Job Health page — queue-time analysis, jobs waiting on capacity (reliability-2)
Per-workspace counts of active job tasks and how many of them have no (or zero) task-level timeout configured, with a separate bucket for the "not yet populated" NULL case.
system.lakeflow.job_tasksOne row = a workspace. The column that matters is tasks_no_timeout - active tasks with no configured (or zero-second) timeout; net_dbus/est_usd_list next to it are the at-risk DBUs of any job that contains at least one such task.
RequiresSELECT on system.lakeflow, system.billing; GA (job_tasks.timeout_seconds was added late Nov 2025)
A task with no timeout can hang indefinitely — waiting on a stuck upstream, a wedged connection, or a runaway query — while its cluster keeps billing DBUs the entire time. Job-level timeouts don't always protect you: a single misbehaving task inside an otherwise-bounded job is a real gap, and this view catches it at the finer task grain. Setting sane task timeouts converts silent open-ended spend into a fast, visible failure. The extra NULL counter keeps you honest: on accounts with little history the timeout column simply isn't populated yet, so a high "no timeout" count may mean "not assessed" rather than a genuine governance failure.
WITH latest_tasks AS (
SELECT workspace_id, job_id, task_key, timeout_seconds, delete_time
FROM system.lakeflow.job_tasks
QUALIFY ROW_NUMBER() OVER (PARTITION BY workspace_id, job_id, task_key ORDER BY change_time DESC) = 1
),
price AS (
SELECT sku_name, cloud, usage_unit, price_start_time, price_end_time,
CAST(pricing.default AS DOUBLE) AS list_rate
FROM system.billing.list_prices
),
-- Pre-aggregated cost per (workspace_id, job_id) over the billing look-back window.
cost_rollup AS (
SELECT u.workspace_id,
u.usage_metadata.job_id AS job_id,
SUM(u.usage_quantity) AS net_dbus,
SUM(u.usage_quantity * COALESCE(p.list_rate, 0)) AS est_usd_list
FROM system.billing.usage u
LEFT JOIN price p
ON u.sku_name = p.sku_name AND u.cloud = p.cloud AND u.usage_unit = p.usage_unit
AND u.usage_end_time >= p.price_start_time
AND (p.price_end_time IS NULL OR u.usage_end_time < p.price_end_time)
WHERE upper(u.usage_unit) = 'DBU'
AND u.usage_metadata.job_id IS NOT NULL
AND u.usage_date >= date_add(current_date(), -:period_days)
AND u.usage_date < current_date()
GROUP BY u.workspace_id, u.usage_metadata.job_id
),
-- Jobs that have >=1 active no-timeout task, with their at-risk cost summed to workspace grain (1 row/ws).
risk_job_cost AS (
SELECT rj.workspace_id,
SUM(COALESCE(cr.net_dbus, 0)) AS dbus_at_risk,
SUM(COALESCE(cr.est_usd_list, 0)) AS est_usd_at_risk
FROM (
SELECT DISTINCT workspace_id, job_id
FROM latest_tasks
WHERE delete_time IS NULL
AND (timeout_seconds IS NULL OR timeout_seconds = 0)
) rj
LEFT JOIN cost_rollup cr
ON cr.workspace_id = rj.workspace_id AND cr.job_id = rj.job_id
GROUP BY rj.workspace_id
)
SELECT lt.workspace_id,
COUNT(*) AS active_tasks,
SUM(CASE WHEN lt.timeout_seconds IS NULL OR lt.timeout_seconds = 0 THEN 1 ELSE 0 END) AS tasks_no_timeout,
SUM(CASE WHEN lt.timeout_seconds IS NULL THEN 1 ELSE 0 END) AS tasks_timeout_null,
COALESCE(rjc.dbus_at_risk, 0) AS net_dbus,
COALESCE(rjc.est_usd_at_risk, 0) AS est_usd_list,
-- status: worst-first band on no-timeout task count (field heuristic; :warn_no_timeout_tasks / :crit_no_timeout_tasks).
CASE
WHEN SUM(CASE WHEN lt.timeout_seconds IS NULL OR lt.timeout_seconds = 0 THEN 1 ELSE 0 END) >= :crit_no_timeout_tasks THEN 'CRITICAL'
WHEN SUM(CASE WHEN lt.timeout_seconds IS NULL OR lt.timeout_seconds = 0 THEN 1 ELSE 0 END) >= :warn_no_timeout_tasks THEN 'WARN'
ELSE 'OK'
END AS status
FROM latest_tasks lt
LEFT JOIN risk_job_cost rjc ON rjc.workspace_id = lt.workspace_id
WHERE lt.delete_time IS NULL
GROUP BY lt.workspace_id, rjc.dbus_at_risk, rjc.est_usd_at_risk
ORDER BY tasks_no_timeout DESC:period_daysdefault 30billing look-back window for the cost rollup only (does not change this query's grain/filters/counts):warn_no_timeout_tasksdefault 5no-timeout tasks per workspace that flags WARN:crit_no_timeout_tasksdefault 20that flags CRITICALactive_tasks) alongside how many lack an effective timeout — either unset or explicitly zero (tasks_no_timeout).tasks_timeout_null) so you can tell a real gap from a column that Databricks hasn't back-filled yet.net_dbus) and a list-price USD estimate (est_usd_list) per workspace, so the finding now shows the waste in DBUs and directional dollars — the estimate prices DBUs at list, not your negotiated rate.| Column | How to read it |
|---|---|
| workspace_id | The Databricks workspace these task counts belong to. Task keys are unique only within a job and job/task ids are unique only within a workspace, so every number here is scoped to this one workspace. |
| active_tasks | Number of live (non-deleted) tasks in this workspace after collapsing each task to its latest configuration version. The denominator for coverage. |
| tasks_no_timeout | How many of those active tasks have no effective timeout — either `timeout_seconds` is NULL or it is 0. These are the tasks that can run open-ended. This count includes the NULL ones. |
| tasks_timeout_null | The subset of `tasks_no_timeout` where the value is specifically NULL (not a literal 0). Read as the 'not assessed' bucket: on short-history accounts the column is late-populated, so a large NULL count likely means the data isn't there yet, not that thousands of tasks were misconfigured. |
| net_dbus | Exact billed DBUs (usage_unit='DBU') attributed to this workspace (at-risk jobs, summed) over the :period_days cost window, netted across all record_types (COALESCE 0 when no billing row matched). DBUs, NOT dollars. AT-RISK subset: total DBUs of jobs that contain ≥1 no-timeout task, summed to workspace grain. Billing exposes only job_id (no task_key), so this OVER-attributes — read it as an upper bound, not the exact cost of the untimed tasks. |
| est_usd_list | List-price USD ESTIMATE = net_dbus × list_prices.pricing.default (‘est · at list’). NOT your negotiated invoice rate (not in any system table) and excludes cloud infra/egress $ — directional only, never a real dollar figure. |
tasks_no_timeout near 0 relative to active_tasks - field heuristic; tune :warn_no_timeout_tasks for your account.
tasks_no_timeout at/above :warn_no_timeout_tasks (WARN) or :crit_no_timeout_tasks (CRITICAL) - field heuristic.
| workspace_id | active_tasks | tasks_no_timeout | tasks_timeout_null | net_dbus | est_usd_list |
|---|---|---|---|---|---|
| 1234567890123456 | 148 | 92 | 40 | 26400 | 13200.00 |
| 2233445566778899 | 37 | 37 | 37 | 8100 | 4050.00 |
| 9988776655443322 | 210 | 6 | 0 | 1450 | 725.00 |
For each workspace, subtract `tasks_timeout_null` from `tasks_no_timeout` to get the tasks that are genuinely set to 0 or otherwise unbounded, then set an explicit task-level `timeout_seconds` on those tasks so a stuck task fails fast instead of billing compute indefinitely. If `tasks_timeout_null` equals `active_tasks`, treat the workspace as 'not assessed — column not yet populated' and re-run once history accrues.
Feeds into: jobs-no-timeout
Per-workspace counts of active jobs that have no (or zero) job-level timeout configured, alongside a separate "not yet populated" NULL bucket so the finding never over-claims.
system.lakeflow.jobsOne row = a workspace. The column that matters is jobs_no_timeout - active jobs with no configured (or zero-second) timeout; net_dbus/est_usd_list next to it are the DBUs/list-price dollars attributed to just that flagged subset over the cost-lookback window.
RequiresSELECT on system.lakeflow, system.billing; GA (timeout_seconds was added early Dec 2025)
A job with no timeout can hang indefinitely — a stuck query, a wedged cluster, or an infinite retry keeps consuming compute until someone notices, which can be hours or days of billed DBUs for zero output. Setting a timeout is a cheap, one-time governance control that caps the blast radius of any single runaway run. This query tells an admin, per workspace, how many active jobs lack that guardrail so they can prioritize which workspaces need a timeout policy applied. Because timeout_seconds only began populating in early Dec 2025, the query deliberately isolates the ambiguous NULL cases so you don't mistake "not yet recorded" for "no timeout set."
WITH latest_jobs AS (
SELECT workspace_id, job_id, name, timeout_seconds, paused, delete_time, create_time
FROM system.lakeflow.jobs
QUALIFY ROW_NUMBER() OVER (PARTITION BY workspace_id, job_id ORDER BY change_time DESC) = 1
),
price AS (
SELECT sku_name, cloud, usage_unit, price_start_time, price_end_time,
CAST(pricing.default AS DOUBLE) AS list_rate
FROM system.billing.list_prices
),
cost_rollup AS (
-- Per (workspace_id, job_id) DBUs + list-price $ over the cost-lookback window. job_id is NOT
-- globally unique -> keyed on workspace_id + job_id. Pre-aggregated (1 row per workspace_id+job_id)
-- so the LEFT JOIN below to the deduped job set is strictly 1:1 and cannot fan out.
SELECT u.workspace_id,
u.usage_metadata.job_id AS job_id,
SUM(u.usage_quantity) AS net_dbus,
SUM(u.usage_quantity * COALESCE(p.list_rate, 0)) AS est_usd_list
FROM system.billing.usage u
LEFT JOIN price p
ON u.sku_name = p.sku_name AND u.cloud = p.cloud AND u.usage_unit = p.usage_unit
AND u.usage_end_time >= p.price_start_time
AND (p.price_end_time IS NULL OR u.usage_end_time < p.price_end_time)
WHERE upper(u.usage_unit) = 'DBU'
AND u.usage_metadata.job_id IS NOT NULL
AND u.usage_date >= date_sub(current_date(), :period_days)
AND u.usage_date < current_date()
GROUP BY u.workspace_id, u.usage_metadata.job_id
)
SELECT lj.workspace_id,
COUNT(*) AS active_jobs,
SUM(CASE WHEN lj.timeout_seconds IS NULL OR lj.timeout_seconds = 0 THEN 1 ELSE 0 END) AS jobs_no_timeout,
SUM(CASE WHEN lj.timeout_seconds IS NULL THEN 1 ELSE 0 END) AS jobs_timeout_null,
SUM(CASE WHEN lj.create_time IS NULL THEN 1 ELSE 0 END) AS jobs_create_time_null,
-- cost visibility: DBUs / list-price $ attributed to the flagged subset only
-- (jobs with no timeout: timeout_seconds IS NULL OR = 0), summed over qualifying jobs.
SUM(CASE WHEN lj.timeout_seconds IS NULL OR lj.timeout_seconds = 0
THEN COALESCE(cr.net_dbus, 0) ELSE 0 END) AS net_dbus,
SUM(CASE WHEN lj.timeout_seconds IS NULL OR lj.timeout_seconds = 0
THEN COALESCE(cr.est_usd_list, 0) ELSE 0 END) AS est_usd_list,
-- status: worst-first band on no-timeout job count (field heuristic; :warn_no_timeout_jobs / :crit_no_timeout_jobs).
CASE
WHEN SUM(CASE WHEN lj.timeout_seconds IS NULL OR lj.timeout_seconds = 0 THEN 1 ELSE 0 END) >= :crit_no_timeout_jobs THEN 'CRITICAL'
WHEN SUM(CASE WHEN lj.timeout_seconds IS NULL OR lj.timeout_seconds = 0 THEN 1 ELSE 0 END) >= :warn_no_timeout_jobs THEN 'WARN'
ELSE 'OK'
END AS status
FROM latest_jobs lj
LEFT JOIN cost_rollup cr
ON lj.workspace_id = cr.workspace_id AND lj.job_id = cr.job_id
WHERE lj.delete_time IS NULL
GROUP BY lj.workspace_id
ORDER BY jobs_no_timeout DESC:period_daysdefault 30cost-lookback window for the flagged-subset DBU/dollar rollup:warn_no_timeout_jobsdefault 5no-timeout jobs per workspace that flags WARN:crit_no_timeout_jobsdefault 20that flags CRITICALjobs_no_timeout) — the governance gap worth acting on.jobs_timeout_null), so you can tell a real 'no timeout' finding apart from a column that simply hasn't been populated yet on a short-history account.create_time, a signal that the metadata itself is incomplete.net_dbus) and a list-price USD estimate (est_usd_list) per workspace, so the finding now shows the waste in DBUs and directional dollars — the estimate prices DBUs at list, not your negotiated rate.| Column | How to read it |
|---|---|
| workspace_id | The workspace the counts belong to. Job ids are unique only within a workspace, so every number here is per-workspace, never account-wide. |
| active_jobs | Total active (latest-version, non-deleted) jobs in the workspace — the denominator for the timeout-coverage picture. |
| jobs_no_timeout | Jobs whose timeout_seconds is NULL OR 0 — i.e. no effective timeout. This is the headline finding, but note it includes the ambiguous NULLs, so read it together with jobs_timeout_null. |
| jobs_timeout_null | Jobs whose timeout_seconds is specifically NULL. If this equals jobs_no_timeout (and roughly equals active_jobs), the column is likely just not-yet-populated on this account — degrade to 'not assessed' rather than reporting a violation. The gap between jobs_no_timeout and this is the count of jobs with an explicit zero. |
| jobs_create_time_null | Jobs missing a create_time value — a metadata-completeness signal; a high count hints the jobs table is only partially populated for this workspace. |
| net_dbus | Exact billed DBUs (usage_unit='DBU') attributed to this workspace (flagged jobs, summed) over the :period_days cost window, netted across all record_types (COALESCE 0 when no billing row matched). DBUs, NOT dollars. Attributed to the FLAGGED subset only (jobs with timeout_seconds NULL or 0), summed over qualifying jobs — not all active jobs. |
| est_usd_list | List-price USD ESTIMATE = net_dbus × list_prices.pricing.default (‘est · at list’). NOT your negotiated invoice rate (not in any system table) and excludes cloud infra/egress $ — directional only, never a real dollar figure. |
jobs_no_timeout near 0 relative to active_jobs - field heuristic; tune :warn_no_timeout_jobs for your account.
jobs_no_timeout at/above :warn_no_timeout_jobs (WARN) or :crit_no_timeout_jobs (CRITICAL), especially where est_usd_list is also high - field heuristic.
| workspace_id | active_jobs | jobs_no_timeout | jobs_timeout_null | jobs_create_time_null | net_dbus | est_usd_list |
|---|---|---|---|---|---|---|
| 1234567890123456 | 120 | 18 | 6 | 0 | 5200 | 2600.00 |
| 2233445566778899 | 64 | 64 | 64 | 64 | 9800 | 4900.00 |
| 9988776655443322 | 305 | 3 | 3 | 0 | 640 | 320.00 |
For each workspace where jobs_no_timeout materially exceeds jobs_timeout_null (i.e. real explicit-zero/no-timeout jobs, not just an unpopulated column), apply a default job-level timeout to those jobs so a single hung run can't burn compute indefinitely; where jobs_timeout_null ≈ active_jobs, defer and re-run once the column has populated.
Feeds into: jobs-no-timeout; stale/zombie jobs
A per-job count of Databricks job runs that were terminated before they ever began executing, broken out by the termination reason, over the trailing 30 days.
system.lakeflow.job_run_timelineOne row = a (workspace, job, termination_code) combination. The column that matters is never_started_runs - runs that were created but never began executing (queue rejection, quota hit, config error) before terminating.
RequiresSELECT on system.lakeflow; GA
These runs consumed a scheduling slot but produced zero work, so the failure is almost never a code bug — it is a capacity, quota, or configuration problem such as hitting a workspace run limit, exceeding a job-queue size, or a cluster/storage access error at launch. Left unseen, they mean scheduled SLAs are silently missed while the job never even starts, and the fix (raising concurrency limits, resizing queues, correcting config) is different from and cheaper than debugging failing code. Grouping by termination_code tells an admin exactly which lever to pull.
SELECT workspace_id, job_id, termination_code,
COUNT(DISTINCT run_id) AS never_started_runs,
-- status: worst-first band on never-started run count (field heuristic; :warn_never_started / :crit_never_started).
CASE
WHEN COUNT(DISTINCT run_id) >= :crit_never_started THEN 'CRITICAL'
WHEN COUNT(DISTINCT run_id) >= :warn_never_started THEN 'WARN'
ELSE 'OK'
END AS status
FROM system.lakeflow.job_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp())
AND result_state IS NOT NULL
AND period_start_time = period_end_time -- never began execution
GROUP BY workspace_id, job_id, termination_code
ORDER BY never_started_runs DESC:period_daysdefault 30rolling window in days:warn_never_starteddefault 3never-started runs for a (job, termination_code) pair that flags WARN:crit_never_starteddefault 10that flags CRITICALtermination_code so the launch-blocking reason (quota hit, queue overflow, cluster error) is visible.(workspace_id, job_id) so counts stay correct across a multi-workspace account.| Column | How to read it |
|---|---|
| workspace_id | The workspace the job belongs to. job_id is only unique within a workspace, so always read it together with this. |
| job_id | The job (workflow) whose runs never started. Unique only within its workspace. |
| termination_code | The root-cause reason the run was terminated before launch (e.g. WORKSPACE_RUN_LIMIT_EXCEEDED, MAX_JOB_QUEUE_SIZE_EXCEEDED, CLUSTER_ERROR, STORAGE_ACCESS_ERROR). This is the lever to act on. May be NULL on rows before ~Aug-2024 when the column began populating. |
| never_started_runs | Count of distinct runs of this job, with this termination code, that ended before executing (period_start_time = period_end_time). An integer run count, not DBUs or dollars. |
never_started_runs at/near 0 for every job - field heuristic; tune :warn_never_started for your account.
never_started_runs at/above :warn_never_started (WARN) or :crit_never_started (CRITICAL) for a job/termination_code pair - field heuristic; check termination_code first, it usually names the blocker (queue/quota/config).
| workspace_id | job_id | termination_code | never_started_runs |
|---|---|---|---|
| 1234567890123456 | 778899001122 | WORKSPACE_RUN_LIMIT_EXCEEDED | 14 |
| 1234567890123456 | 445566778899 | MAX_JOB_QUEUE_SIZE_EXCEEDED | 6 |
| 9876543210987654 | 112233445566 | CLUSTER_ERROR | 3 |
Read the dominant termination_code per job: for WORKSPACE_RUN_LIMIT_EXCEEDED / MAX_JOB_QUEUE_SIZE_EXCEEDED, raise the workspace concurrency or job-queue limits (or stagger schedules); for CLUSTER_ERROR / STORAGE_ACCESS_ERROR, fix the cluster policy, instance availability, or storage credentials so the run can launch.
Feeds into: never-started runs
Per-job phase totals (setup, queue, execution, cleanup, and overall run) plus 95th-percentile setup and queue times over the trailing 30 days, so you can see how much wall-clock time is startup and wait overhead versus real work.
system.lakeflow.job_run_timelineOne row = a job in the window. The column that matters is setup_s_p95 - the 95th-percentile cold-start (cluster setup) seconds before a run's code starts executing; execution_s_total is how much of the run's time was actual work.
RequiresSELECT on system.lakeflow; GA (the five *_duration_seconds columns were added early Dec 2025)
Every second a job spends provisioning compute (setup) or waiting for a slot (queue) is wall-clock latency your pipelines and downstream SLAs pay for, often with little or no useful work happening. When setup and queue time dwarf actual execution time, you are looking at cold-start overhead that warm pools, pre-provisioned or serverless compute, or higher concurrency limits could reclaim. Isolating this per job tells you which workloads are latency-bound by startup rather than by their own logic, so you tune the right thing instead of over-provisioning blindly.
SELECT workspace_id, job_id,
COUNT(DISTINCT run_id) AS runs,
SUM(setup_duration_seconds) AS setup_s_total,
SUM(queue_duration_seconds) AS queue_s_total,
SUM(execution_duration_seconds) AS execution_s_total,
SUM(cleanup_duration_seconds) AS cleanup_s_total,
SUM(run_duration_seconds) AS run_s_total,
PERCENTILE(setup_duration_seconds, 0.95) AS setup_s_p95,
PERCENTILE(queue_duration_seconds, 0.95) AS queue_s_p95,
SUM(CASE WHEN setup_duration_seconds IS NULL THEN 1 ELSE 0 END) AS rows_setup_null,
-- status: worst-first band on p95 setup (cold-start) seconds (field heuristic; :warn_setup_p95_s / :crit_setup_p95_s).
CASE
WHEN PERCENTILE(setup_duration_seconds, 0.95) IS NULL THEN 'NOT_ASSESSED'
WHEN PERCENTILE(setup_duration_seconds, 0.95) >= :crit_setup_p95_s THEN 'CRITICAL'
WHEN PERCENTILE(setup_duration_seconds, 0.95) >= :warn_setup_p95_s THEN 'WARN'
ELSE 'OK'
END AS status
FROM system.lakeflow.job_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp())
AND result_state IS NOT NULL -- end row carries the final durations
GROUP BY workspace_id, job_id
ORDER BY setup_s_p95 DESC:period_daysdefault 30rolling window in days:warn_setup_p95_sdefault 6095th-percentile setup seconds that flags WARN:crit_setup_p95_sdefault 300that flags CRITICAL| Column | How to read it |
|---|---|
| workspace_id | The workspace the job belongs to. Part of the key because job_id is unique only within a workspace. |
| job_id | The job (workflow) definition, scoped to its workspace. One row per job per workspace. |
| runs | Distinct runs of this job counted in the window (end rows only). Use it as the denominator when judging whether a total is large per-run. |
| setup_s_total | Total seconds spent in the setup/provisioning phase across all runs. High relative to execution signals cold-start overhead. NULL when phase durations are not yet populated on this account. |
| queue_s_total | Total seconds runs spent queued waiting for capacity before starting. This column exists only on job_run_timeline. NULL when not yet populated. |
| execution_s_total | Total seconds of actual execution across all runs — the real work. Compare against setup+queue to judge how much time is overhead vs work. |
| cleanup_s_total | Total seconds spent in the post-run cleanup/teardown phase across all runs. |
| run_s_total | Total end-to-end run seconds across all runs. The umbrella figure the phase totals decompose. |
| setup_s_p95 | 95th-percentile setup seconds — the tail cold-start a run hits on a bad day, not the average. NULL when setup durations are unpopulated. |
| queue_s_p95 | 95th-percentile queue seconds — the worst-case wait a run endured. A large gap vs the typical queue time points to intermittent capacity contention. |
| rows_setup_null | Number of this job's runs with a NULL setup duration. If it equals runs, phase timing is simply not populated yet (early-Dec-2025 gating) — read as "not assessed," never as zero overhead. |
setup_s_p95 below :warn_setup_p95_s seconds - field heuristic; tune :warn_setup_p95_s for your account.
setup_s_p95 at/above :warn_setup_p95_s (WARN) or :crit_setup_p95_s (CRITICAL) seconds - field heuristic; also check rows_setup_null before trusting a low number on a short-history account.
| workspace_id | job_id | runs | setup_s_total | queue_s_total | execution_s_total | cleanup_s_total | run_s_total | setup_s_p95 | queue_s_p95 | rows_setup_null |
|---|---|---|---|---|---|---|---|---|---|---|
| 1234567890123456 | 884422 | 120 | 3600 | 5400 | 42000 | 1200 | 52200 | 45 | 120 | 0 |
| 1234567890123456 | 990011 | 30 | 9000 | 1200 | 6000 | 600 | 16800 | 380 | 90 | 0 |
| 9876543210987654 | 112233 | 40 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 40 |
Rank jobs where setup_s_total plus queue_s_total is a large fraction of run_s_total (or where setup_s_p95 spikes well above the norm), and move those to warm-pool, pre-provisioned, or serverless compute — or raise concurrency limits for the queue-bound ones — to reclaim cold-start latency. If rows_setup_null equals runs, mark the job "not assessed" and revisit once phase timing populates.
Feeds into: phase-level cold-start
One row per Lakeflow (DLT) pipeline over the trailing window, showing its net DBUs billed, its maintenance DBUs broken out separately, a list-price dollar estimate, and how many times it refreshed and for how long.
system.billing.usagesystem.billing.list_pricessystem.lakeflow.pipelinessystem.lakeflow.pipeline_update_timelineOne row = a pipeline. The column that matters is net_pipeline_dbus - net DBUs billed to that pipeline in the window; net_maintenance_dbus is the housekeeping share of that total, broken out separately; updates/active_seconds_total are the over-refresh signal (many short updates can mean the pipeline is triggered more often than the data actually changes).
RequiresSELECT on system.billing, system.lakeflow; GA (system.billing.usage/list_prices); Public Preview (system.lakeflow.pipelines and pipeline_update_timeline - a missing/disabled table degrades names/updates to NULL, never the DBU attribution)
DLT/Lakeflow pipelines are a common source of silent spend: a pipeline set to refresh too often, or a continuous pipeline left running, can quietly dominate a workspace's bill. This query ranks pipelines by the DBUs they actually consumed and pairs that with a refresh count and active-second proxy, so an admin can see not just which pipelines cost the most but which are earning that cost through over-refreshing. Splitting maintenance DBUs into their own column means routine housekeeping (compaction, vacuum) is never mistaken for pipeline-logic cost, so tuning effort goes to the right place.
WITH pipe_dbus AS (
-- DBUs billed to each pipeline over the window. Net across ALL record_types; DBU only.
-- Split maintenance DBUs out via dlt_maintenance_id so they are reported separately.
SELECT workspace_id,
usage_metadata.dlt_pipeline_id AS pipeline_id,
SUM(usage_quantity) AS net_pipeline_dbus,
SUM(CASE WHEN usage_metadata.dlt_maintenance_id IS NOT NULL
THEN usage_quantity ELSE 0 END) AS net_maintenance_dbus
FROM system.billing.usage
WHERE usage_date >= dateadd(day, -:period_days, current_date())
AND usage_date < current_date()
AND upper(usage_unit) = 'DBU'
AND usage_metadata.dlt_pipeline_id IS NOT NULL
GROUP BY workspace_id, usage_metadata.dlt_pipeline_id
),
pipe_list AS (
-- LIST-$ estimate only (pre-discount, CAST-unverified): SUM(usage_quantity * list_rate)
-- per pipeline. Apply your negotiated discount on top of this; never a billed dollar.
SELECT u.workspace_id,
u.usage_metadata.dlt_pipeline_id AS pipeline_id,
MAX(lp.currency_code) AS currency_code,
SUM(u.usage_quantity * lp.list_rate) AS net_list_cost
FROM system.billing.usage u
LEFT JOIN (
SELECT sku_name, cloud, currency_code, usage_unit, price_start_time, price_end_time,
CAST(pricing.effective_list.default AS DOUBLE) AS list_rate -- <-- UNVERIFIED path
FROM system.billing.list_prices
) lp
ON u.sku_name = lp.sku_name
AND u.cloud = lp.cloud
AND u.usage_end_time >= lp.price_start_time
AND (lp.price_end_time IS NULL OR u.usage_end_time < lp.price_end_time)
WHERE u.usage_date >= dateadd(day, -:period_days, current_date())
AND u.usage_date < current_date()
AND upper(u.usage_unit) = 'DBU'
AND u.usage_metadata.dlt_pipeline_id IS NOT NULL
GROUP BY u.workspace_id, u.usage_metadata.dlt_pipeline_id
),
pipe_meta AS (
-- SCD2 latest row per (workspace_id, pipeline_id): names-not-ids + type.
SELECT workspace_id, pipeline_id, name AS pipeline_name, pipeline_type
FROM system.lakeflow.pipelines
QUALIFY ROW_NUMBER() OVER (
PARTITION BY workspace_id, pipeline_id ORDER BY change_time DESC
) = 1
),
pipe_updates AS (
-- Over-refresh signal: distinct updates + total active seconds per pipeline.
SELECT workspace_id, pipeline_id,
COUNT(DISTINCT update_id) AS updates,
SUM(unix_timestamp(period_end_time) - unix_timestamp(period_start_time)) AS active_seconds_total
FROM system.lakeflow.pipeline_update_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp())
AND result_state IS NOT NULL -- update end row only
GROUP BY workspace_id, pipeline_id
)
SELECT d.workspace_id,
d.pipeline_id,
CASE WHEN m.pipeline_name IS NULL THEN m.pipeline_name ELSE concat(substr(m.pipeline_name, 1, 2), '****') END AS pipeline_name,
m.pipeline_type,
d.net_pipeline_dbus,
d.net_maintenance_dbus,
l.currency_code,
l.net_list_cost,
COALESCE(up.updates, 0) AS updates,
COALESCE(up.active_seconds_total, 0) AS active_seconds_total,
-- status: worst-first band on net pipeline DBUs in the window (field heuristic; :warn_pipeline_dbus / :crit_pipeline_dbus).
CASE
WHEN d.net_pipeline_dbus >= :crit_pipeline_dbus THEN 'CRITICAL'
WHEN d.net_pipeline_dbus >= :warn_pipeline_dbus THEN 'WARN'
ELSE 'OK'
END AS status
FROM pipe_dbus d
LEFT JOIN pipe_list l
ON d.workspace_id = l.workspace_id AND d.pipeline_id = l.pipeline_id
LEFT JOIN pipe_meta m
ON d.workspace_id = m.workspace_id AND d.pipeline_id = m.pipeline_id
LEFT JOIN pipe_updates up
ON d.workspace_id = up.workspace_id AND d.pipeline_id = up.pipeline_id
WHERE d.net_pipeline_dbus > 0
ORDER BY d.net_pipeline_dbus DESC:period_daysdefault 30rolling window in days:warn_pipeline_dbusdefault 500net pipeline DBUs in the window that flags WARN:crit_pipeline_dbusdefault 2000that flags CRITICAL:period_days window, ranked with the most DBU-hungry pipelines first.net_pipeline_dbus (total DBUs billed) and net_maintenance_dbus (the housekeeping slice), so routine maintenance is never blamed on pipeline logic.net_list_cost) and its currency - a pre-discount rack-rate approximation, not a billed amount.updates ran and the total active_seconds_total they were executing, so frequent or long-running refreshers stand out.| Column | How to read it |
|---|---|
| workspace_id | The workspace the pipeline belongs to. Pipeline ids are unique only within a workspace, so always read this together with pipeline_id. |
| pipeline_id | The DLT/Lakeflow pipeline UUID that the billed DBUs attribute to (from usage_metadata.dlt_pipeline_id). |
| pipeline_name | Human-readable pipeline name, masked to its first two characters (e.g. 'sa****'). NULL when the pipeline has no matching latest row in the pipelines catalog - treat NULL as 'name not resolved,' not as a missing pipeline. (A missing/disabled Preview catalog errors the whole query rather than returning NULL.) |
| pipeline_type | The pipeline type from the pipelines catalog. NULL when the pipeline is not matched in the (present) catalog. |
| net_pipeline_dbus | Total net DBUs (a compute-unit quantity, NOT dollars) billed to this pipeline over the window, summed across all record types so restatements/retractions already net out. Only pipelines with net_pipeline_dbus > 0 are returned. |
| net_maintenance_dbus | The subset of net_pipeline_dbus attributable to maintenance/housekeeping (dlt_maintenance_id present). Also DBUs, not dollars. It is included within net_pipeline_dbus, not additional to it. |
| currency_code | Currency of the list-price estimate (e.g. USD). NULL when no list price matched the pipeline's SKUs. |
| net_list_cost | LIST-price dollar ESTIMATE = sum of usage_quantity x list_rate (pre-discount rack rate). Treat as an estimate to be multiplied by your negotiated discount, NEVER as a billed dollar amount; the price CAST path is unverified. |
| updates | Count of distinct pipeline updates (refreshes) that reached an end state (any result) in the window. 0 when the pipeline had no matching end rows in the timeline. A high count relative to peers is the over-refresh signal. |
| active_seconds_total | Total seconds the pipeline's updates were actively running over the window (a proxy for compute time, in seconds not dollars). 0 when the pipeline had no matching timeline rows. |
net_pipeline_dbus below :warn_pipeline_dbus for the window, with maintenance a small share of the total - field heuristic; tune :warn_pipeline_dbus for your account.
net_pipeline_dbus at/above :warn_pipeline_dbus (WARN) or :crit_pipeline_dbus (CRITICAL) - field heuristic; also look at updates vs active_seconds_total for over-refresh.
| pipeline_id | pipeline_name | net_pipeline_dbus | net_maintenance_dbus | net_list_cost | updates | active_seconds_total |
|---|---|---|---|---|---|---|
| a1b2c3d4-1111-4a2b-8c3d-000000000001 | sa**** | 4820.5 | 310.0 | 2892.30 | 1440 | 43200 |
| b2c3d4e5-2222-4b3c-9d4e-000000000002 | cu**** | 1180.0 | 95.5 | 708.00 | 96 | 28800 |
| c3d4e5f6-3333-4c4d-ae5f-000000000003 | re**** | 240.75 | 0.0 | 144.45 | 30 | 5400 |
Sort by net_pipeline_dbus to find the costliest pipelines, then cross-check their updates and active_seconds_total: a high update count relative to data freshness needs signals over-refreshing - dial back the refresh schedule or switch triggered/continuous mode. If net_maintenance_dbus is a large share, investigate housekeeping settings separately from pipeline logic. Multiply net_list_cost by your negotiated discount for a rough dollar figure, and confirm the price path before quoting it.
Feeds into: Lakeflow domain - per-pipeline DBU cost + over-refresh signal (lakeflow_pipeline_cost finding)
Per-Lakeflow-pipeline count of updates and total active run seconds over the last 30 days, tagged with each pipeline's type and its continuous/development settings so you can spot pipelines whose clusters likely linger idle between refreshes.
system.lakeflow.pipeline_update_timelineOne row = a pipeline. The columns that matter are updates/active_seconds_total (how much the pipeline actually ran) next to net_dbus (what it was billed) - a large gap between billed DBUs and active update time on a non-continuous pipeline suggests the cluster is lingering after the run (idle tail) rather than shutting down promptly.
RequiresSELECT on system.lakeflow, system.billing; Public Preview (system.lakeflow.pipelines and pipeline_update_timeline); GA (system.billing.usage/list_prices)
Continuous and development-mode pipelines keep compute warm and can leave clusters lingering idle long after an update finishes, and that idle tail bills as serverless/DLT DBUs with nothing to show for it. This query surfaces the pipelines doing lots of short updates (a continuous or dev pattern) versus few long ones, so you can target the ones most likely to be wasting money on idle compute. It is a screening signal, not a bill: the active-seconds here measure time spent running, and the actual idle tail must be confirmed against billing idle DBUs before you act.
WITH price AS (
SELECT sku_name, cloud, usage_unit, price_start_time, price_end_time,
CAST(pricing.default AS DOUBLE) AS list_rate
FROM system.billing.list_prices
),
cost_rollup AS (
SELECT u.workspace_id,
u.usage_metadata.dlt_pipeline_id AS dlt_pipeline_id,
SUM(u.usage_quantity) AS net_dbus,
SUM(u.usage_quantity * COALESCE(p.list_rate, 0)) AS est_usd_list
FROM system.billing.usage u
LEFT JOIN price p
ON u.sku_name = p.sku_name AND u.cloud = p.cloud AND u.usage_unit = p.usage_unit
AND u.usage_end_time >= p.price_start_time
AND (p.price_end_time IS NULL OR u.usage_end_time < p.price_end_time)
WHERE upper(u.usage_unit) = 'DBU'
AND u.usage_metadata.dlt_pipeline_id IS NOT NULL
AND u.usage_date >= dateadd(day, -:period_days, current_date())
AND u.usage_date < current_date()
GROUP BY u.workspace_id, u.usage_metadata.dlt_pipeline_id
)
-- NEEDS CONFIRMATION: settings.<key> dot-access is UNVERIFIED (struct vs map).
SELECT u.workspace_id, u.pipeline_id, p.pipeline_type, p.setting_continuous, p.setting_development,
COUNT(DISTINCT u.update_id) AS updates,
SUM(unix_timestamp(u.period_end_time) - unix_timestamp(u.period_start_time)) AS active_seconds_total,
COALESCE(MAX(cr.net_dbus), 0) AS net_dbus,
COALESCE(MAX(cr.est_usd_list), 0) AS est_usd_list,
-- status: worst-first band on net DBUs for a non-continuous pipeline (field heuristic; :warn_idle_dbus / :crit_idle_dbus).
CASE
WHEN p.setting_continuous = true THEN 'NOT_ASSESSED'
WHEN COALESCE(MAX(cr.net_dbus), 0) >= :crit_idle_dbus THEN 'CRITICAL'
WHEN COALESCE(MAX(cr.net_dbus), 0) >= :warn_idle_dbus THEN 'WARN'
ELSE 'OK'
END AS status
FROM system.lakeflow.pipeline_update_timeline u
LEFT JOIN (
SELECT workspace_id, pipeline_id, pipeline_type,
settings.continuous AS setting_continuous,
settings.development AS setting_development
FROM system.lakeflow.pipelines
QUALIFY ROW_NUMBER() OVER (PARTITION BY workspace_id, pipeline_id ORDER BY change_time DESC) = 1
) p
ON u.workspace_id = p.workspace_id AND u.pipeline_id = p.pipeline_id
LEFT JOIN cost_rollup cr
ON u.workspace_id = cr.workspace_id AND u.pipeline_id = cr.dlt_pipeline_id
WHERE u.period_start_time >= dateadd(day, -:period_days, current_date())
AND u.period_end_time < date_trunc('DAY', current_timestamp())
AND u.result_state IS NOT NULL
GROUP BY u.workspace_id, u.pipeline_id, p.pipeline_type, p.setting_continuous, p.setting_development
ORDER BY net_dbus DESC:period_daysdefault 30rolling window in days:warn_idle_dbusdefault 100net DBUs in the window on a non-continuous pipeline that flags WARN:crit_idle_dbusdefault 500that flags CRITICALpipeline_type and whether it is set to continuous and/or development mode — the settings most associated with idle-lingering compute.net_dbus) and a list-price USD estimate (est_usd_list) per pipeline, so the finding now shows the waste in DBUs and directional dollars — the estimate prices DBUs at list, not your negotiated rate.| Column | How to read it |
|---|---|
| workspace_id | The workspace the pipeline belongs to; pipeline_id is unique only within a workspace, so always read it together with this. |
| pipeline_id | The Lakeflow/DLT pipeline identifier. Join back to billing on (workspace_id, dlt_pipeline_id) to confirm idle DBUs. |
| pipeline_type | The pipeline's declared type from the deduped catalog. NULL if the pipeline had no matching (or deleted) catalog row. |
| setting_continuous | Whether the pipeline is configured to run continuously (settings.continuous). Continuous pipelines are prime idle-tail suspects. UNVERIFIED struct access — may be NULL/error on accounts where settings is a MAP. |
| setting_development | Whether the pipeline runs in development mode (settings.development), where clusters are deliberately kept warm between updates. Same unverified-access caveat. |
| updates | Distinct pipeline updates (refreshes) completed in the window. Many small updates hints at a continuous/frequent-refresh pattern. |
| active_seconds_total | Sum of each update's active window (period_end_time minus period_start_time) in seconds. This is run time, not idle time, and not dollars. |
| net_dbus | Exact billed DBUs (usage_unit='DBU') attributed to this pipeline (workspace_id + dlt_pipeline_id) over the fixed 30-day window, netted across all record_types (COALESCE 0 when no billing row matched). DBUs, NOT dollars. Whole-pipeline DBUs including any idle tail; corroborate the idle share against billing separately. |
| est_usd_list | List-price USD ESTIMATE = net_dbus × list_prices.pricing.default (‘est · at list’). NOT your negotiated invoice rate (not in any system table) and excludes cloud infra/egress $ — directional only, never a real dollar figure. |
net_dbus roughly tracks active_seconds_total for non-continuous pipelines, and stays below :warn_idle_dbus for the window - field heuristic; tune :warn_idle_dbus for your account.
net_dbus at/above :warn_idle_dbus (WARN) or :crit_idle_dbus (CRITICAL) on a pipeline where setting_continuous is false and active_seconds_total looks small relative to net_dbus - field heuristic.
| workspace_id | pipeline_id | pipeline_type | setting_continuous | setting_development | updates | active_seconds_total | net_dbus | est_usd_list |
|---|---|---|---|---|---|---|---|---|
| 1234567890123456 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 | MANAGED | true | false | 412 | 148320 | 18400 | 9200.00 |
| 1234567890123456 | b2c3d4e5-f6a7-8901-bcde-f12345678901 | MANAGED | false | true | 9 | 32040 | 2600 | 1300.00 |
| 9876543210987654 | c3d4e5f6-a7b8-9012-cdef-123456789012 | MANAGED | false | false | 28 | 61200 | 4100 | 2050.00 |
Rank pipelines with high update counts and continuous or development mode set to true, then confirm the suspected idle tail by joining system.billing.usage on (workspace_id, usage_metadata.dlt_pipeline_id); for confirmed offenders, switch dev pipelines out of development mode, tune cluster auto-shutdown, or move frequent continuous refreshes to triggered updates.
Feeds into: DLT/Lakeflow pipeline tier + idle tail
A 30-day breakdown of Lakeflow/DLT pipeline update outcomes, counting how many refreshes ran, how many failed, and how many were fired by retry-on-failure, sliced per workspace, pipeline, update type, trigger, and result state.
system.lakeflow.pipeline_update_timelineOne row = a (workspace, pipeline, update_type, trigger_type, result_state) combination. The column that matters is failed_update_rows; retry_triggered_rows next to it tells you how many of the group's updates were themselves a retry, not an original attempt.
RequiresSELECT on system.lakeflow; Public Preview (system.lakeflow.pipeline_update_timeline)
Pipeline refreshes that fail or only succeed after retries are the silent tax on a data platform: every failed FULL_REFRESH re-processes data and burns pipeline compute, and a high retry-on-failure count signals a flapping pipeline that is unstable even when it eventually turns green. This view tells an admin which pipelines and which update types (full refresh vs incremental refresh vs validate) are eating reliability budget, so remediation is aimed at the worst offenders rather than the whole fleet. Because these DBUs and re-runs are real spend, cutting failure and retry rates translates directly into lower pipeline cost and fewer late data deliveries.
WITH end_rows AS (
SELECT workspace_id, pipeline_id, update_id, request_id, update_type,
trigger_type, result_state, period_start_time, period_end_time
FROM system.lakeflow.pipeline_update_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp())
AND result_state IS NOT NULL -- end row only for updates >1h
)
SELECT e.workspace_id, e.pipeline_id, e.update_type, e.trigger_type, e.result_state,
COUNT(DISTINCT e.update_id) AS updates,
SUM(CASE WHEN e.result_state = 'FAILED' THEN 1 ELSE 0 END) AS failed_update_rows,
SUM(CASE WHEN e.trigger_type = 'RETRY_ON_FAILURE' THEN 1 ELSE 0 END) AS retry_triggered_rows,
-- status: worst-first band on failed update rows per group (field heuristic; :warn_failed_updates / :crit_failed_updates).
CASE
WHEN SUM(CASE WHEN e.result_state = 'FAILED' THEN 1 ELSE 0 END) >= :crit_failed_updates THEN 'CRITICAL'
WHEN SUM(CASE WHEN e.result_state = 'FAILED' THEN 1 ELSE 0 END) >= :warn_failed_updates THEN 'WARN'
ELSE 'OK'
END AS status
FROM end_rows e
GROUP BY e.workspace_id, e.pipeline_id, e.update_type, e.trigger_type, e.result_state
ORDER BY failed_update_rows DESC:period_daysdefault 30rolling window in days:warn_failed_updatesdefault 3failed update rows in a group that flags WARN:crit_failed_updatesdefault 10that flags CRITICALupdate_type (FULL_REFRESH / REFRESH / VALIDATE) and by result (COMPLETED / FAILED / CANCELED).| Column | How to read it |
|---|---|
| workspace_id | The workspace the pipeline lives in. Always read together with pipeline_id, since pipeline ids are unique only within a workspace. |
| pipeline_id | The Lakeflow/DLT pipeline identifier (a UUID). One pipeline can appear on several rows, one per update-type/trigger/result combination. |
| update_type | The kind of refresh: FULL_REFRESH (reprocess everything), REFRESH (incremental), or VALIDATE (dry-run/validation). Full refreshes are the most expensive to fail. |
| trigger_type | What launched the update. RETRY_ON_FAILURE means this update was an automatic retry of a prior failed one — the key instability signal. |
| result_state | How the update ended: COMPLETED, FAILED, or CANCELED (note the single L). CANCELED is a user/system cancel, not a failure. |
| updates | Number of distinct pipeline updates in this bucket (COUNT DISTINCT update_id). This is the headline volume for the combination. |
| failed_update_rows | Count of end rows in this bucket whose result was FAILED. Because rows are grouped by result_state, it is non-zero only on rows where result_state = FAILED, so it quantifies the failure count for that line. |
| retry_triggered_rows | Count of end rows in this bucket triggered by RETRY_ON_FAILURE. Because rows are grouped by trigger_type, it is non-zero only on rows whose trigger_type is RETRY_ON_FAILURE; a high value on a pipeline flags repeated retrying. |
failed_update_rows low relative to updates, with retry_triggered_rows explaining most of any repeats - field heuristic; tune :warn_failed_updates for your account.
failed_update_rows at/above :warn_failed_updates (WARN) or :crit_failed_updates (CRITICAL) for a pipeline - field heuristic; a pipeline that fails and keeps auto-retrying without ever completing is the priority case.
| workspace_id | pipeline_id | update_type | trigger_type | result_state | updates | failed_update_rows | retry_triggered_rows |
|---|---|---|---|---|---|---|---|
| 1234567890123456 | a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d | REFRESH | SCHEDULE | FAILED | 12 | 12 | 0 |
| 1234567890123456 | a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d | FULL_REFRESH | RETRY_ON_FAILURE | COMPLETED | 5 | 0 | 5 |
| 9876543210987654 | f0e9d8c7-b6a5-4321-9f8e-7d6c5b4a3210 | REFRESH | MANUAL | COMPLETED | 48 | 0 | 0 |
Sort by failed_update_rows and retry_triggered_rows to find the pipelines with the most failing or retry-driven refreshes, then investigate those pipelines' logic and dependencies — prioritizing FULL_REFRESH failures, which reprocess the most data — to cut the wasted re-runs before they show up on the pipeline cost report.
Feeds into: DLT/Lakeflow pipeline tier + idle tail; failed runs; retries/repairs
A per-workspace census of your live Lakeflow Declarative (DLT) pipelines, counting how many run in each combination of pipeline type, serverless, development-mode, continuous, and product edition.
system.lakeflow.pipelinesOne row = a distinct combination of workspace, pipeline_type, and key settings, with pipelines as the count. Use this to see your pipeline mix (serverless vs classic, continuous vs triggered, dev vs prod) before drilling into a cost or idle-tail finding.
RequiresSELECT on system.lakeflow; Public Preview (system.lakeflow.pipelines)
Configuration drift in a DLT fleet is where quiet money leaks and reliability gaps hide: a pipeline left in development mode won't retry or run on production-grade compute, a continuous pipeline keeps compute always-on around the clock, and paying for the ADVANCED edition on a pipeline that only needs CORE features is pure overspend. This is the one-screen fleet map that tells an admin how many pipelines sit in each posture and in which workspace, so misconfigurations and edition-tier overspend get caught before they compound. It reads current state only, so it is safe to run anytime as a governance baseline.
-- NEEDS CONFIRMATION: settings.<key> dot-access vs settings['<key>'] map-access is UNVERIFIED.
WITH latest_pipelines AS (
SELECT workspace_id, pipeline_id, pipeline_type, name, created_by, run_as, settings, delete_time,
settings.serverless AS setting_serverless,
settings.development AS setting_development,
settings.continuous AS setting_continuous,
settings.photon AS setting_photon,
settings.edition AS setting_edition,
settings.channel AS setting_channel
FROM system.lakeflow.pipelines
QUALIFY ROW_NUMBER() OVER (PARTITION BY workspace_id, pipeline_id ORDER BY change_time DESC) = 1
)
SELECT workspace_id, pipeline_type, setting_serverless, setting_development,
setting_continuous, setting_edition,
COUNT(*) AS pipelines
FROM latest_pipelines
WHERE delete_time IS NULL
GROUP BY workspace_id, pipeline_type, setting_serverless, setting_development, setting_continuous, setting_edition
ORDER BY workspace_id, pipeline_type| Column | How to read it |
|---|---|
| workspace_id | The workspace the pipelines live in. Counts are scoped here because pipeline_id is unique only within a workspace; compare postures across workspaces, never merge them. |
| pipeline_type | The kind of Lakeflow/DLT pipeline (e.g. the pipeline product/workload type as recorded in the catalog). One dimension of the posture breakdown. |
| setting_serverless | Whether the pipeline runs on serverless compute (true) vs classic/customer-managed compute (false). NULL means the setting was not present on the row. Access path unverified — see caveats. |
| setting_development | Whether the pipeline is in development mode (true) vs production mode (false). Dev-mode pipelines behave differently on retries and cluster reuse; production pipelines left as true are a common misconfiguration. |
| setting_continuous | Whether the pipeline runs continuously / always-on (true) vs triggered on demand (false). Continuous pipelines hold compute open and are the ones to scrutinize for idle cost. |
| setting_edition | The pipeline PRODUCT edition — CORE, PRO, or ADVANCED — which gates feature availability. This is NOT the billing DLT tier (that lives in billing). Use it to spot pipelines paying for a higher edition than they use. |
| pipelines | Integer count of live pipelines matching this exact combination of workspace / type / serverless / development / continuous / edition. Sum across rows within a workspace to get its total live pipeline count. |
Inventory / reference query — it returns state to read, not a pass/fail band.
| workspace_id | pipeline_type | setting_serverless | setting_development | setting_continuous | setting_edition | pipelines |
|---|---|---|---|---|---|---|
| 1234567890123456 | WORKSPACE | true | false | false | ADVANCED | 14 |
| 1234567890123456 | WORKSPACE | false | true | true | CORE | 3 |
| 9876543210987654 | WORKSPACE | true | false | true | PRO | 6 |
Use the breakdown to right-size your DLT fleet: confirm each cluster of pipelines matches intended posture — flag any production pipelines still stuck in `setting_development=true`, review `setting_continuous=true` pipelines for always-on cost, and reconcile ADVANCED/PRO editions against what each pipeline actually needs. Before trusting the numbers, run the query once and confirm dot-access on `settings` didn't error; if it did, switch to `settings['<key>']` map syntax.
Feeds into: DLT/Lakeflow pipeline tier + idle tail
Per-job retry tallies over the trailing 30 days — how many runs each job had, how many extra attempts those runs racked up, and how many runs needed at least one retry.
system.lakeflow.job_run_timelineOne row = a job in the window. The column that matters is total_retries - extra attempt rows beyond the first, summed across all the job's runs; runs_with_retry tells you how many distinct runs needed at least one retry, and net_dbus/est_usd_list is the job's overall DBU cost for context.
RequiresSELECT on system.lakeflow, system.billing; GA
A job that only ever succeeds after two or three automatic retries looks green on the dashboard but is quietly unstable — and every failed attempt still spins up compute and burns DBUs before the next try. This query surfaces the jobs doing the most re-running so you can distinguish genuinely healthy jobs from flappy ones that are masking a real reliability problem and wasting money on repeated startups. Left unaddressed, retry-heavy jobs inflate spend and erode trust in your scheduled workloads.
WITH end_rows AS (
SELECT workspace_id, job_id, run_id
FROM system.lakeflow.job_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp())
AND result_state IS NOT NULL -- one non-NULL result_state row per attempt
),
per_run AS (
SELECT workspace_id, job_id, run_id, COUNT(*) AS attempt_rows
FROM end_rows GROUP BY workspace_id, job_id, run_id
),
price AS (
SELECT sku_name, cloud, usage_unit, price_start_time, price_end_time,
CAST(pricing.default AS DOUBLE) AS list_rate
FROM system.billing.list_prices
),
cost_rollup AS (
-- Pre-aggregated cost per (workspace_id, job_id). job_id is not globally unique, so we key on
-- workspace_id + job_id. Window mirrors this query's trailing window via usage_date.
SELECT u.workspace_id,
u.usage_metadata.job_id AS job_id,
SUM(u.usage_quantity) AS net_dbus,
SUM(u.usage_quantity * COALESCE(p.list_rate, 0)) AS est_usd_list
FROM system.billing.usage u
LEFT JOIN price p
ON u.sku_name = p.sku_name AND u.cloud = p.cloud AND u.usage_unit = p.usage_unit
AND u.usage_end_time >= p.price_start_time
AND (p.price_end_time IS NULL OR u.usage_end_time < p.price_end_time)
WHERE upper(u.usage_unit) = 'DBU'
AND u.usage_metadata.job_id IS NOT NULL
AND u.usage_date >= dateadd(day, -:period_days, current_date())
AND u.usage_date < current_date()
GROUP BY u.workspace_id, u.usage_metadata.job_id
)
SELECT pr.workspace_id, pr.job_id,
COUNT(*) AS distinct_runs,
SUM(pr.attempt_rows) AS total_attempt_rows,
SUM(pr.attempt_rows - 1) AS total_retries,
SUM(CASE WHEN pr.attempt_rows > 1 THEN 1 ELSE 0 END) AS runs_with_retry,
COALESCE(MAX(cr.net_dbus), 0) AS net_dbus,
COALESCE(MAX(cr.est_usd_list), 0) AS est_usd_list,
-- status: worst-first band on total retries in the window (field heuristic; :warn_total_retries / :crit_total_retries).
CASE
WHEN SUM(pr.attempt_rows - 1) >= :crit_total_retries THEN 'CRITICAL'
WHEN SUM(pr.attempt_rows - 1) >= :warn_total_retries THEN 'WARN'
ELSE 'OK'
END AS status
FROM per_run pr
LEFT JOIN cost_rollup cr
ON pr.workspace_id = cr.workspace_id AND pr.job_id = cr.job_id
GROUP BY pr.workspace_id, pr.job_id
ORDER BY total_retries DESC:period_daysdefault 30rolling window in days:warn_total_retriesdefault 5total retries for a job in the window that flags WARN:crit_total_retriesdefault 20that flags CRITICAL(workspace_id, job_id), so retry behavior is attributed to the exact job in the exact workspace.total_retries (the extra attempts beyond the first for every run combined) and runs_with_retry (how many individual runs needed at least one retry).run_id, so a job that always succeeds first try shows zero across the retry columns.net_dbus) and a list-price USD estimate (est_usd_list) per job, so the finding now shows the waste in DBUs and directional dollars — the estimate prices DBUs at list, not your negotiated rate.| Column | How to read it |
|---|---|
| workspace_id | The workspace the job belongs to. Always read together with `job_id` — job ids are unique only within a workspace. |
| job_id | The job (workflow) definition id, scoped to its workspace. This is the grain of one output row. |
| distinct_runs | Number of separate runs of this job that completed in the window (each `run_id` counted once, regardless of how many retries it contained). |
| total_attempt_rows | Total completed attempts across all those runs, including retries. Equals `distinct_runs` plus `total_retries`. |
| total_retries | Extra attempts beyond the first for every run summed together (`total_attempt_rows` minus `distinct_runs`). Zero means no run of this job ever retried. |
| runs_with_retry | How many individual runs needed at least one retry. Compare against `distinct_runs`: a high ratio means retrying is the norm, not the exception, for this job. |
| net_dbus | Exact billed DBUs (usage_unit='DBU') attributed to this job (workspace_id + job_id) over the fixed 30-day window, netted across all record_types (COALESCE 0 when no billing row matched). DBUs, NOT dollars. |
| est_usd_list | List-price USD ESTIMATE = net_dbus × list_prices.pricing.default (‘est · at list’). NOT your negotiated invoice rate (not in any system table) and excludes cloud infra/egress $ — directional only, never a real dollar figure. |
total_retries low relative to distinct_runs - field heuristic; tune :warn_total_retries for your account.
total_retries at/above :warn_total_retries (WARN) or :crit_total_retries (CRITICAL) - field heuristic; a job where runs_with_retry is close to distinct_runs is retrying on (almost) every run, which usually points at a systemic cause.
| workspace_id | job_id | distinct_runs | total_attempt_rows | total_retries | runs_with_retry | net_dbus | est_usd_list |
|---|---|---|---|---|---|---|---|
| 1470987654321098 | 884213097654321 | 50 | 63 | 13 | 9 | 3400 | 1700.00 |
| 1470987654321098 | 771002845190022 | 120 | 120 | 0 | 0 | 5200 | 2600.00 |
| 2039118472650913 | 905544120983311 | 30 | 41 | 11 | 8 | 1900 | 950.00 |
Sort by `total_retries` (or the `runs_with_retry` / `distinct_runs` ratio) to find the flappiest jobs, then investigate their termination codes and root causes — fix the underlying instability rather than relying on retry-on-failure to paper over it, and reclaim the compute wasted on repeated attempts.
Feeds into: retries/repairs
Every active (non-deleted) job definition with the timestamp its last run was last seen running and a flag marking those that haven't run in over 30 days (or ever).
system.lakeflow.jobsOne row = an active job. The column that matters is last_run_start - the most recent time the job ran, across all history; is_stale flags jobs that have not run in over :stale_days days (or never at all).
RequiresSELECT on system.lakeflow, system.billing; GA
Zombie job definitions that haven't executed in a month (or have never run at all) clutter the workspace, inflate job inventories, and are often stale scaffolding, abandoned experiments, or handed-off work no one owns anymore. Each is an audit and cleanup candidate: dead definitions can hide orphaned schedules, obsolete credentials, or forgotten service-principal run-as identities. Pruning them shrinks the governance surface and makes the remaining active fleet easier to reason about.
WITH latest_jobs AS (
SELECT workspace_id, job_id, name, run_as, creator_id, delete_time
FROM system.lakeflow.jobs
QUALIFY ROW_NUMBER() OVER (PARTITION BY workspace_id, job_id ORDER BY change_time DESC) = 1
),
last_run AS (
SELECT workspace_id, job_id, MAX(period_start_time) AS last_run_start
FROM system.lakeflow.job_run_timeline
GROUP BY workspace_id, job_id
),
price AS (
SELECT sku_name, cloud, usage_unit, price_start_time, price_end_time,
CAST(pricing.default AS DOUBLE) AS list_rate
FROM system.billing.list_prices
),
cost_rollup AS (
SELECT u.workspace_id,
u.usage_metadata.job_id AS job_id,
SUM(u.usage_quantity) AS net_dbus,
SUM(u.usage_quantity * COALESCE(p.list_rate, 0)) AS est_usd_list
FROM system.billing.usage u
LEFT JOIN price p
ON u.sku_name = p.sku_name AND u.cloud = p.cloud AND u.usage_unit = p.usage_unit
AND u.usage_end_time >= p.price_start_time
AND (p.price_end_time IS NULL OR u.usage_end_time < p.price_end_time)
WHERE upper(u.usage_unit) = 'DBU'
AND u.usage_metadata.job_id IS NOT NULL
AND u.usage_date >= dateadd(day, -:period_days, current_date())
AND u.usage_date < current_date()
GROUP BY u.workspace_id, u.usage_metadata.job_id
)
SELECT j.workspace_id, j.job_id,
CASE WHEN j.name IS NULL THEN j.name ELSE concat(substr(j.name, 1, 2), '****') END AS name,
r.last_run_start,
CASE WHEN r.last_run_start IS NULL
OR r.last_run_start < dateadd(day, -:stale_days, current_date())
THEN 1 ELSE 0 END AS is_stale,
COALESCE(cr.net_dbus, 0) AS net_dbus,
COALESCE(cr.est_usd_list, 0) AS est_usd_list,
-- status: worst-first band on days since last run (field heuristic; :stale_days / :crit_stale_days).
CASE
WHEN r.last_run_start IS NULL THEN 'WARN' -- no run ever recorded for this job in job_run_timeline history
WHEN r.last_run_start < dateadd(day, -:crit_stale_days, current_date()) THEN 'CRITICAL'
WHEN r.last_run_start < dateadd(day, -:stale_days, current_date()) THEN 'WARN'
ELSE 'OK'
END AS status
FROM latest_jobs j
LEFT JOIN last_run r ON j.workspace_id = r.workspace_id AND j.job_id = r.job_id
LEFT JOIN cost_rollup cr ON j.workspace_id = cr.workspace_id AND j.job_id = cr.job_id
WHERE j.delete_time IS NULL
ORDER BY last_run_start ASC:stale_daysdefault 30days since last run that flags WARN:crit_stale_daysdefault 90days since last run that flags CRITICAL:period_daysdefault 30cost-lookback window for net_dbus/est_usd_list (a separate window from the staleness thresholds)is_stale_30d.net_dbus) and a list-price USD estimate (est_usd_list) per job, so the finding now shows the waste in DBUs and directional dollars — the estimate prices DBUs at list, not your negotiated rate.| Column | How to read it |
|---|---|
| workspace_id | The workspace the job lives in. Job ids are unique only within a workspace, so always read this together with `job_id`. |
| job_id | The job's identifier within its workspace. Pair with `workspace_id` to look the job up in the Jobs UI. |
| name | The job name masked to its first two characters plus '****' (e.g. 'ni****'). NULL if the job had no name; not a violation. |
| last_run_start | The most recent run-slice start timestamp for this job (its last-seen-running signal). NULL means the job has never appeared in the run timeline — it has never run (or ran before the log's history). |
| is_stale_30d | 1 = stale: the last run started more than 30 days ago, or the job has never run (NULL last_run_start). 0 = ran within the last 30 days. Treat 1 as a cleanup/audit candidate, not an automatic delete. |
| net_dbus | Exact billed DBUs (usage_unit='DBU') attributed to this job (workspace_id + job_id) over the fixed 30-day lookback window, netted across all record_types (COALESCE 0 when no billing row matched). DBUs, NOT dollars. A stale job correctly shows ~0 net_dbus (no recent runs in the window). |
| est_usd_list | List-price USD ESTIMATE = net_dbus × list_prices.pricing.default (‘est · at list’). NOT your negotiated invoice rate (not in any system table) and excludes cloud infra/egress $ — directional only, never a real dollar figure. |
last_run_start recent (within :stale_days days) for every active job - field heuristic; tune :stale_days for your account.
is_stale = 1, especially where last_run_start is NULL (no run ever recorded) or older than :crit_stale_days days - field heuristic.
| workspace_id | job_id | name | last_run_start | is_stale_30d | net_dbus | est_usd_list |
|---|---|---|---|---|---|---|
| 1234567890123456 | 884213 | ni**** | 2026-06-30 02:15:00 | 0 | 420 | 210.00 |
| 1234567890123456 | 771004 | le**** | 2026-01-11 04:00:00 | 1 | 0 | 0.00 |
| 9876543210987654 | 310592 | da**** | NULL (never run) | 1 | 0 | 0.00 |
Review jobs flagged `is_stale_30d = 1`, confirm each is truly abandoned (check the `last_run_start` date and owner), then pause, archive, or delete the dead definitions to clean up the workspace and close ownership gaps.
Feeds into: stale/zombie jobs
Per job, how many runs reported overall SUCCEEDED yet actually contained at least one task that FAILED, ERROR'd, or TIMED_OUT over the trailing 30 days.
system.lakeflow.job_task_run_timelineOne row = a job in the window. The column that matters is succeeded_runs_with_failed_task - runs where the overall job reported SUCCEEDED even though at least one of its tasks reported FAILED/ERROR/TIMED_OUT (usually an optional/best-effort task, or a masked failure worth a second look).
RequiresSELECT on system.lakeflow; GA
A run that reports SUCCEEDED at the job level but contains a task that FAILED, ERROR'd, or TIMED_OUT is a silent partial failure: dashboards look green while data was actually dropped, a load was skipped, or a downstream table went stale. These are the incidents that surface days later as missing rows or bad numbers, because nobody was paged. For an admin, a non-zero `succeeded_runs_with_failed_task` count points directly at jobs whose failure-propagation or task-dependency wiring is misconfigured and needs tightening before the next silent gap ships bad data downstream.
WITH job_end AS (
SELECT workspace_id, job_id, run_id AS job_run_id, result_state AS job_result_state
FROM system.lakeflow.job_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp())
AND result_state IS NOT NULL
),
task_end AS (
SELECT workspace_id, job_id, job_run_id, task_key, result_state AS task_result_state
FROM system.lakeflow.job_task_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND result_state IS NOT NULL
)
SELECT j.workspace_id, j.job_id,
COUNT(DISTINCT j.job_run_id) AS succeeded_runs,
COUNT(DISTINCT CASE WHEN t.task_result_state IN ('FAILED','ERROR','TIMED_OUT')
THEN j.job_run_id END) AS succeeded_runs_with_failed_task,
-- status: worst-first band on succeeded-but-had-a-failed-task run count (field heuristic;
-- :warn_succeeded_with_failed / :crit_succeeded_with_failed).
CASE
WHEN COUNT(DISTINCT CASE WHEN t.task_result_state IN ('FAILED','ERROR','TIMED_OUT') THEN j.job_run_id END) >= :crit_succeeded_with_failed THEN 'CRITICAL'
WHEN COUNT(DISTINCT CASE WHEN t.task_result_state IN ('FAILED','ERROR','TIMED_OUT') THEN j.job_run_id END) >= :warn_succeeded_with_failed THEN 'WARN'
ELSE 'OK'
END AS status
FROM job_end j
LEFT JOIN task_end t
ON j.workspace_id = t.workspace_id AND j.job_id = t.job_id AND j.job_run_id = t.job_run_id
WHERE j.job_result_state = 'SUCCEEDED'
GROUP BY j.workspace_id, j.job_id
ORDER BY succeeded_runs_with_failed_task DESC:period_daysdefault 30rolling window in days:warn_succeeded_with_faileddefault 3such runs for a job that flags WARN:crit_succeeded_with_faileddefault 10that flags CRITICALworkspace_id + job_id) alongside a count of its runs that finished with an overall status of SUCCEEDED in the last 30 days.SKIPPED, CANCELLED, and BLOCKED tasks as not failures, so only genuinely failed/errored/timed-out tasks inflate the flag.| Column | How to read it |
|---|---|
| workspace_id | The workspace the job belongs to. Job ids repeat across workspaces, so always read it together with `job_id`. |
| job_id | The job (workflow) identifier, unique only within its `workspace_id`. Look this up in `system.lakeflow.jobs` to get the human name and owner. |
| succeeded_runs | How many distinct runs of this job finished with an overall result of SUCCEEDED in the trailing 30 days. Your baseline of 'green' runs. |
| succeeded_runs_with_failed_task | The subset of those green runs that nonetheless contained at least one task reporting FAILED, ERROR, or TIMED_OUT. Any value above 0 is a silent partial failure to investigate; if it equals `succeeded_runs`, every green run was hiding a failed task. |
succeeded_runs_with_failed_task at/near 0 - field heuristic; tune :warn_succeeded_with_failed for your account.
succeeded_runs_with_failed_task at/above :warn_succeeded_with_failed (WARN) or :crit_succeeded_with_failed (CRITICAL) - field heuristic; check whether the failing task is intentionally marked non-blocking or is silently swallowing a real failure.
| workspace_id | job_id | succeeded_runs | succeeded_runs_with_failed_task |
|---|---|---|---|
| 1234567890123456 | 884422113355 | 212 | 0 |
| 1234567890123456 | 775533991122 | 88 | 14 |
| 9876543210987654 | 601020304050 | 30 | 30 |
For each job where `succeeded_runs_with_failed_task` is non-zero, open its recent SUCCEEDED runs, identify which task keys reported FAILED/ERROR/TIMED_OUT, and fix the job's failure-propagation settings (e.g. mark the task's downstream dependency as required / disable overly permissive "continue on failure") so a dropped task no longer lets the whole run report green.
Feeds into: succeeded-with-failures
Per-task run statistics over the lookback window flagging task runs that reached at least 80% of (or exceeded) their configured timeout, so you can tune the bound or fix the slowdown before it trips.
system.lakeflow.job_task_run_timelinesystem.lakeflow.job_tasksOne row = a task within a job. The columns that matter are runs_near_timeout (execution reached at/above :near_timeout_ratio of the configured timeout) and runs_over_timeout (execution reached or passed the timeout and should have been killed).
RequiresSELECT on system.lakeflow; GA (execution_duration_seconds and timeout_seconds were both added late Nov 2025)
A task that runs at 80-100% of its timeout is one slow input or one bad node away from being force-killed, which turns a green pipeline into a failed run and wastes the compute already spent. Catching these early lets you either raise a too-tight bound or fix the underlying slowdown before it causes a production outage and a costly re-run. The `runs_over_timeout` counter is more alarming: it flags tasks that reached or passed their configured limit yet were not killed, which points to a timeout that is not actually being enforced. Because the assessment depends on two late-populated columns, the query also tells you honestly how much of the fleet it could not evaluate, so you never mistake missing data for a clean bill of health.
WITH latest_tasks AS (
SELECT workspace_id, job_id, task_key, timeout_seconds, delete_time
FROM system.lakeflow.job_tasks
QUALIFY ROW_NUMBER() OVER (
PARTITION BY workspace_id, job_id, task_key ORDER BY change_time DESC
) = 1
),
task_end AS (
SELECT workspace_id, job_id, task_key, run_id, execution_duration_seconds
FROM system.lakeflow.job_task_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp()) -- drop incomplete current day
AND result_state IS NOT NULL -- task end row only
)
SELECT t.workspace_id, t.job_id, t.task_key,
lt.timeout_seconds,
COUNT(DISTINCT t.run_id) AS task_runs,
MAX(t.execution_duration_seconds) AS max_exec_s,
percentile_approx(t.execution_duration_seconds, 0.95) AS exec_s_p95,
-- runs whose execution reached >= :near_timeout_ratio of a CONFIGURED (non-null, >0) timeout
SUM(CASE WHEN lt.timeout_seconds IS NOT NULL AND lt.timeout_seconds > 0
AND t.execution_duration_seconds >= :near_timeout_ratio * lt.timeout_seconds
THEN 1 ELSE 0 END) AS runs_near_timeout,
-- runs that ran AT/PAST the configured timeout (should have been killed)
SUM(CASE WHEN lt.timeout_seconds IS NOT NULL AND lt.timeout_seconds > 0
AND t.execution_duration_seconds >= lt.timeout_seconds
THEN 1 ELSE 0 END) AS runs_over_timeout,
-- degradation buckets: a NULL/zero timeout is "no bound configured" (not a finding);
-- a NULL execution duration is "column not yet populated" (not assessed).
SUM(CASE WHEN lt.timeout_seconds IS NULL OR lt.timeout_seconds = 0
THEN 1 ELSE 0 END) AS runs_no_task_timeout,
SUM(CASE WHEN t.execution_duration_seconds IS NULL THEN 1 ELSE 0 END) AS runs_exec_null,
-- status: worst-first band on near/over-timeout run count (field heuristic;
-- :warn_near_timeout_runs / :crit_near_timeout_runs).
CASE
WHEN (SUM(CASE WHEN lt.timeout_seconds IS NOT NULL AND lt.timeout_seconds > 0
AND t.execution_duration_seconds >= :near_timeout_ratio * lt.timeout_seconds THEN 1 ELSE 0 END)
+ SUM(CASE WHEN lt.timeout_seconds IS NOT NULL AND lt.timeout_seconds > 0
AND t.execution_duration_seconds >= lt.timeout_seconds THEN 1 ELSE 0 END)) >= :crit_near_timeout_runs THEN 'CRITICAL'
WHEN (SUM(CASE WHEN lt.timeout_seconds IS NOT NULL AND lt.timeout_seconds > 0
AND t.execution_duration_seconds >= :near_timeout_ratio * lt.timeout_seconds THEN 1 ELSE 0 END)
+ SUM(CASE WHEN lt.timeout_seconds IS NOT NULL AND lt.timeout_seconds > 0
AND t.execution_duration_seconds >= lt.timeout_seconds THEN 1 ELSE 0 END)) >= :warn_near_timeout_runs THEN 'WARN'
ELSE 'OK'
END AS status
FROM task_end t
LEFT JOIN latest_tasks lt
ON t.workspace_id = lt.workspace_id
AND t.job_id = lt.job_id
AND t.task_key = lt.task_key
GROUP BY t.workspace_id, t.job_id, t.task_key, lt.timeout_seconds
ORDER BY (runs_near_timeout + runs_over_timeout) DESC:period_daysdefault 30rolling window in days:near_timeout_ratiodefault 0.8fraction of the configured timeout that counts as "near timeout":warn_near_timeout_runsdefault 3near/over-timeout runs for a task that flags WARN:crit_near_timeout_runsdefault 10that flags CRITICALworkspace_id, job_id, task_key) with how many runs it had and how long they took — the worst run (max_exec_s) and the p95 (exec_s_p95) in seconds.runs_near_timeout) and how many actually hit or blew past it without being killed (runs_over_timeout).runs_no_task_timeout) and those where the duration column is not yet populated (runs_exec_null) — so missing data reads as *not assessed*, never as a pass.| Column | How to read it |
|---|---|
| workspace_id | The workspace the task ran in. Part of the identity — job_id and task_key are unique only within a workspace, so always read the three together. |
| job_id | The parent job id (workspace-scoped). Identifies which workflow the task belongs to. |
| task_key | The task's name/key within the job (unique only within that job). |
| timeout_seconds | The configured task-level timeout in seconds from the latest task definition. NULL or 0 means no bound is configured — those runs are counted in runs_no_task_timeout, not as violations. |
| task_runs | Distinct task runs (COUNT DISTINCT run_id) observed for this task in the window. The denominator for the near/over counters. |
| max_exec_s | The single longest observed execution duration (seconds) across all runs of this task — the worst case. NULL when the duration column is unpopulated for every run. |
| exec_s_p95 | Approximate 95th-percentile execution duration (seconds). Compare against timeout_seconds to see how much headroom typical-but-slow runs have left. NULL when no run has a populated duration. |
| runs_near_timeout | Count of runs that reached >=80% of a configured (non-null, >0) timeout. The early-warning bucket — tasks approaching their limit. |
| runs_over_timeout | Count of runs that ran at or past the configured timeout yet were still recorded — tasks that should have been killed. The most urgent bucket (always a subset of runs_near_timeout). |
| runs_no_task_timeout | Count of runs on a task whose configured timeout is NULL or 0 — 'no bound configured', a governance gap but not a timeout finding. |
| runs_exec_null | Count of runs with a NULL execution duration — the column is not yet populated for those rows, so they are 'not assessed', not clean. |
runs_near_timeout and runs_over_timeout both at/near 0 - field heuristic; tune :near_timeout_ratio and :warn_near_timeout_runs for your account.
(runs_near_timeout + runs_over_timeout) at/above :warn_near_timeout_runs (WARN) or :crit_near_timeout_runs (CRITICAL) - field heuristic; runs_over_timeout > 0 is the more urgent signal since the task should already have been killed.
| workspace_id | job_id | task_key | timeout_seconds | task_runs | max_exec_s | exec_s_p95 | runs_near_timeout | runs_over_timeout | runs_no_task_timeout | runs_exec_null |
|---|---|---|---|---|---|---|---|---|---|---|
| 1234567890123456 | 894213765512340 | ingest_bronze | 3600 | 30 | 3720 | 3480 | 6 | 2 | 0 | 0 |
| 1234567890123456 | 778451236698120 | silver_merge | 0 | 14 | 410 | 395 | 0 | 0 | 14 | 0 |
| 9876543210987654 | 553120987764310 | gold_publish | 1800 | 22 | NULL | NULL | 0 | 0 | 0 | 22 |
Start with rows where `runs_over_timeout` > 0 (a limit that isn't being enforced), then rows where `runs_near_timeout` is a large share of `task_runs` or `exec_s_p95` sits just under `timeout_seconds`: either raise the timeout to a safe bound or investigate the slowdown before it trips. Treat large `runs_exec_null` / `runs_no_task_timeout` counts as 'not assessed' and re-run once the late-populated columns fill in, rather than assuming those tasks are healthy.
Feeds into: Job Health page — retrying / hanging tasks running close to (or past) their timeout (reliability-4)
A 30-day tally, per workspace, of how many job runs ended with each termination code — the root-cause taxonomy of why runs stopped.
system.lakeflow.job_run_timelineOne row = a (workspace, termination_code) combination in the window. Use run_rows / distinct_runs to see which termination reasons dominate before drilling into the specific failing jobs.
RequiresSELECT on system.lakeflow; GA
Failures rarely announce their cause — this turns a wall of failed runs into a ranked list of root causes per workspace, so you fix the dominant one first instead of guessing. It specifically surfaces quota and capacity ceilings (`WORKSPACE_RUN_LIMIT_EXCEEDED`, `MAX_JOB_QUEUE_SIZE_EXCEEDED`, `CLUSTER_ERROR`, `STORAGE_ACCESS_ERROR`) that are platform-side, not code bugs — the kind an admin can actually fix by raising a limit or fixing access, often unblocking many jobs at once. A concentration of one code in one workspace is a signal to act there; the counts tell you how much pain each cause is really causing.
SELECT workspace_id, termination_code,
COUNT(*) AS run_rows,
COUNT(DISTINCT run_id) AS distinct_runs
FROM system.lakeflow.job_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_end_time < date_trunc('DAY', current_timestamp())
AND result_state IS NOT NULL -- end row only
AND termination_code IS NOT NULL
GROUP BY workspace_id, termination_code
ORDER BY workspace_id, run_rows DESC:period_daysdefault 30rolling window in days| Column | How to read it |
|---|---|
| workspace_id | The workspace the runs belong to. Termination codes are counted per workspace because job/run ids are only unique within a workspace; the same code can appear on multiple rows, one per workspace. |
| termination_code | The root-cause code Databricks assigned to why the run stopped (e.g. WORKSPACE_RUN_LIMIT_EXCEEDED, MAX_JOB_QUEUE_SIZE_EXCEEDED, CLUSTER_ERROR, STORAGE_ACCESS_ERROR). This is the finer root-cause classification, not the coarser termination_type (which is deliberately excluded here as unverified). |
| run_rows | Count of end-rows carrying this code. Because retries reuse a run_id and long runs are hourly-sliced, this can exceed the number of actual runs — use it only alongside distinct_runs. |
| distinct_runs | Count of distinct run_id values that ended with this code — the honest 'how many runs' number. This is the figure to rank and act on. |
Inventory / reference query — it returns state to read, not a pass/fail band.
| workspace_id | termination_code | run_rows | distinct_runs |
|---|---|---|---|
| 1234567890123456 | WORKSPACE_RUN_LIMIT_EXCEEDED | 142 | 118 |
| 1234567890123456 | CLUSTER_ERROR | 37 | 37 |
| 9876543210987654 | MAX_JOB_QUEUE_SIZE_EXCEEDED | 64 | 51 |
Rank each workspace's codes by `distinct_runs`, then attack the dominant one at its source: raise the concurrent-run or queue-size limit for `WORKSPACE_RUN_LIMIT_EXCEEDED` / `MAX_JOB_QUEUE_SIZE_EXCEEDED`, investigate cluster config or cloud capacity for `CLUSTER_ERROR`, and fix storage credentials/paths for `STORAGE_ACCESS_ERROR` — one limit change can unblock many jobs at once.
Feeds into: termination taxonomy; failed runs
A runtime discovery probe that lists the distinct raw `termination_type` values appearing on finished job runs per workspace over the last 30 days, with a count of how many end-of-run rows carry each value.
system.lakeflow.job_run_timelineOne row = a (workspace, termination_type) combination in the window, with a raw row count. Run this once to discover the actual termination_type values on your account before building any logic that filters or bands on them.
RequiresSELECT on system.lakeflow; GA (the column exists; its value enum is unverified - see caveats)
The `termination_type` column is confirmed to exist but its set of possible values is UNVERIFIED, and it does not populate before early December 2025 — so any dashboard or alert built on hardcoded termination-type values could silently break or return nothing on your account. Running this probe first tells you whether the column is actually populated and exactly which values your metastore emits, before you invest in taxonomy logic that depends on it. It is a cheap correctness check that prevents shipping reliability reporting built on assumptions the data does not support.
-- NEEDS CONFIRMATION: termination_type column exists but its value enum is UNVERIFIED.
-- This probe discovers distinct values at runtime; confirm the column populates on the account.
SELECT workspace_id, termination_type, COUNT(*) AS run_rows
FROM system.lakeflow.job_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND result_state IS NOT NULL
GROUP BY workspace_id, termination_type
ORDER BY workspace_id, run_rows DESC:period_daysdefault 30rolling window in daystermination_type values your account actually emits on completed job runs, rather than assuming a fixed value list.NULL result means the column is not yet populated on the account, not that runs terminated cleanly.| Column | How to read it |
|---|---|
| workspace_id | The workspace the finished runs belong to. Values are workspace-scoped, so the same termination_type may appear once per workspace. |
| termination_type | The raw, undeduced `termination_type` value straight from the run timeline (the coarse termination class). A NULL here means the column is not populated for those rows — treat it as 'not assessed / not yet populated,' never as a clean/successful termination. |
| run_rows | The number of end-of-run timeline rows carrying that termination_type in the workspace. This is a row count of finished-run slices, NOT a distinct-run count, so retries and multi-slice runs can inflate it — use it only to gauge whether/how heavily a value appears, not as a precise run tally. |
Inventory / reference query — it returns state to read, not a pass/fail band.
| workspace_id | termination_type | run_rows |
|---|---|---|
| 1234567890123456 | SUCCESS | 842 |
| 1234567890123456 | CLIENT_ERROR | 37 |
| 9876543210987654 | NULL (column not yet populated) | 1290 |
Run this first on a workspace with recent (post-early-Dec-2025) job activity. If you get non-NULL `termination_type` values, record the actual distinct list and use it to drive your termination taxonomy; if every row is NULL, the column is not yet populated on your account — do not build alerting on `termination_type` and fall back to `termination_code` instead.
Feeds into: termination taxonomy
A daily breakdown, per workspace, of how many distinct job runs were active and how many execution-seconds they consumed, sliced by run type and trigger type over the trailing 30 days.
system.lakeflow.job_run_timelineOne row = a (workspace, run_type, trigger_type, day) combination. Use distinct_runs and execution_s_total to see your run-type/trigger mix and daily volume before drilling into a specific failure or cost finding.
RequiresSELECT on system.lakeflow; GA (execution_duration_seconds was added early Dec 2025)
This is the portfolio baseline for your Jobs estate: it shows an admin where run volume and compute hours actually go, split by whether runs are scheduled jobs, ad-hoc submit runs, or notebook-driven workflow runs. Unexplained spikes in run counts or execution hours are early signals of runaway schedules, retry storms, or a team quietly moving heavy work onto jobs compute — each of which shows up as DBUs on the bill. Because it is source data for the failed-runs and reliability views, getting the mix right frames where to spend the deeper (and costlier) investigation.
SELECT workspace_id, run_type, trigger_type,
date_trunc('DAY', period_start_time) AS run_day,
COUNT(DISTINCT run_id) AS distinct_runs,
SUM(CASE WHEN result_state IS NOT NULL THEN 1 ELSE 0 END) AS completed_run_rows,
SUM(execution_duration_seconds) AS execution_s_total
FROM system.lakeflow.job_run_timeline
WHERE period_start_time >= dateadd(day, -:period_days, current_date())
AND period_start_time < date_trunc('DAY', current_timestamp())
GROUP BY workspace_id, run_type, trigger_type, date_trunc('DAY', period_start_time)
ORDER BY workspace_id, run_day DESC:period_daysdefault 30rolling window in daysrun_type (scheduled JOB_RUN, ad-hoc SUBMIT_RUN, notebook-driven WORKFLOW_RUN) and by trigger_type.WORKFLOW_RUN in the counts for run-mix visibility only — its compute belongs to the parent notebook, so its seconds are not a job cost you should bill against the job.| Column | How to read it |
|---|---|
| workspace_id | The workspace the runs belong to. Ids are workspace-scoped, so always read the mix per workspace, never pooled across the metastore. |
| run_type | How the run originated: JOB_RUN (a defined/scheduled job), SUBMIT_RUN (an ad-hoc one-off submit), or WORKFLOW_RUN (a run launched from within a notebook/workflow). WORKFLOW_RUN is here for counting only — do not attribute its DBUs to the job. |
| trigger_type | What kicked off the run (e.g. a schedule, a manual trigger, RETRY_ON_FAILURE). Lets you separate organic runs from retry-driven volume. |
| run_day | The calendar day the run slices are bucketed into, from day-truncating period_start_time (the slice's period start). One row per (workspace, run_type, trigger_type, day); a run crossing midnight can appear in two adjacent day buckets. |
| distinct_runs | Count of distinct run_ids active in the bucket — the true run volume for that day, already netted out of hourly slicing. This is your headline 'how many runs' number (all runs present, not only completed ones). |
| completed_run_rows | Count of end-rows (slices carrying a final result_state) in the bucket. Read as an 'end-row count,' not a run count: retries reuse the same run_id and add extra end rows, so this can exceed distinct_runs. |
| execution_s_total | Total execution_duration_seconds (seconds, not dollars) summed across the bucket. NULL/zero on short-history accounts because this column is not populated before early-Dec-2025 — treat empty as 'not yet populated,' not as zero work. |
Inventory / reference query — it returns state to read, not a pass/fail band.
| workspace_id | run_type | trigger_type | run_day | distinct_runs | completed_run_rows | execution_s_total |
|---|---|---|---|---|---|---|
| 1470123456789012 | JOB_RUN | SCHEDULED | 2026-06-28 | 142 | 148 | 53400 |
| 1470123456789012 | WORKFLOW_RUN | MANUAL | 2026-06-28 | 9 | 9 | 3120 |
| 8899223344556677 | SUBMIT_RUN | RETRY_ON_FAILURE | 2026-06-28 | 4 | 7 | 860 |
Use the daily run mix to right-size your job portfolio: see where run volume and execution hours concentrate (scheduled vs submit vs workflow runs), spot day-over-day spikes worth investigating, and identify high-hour trigger types to target for compute or scheduling tuning. Treat it as a portfolio baseline, then drill into specific workspaces with the reliability and cost queries.
Feeds into: workload mix & hours; failed runs