What compute exists, how it's configured, and how hard it's actually working. Cluster and SQL-warehouse config, node utilization, idle ratio, autoscale churn, idle tails and pools.
The configuration history of classic compute — all-purpose, jobs, Lakeflow SDP/pipeline, and pipeline-maintenance clusters. Excludes SQL warehouses and serverless entirely.
cluster_id per configuration change (change_time). Queries take the latest row per cluster_id (ROW_NUMBER() … ORDER BY change_time DESC) and keep only rows where delete_time IS NULL.cluster_id, cluster_name, owned_by, driver_node_type / worker_node_type, worker_count (NULL when autoscaling), min_autoscale_workers / max_autoscale_workers (NULL when fixed-size), auto_termination_minutes (idle auto-stop config), enable_elastic_disk, cluster_source, dbr_version (runtime/EOL sprawl), data_security_mode (access-mode posture: USER_ISOLATION / SINGLE_USER / LEGACY_* / NONE / null), policy_id (NULL = no compute-policy coverage), driver_instance_pool_id / worker_instance_pool_id, tags (chargeback), init_scripts, aws_attributes / azure_attributes / gcp_attributes (STRUCTs — only the host cloud's is populated; hold spot/on-demand mix), create_time / delete_time / change_time, workspace_id, account_id.SELECT on system.compute (enable the compute schema per-metastore). Regional — run per metastore region. No runtime_engine / Photon column (confirmed absent on all clouds). Cloud-attribute STRUCTs are selected whole; extracting a named subfield is unverified.Configuration history of SQL warehouses.
warehouse_id per change; queries take the latest row and keep delete_time IS NULL.warehouse_id, warehouse_name, workspace_id, account_id, warehouse_type (CLASSIC/PRO/SERVERLESS), warehouse_channel, warehouse_size (XS … 4X_LARGE, plus 5X_LARGE Beta on PRO/SERVERLESS), min_clusters / max_clusters (autoscaling range), auto_stop_minutes (idle suspend config), tags (map, chargeback), change_time, delete_time.SELECT on system.compute. Regional. Empty if the workspace runs no SQL warehouses.State-transition event log for SQL warehouses — the behavioral counterpart to warehouses.
event_time).warehouse_id, event_type, event_time, cluster_count (clusters running at event time). Authoritative 6-value enum: SCALED_UP, SCALED_DOWN, STOPPING, RUNNING, STARTING, STOPPED. (SCALING_UP / SCALING_DOWN appear in one official sample but are undocumented — the queries ignore them.)SELECT on system.compute. Regional. Carries no DBU/$ — a warehouse's idle tail and autoscale churn are behavioral only. Retention is limited; window queries assume events exist within :period_days.Per-minute hardware-utilization telemetry for classic-compute nodes (driver and every worker).
cluster_id, node_type, driver (bool), start_time / end_time, cpu_user_percent / cpu_system_percent (busy = sum; idle threshold in compute_idle_node_ratio is sum < 5%), cpu_wait_percent, mem_used_percent (0-100, includes background processes), network_sent_bytes / network_received_bytes. (disk_free_bytes_per_mount_point is a map — not selected.)SELECT on system.compute. Regional. Retention is 90 days only — :lookback_days is capped at 90; a longer window silently truncates. Classic compute only — there are no rows for SQL warehouses or serverless. Nodes that ran < 10 minutes may not appear (short-job blind spot). No DBU/$.Static reference dimension of node/instance types and their hardware specs — the denominator for right-sizing.
node_type. No history, no aggregation.node_type (join key to clusters.driver/worker_node_type and node_timeline.node_type), core_count (double), memory_mb (long), gpu_count (long), account_id.SELECT on system.compute. Indefinite retention. Cloud-specific catalog (the node types listed are those available in the account's cloud/region).Configuration history of instance pools (pre-warmed idle VMs shared by clusters).
instance_pool_id, keep delete_time IS NULL.instance_pool_id, instance_pool_name, node_type, min_idle_instances (bigint — the always-on idle floor that drives pool waste), max_capacity (bigint), idle_instance_autotermination_minutes, enable_elastic_disk, preloaded_spark_version, preloaded_docker_images (Docker/preload risk), tags, aws/azure/gcp_attributes + disk_spec (STRUCTs selected whole), create_time / delete_time / change_time, workspace_id, account_id.TABLE_OR_VIEW_NOT_FOUND if the schema isn't enabled). UC + SELECT on system.compute. Regional. Idle-waste dollarization needs node cost from the billing domain, not here.Cloud-VM lifecycle/state-transition events for classic-compute instances — the source for spot-vs-on-demand mix and true instance idle-vs-active time.
event_time).workspace_id, instance_id, node_type, availability_type (ON_DEMAND/SPOT on AWS/Azure, ON_DEMAND/PREEMPTIBLE on GCP), state (INSTANCE_LAUNCHING / INSTANCE_READY = idle / INSTANCE_PLACED = in use / INSTANCE_TERMINATED), event_type (INSTANCE_LAUNCHING / STATE_TRANSITION), cluster_id (populated only when state = INSTANCE_PLACED), event_time.SELECT on system.compute. Regional, cloud-specific enum values. The query is a count/summary aggregate; converting READY vs PLACED events into exact idle-vs-active minutes needs per-instance windowing that is not attempted here.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.Per SQL warehouse, the total seconds spent RUNNING (the auto-stop idle tail), STARTING (cold-start tax) and STOPPED, the worst single RUNNING gap, and event counts by state — all derived from state-transition events over the trailing :period_days window.
system.compute.warehouse_eventsOne row = one SQL warehouse's time spent in each event state (RUNNING, STARTING, STOPPED) over the window, computed as the gap from each event to that warehouse's very next event (any type). The columns that matter are running_seconds (total time the warehouse sat RUNNING before its next event - a mix of active query time and the idle tail before auto-stop, since per-query activity is deliberately not joined into this query) and max_running_gap_seconds (the single longest continuous RUNNING stretch - an unusually long one usually means the warehouse never triggered its own auto-stop for a long time).
RequiresSELECT on system.compute, system.billing; GA
A SQL warehouse keeps billing compute for the entire stretch it sits RUNNING-but-idle before auto_stop_minutes finally suspends it — that idle tail is pure waste, repeated every time the warehouse goes quiet. This query quantifies exactly how many seconds each warehouse spent in that RUNNING tail and exposes the single worst gap, so an admin can prove a warehouse's auto_stop_minutes is set too high. It also separates STARTING seconds (the cold-start tax you pay for aggressive auto-stop) so the trade-off is visible, not guessed. The numbers are seconds and event counts only; the dollar impact appears after joining warehouse DBU usage from the billing domain.
WITH ordered AS (
SELECT
warehouse_id,
event_type,
event_time,
LEAD(event_time) OVER (PARTITION BY warehouse_id ORDER BY event_time) AS next_event_time
FROM system.compute.warehouse_events
WHERE event_time >= current_timestamp() - INTERVAL :period_days DAYS
),
gaps AS (
SELECT
warehouse_id,
event_type,
-- gap (seconds) this warehouse spent in `event_type` until its NEXT event.
-- The last event per warehouse has next_event_time = NULL -> gap NULL (open, excluded).
CASE
WHEN next_event_time IS NULL THEN NULL
ELSE unix_timestamp(next_event_time) - unix_timestamp(event_time)
END AS gap_seconds
FROM ordered
),
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 per-warehouse DBU/$ over the SAME :period_days window as the query above.
-- warehouse_id is a globally-unique GUID, so we key on it alone (no workspace_id).
SELECT
u.usage_metadata.warehouse_id AS warehouse_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.warehouse_id IS NOT NULL
AND u.usage_date >= current_date() - INTERVAL :period_days DAYS
AND u.usage_date < current_date()
GROUP BY u.usage_metadata.warehouse_id
)
SELECT
g.warehouse_id,
COUNT(*) AS event_count,
-- seconds the warehouse held RUNNING before the next event (the auto-stop idle tail lives here)
SUM(CASE WHEN g.event_type = 'RUNNING' AND g.gap_seconds IS NOT NULL THEN g.gap_seconds ELSE 0 END) AS running_seconds,
-- seconds spent STARTING (cold-start tax) and STOPPED (suspended, not billing compute)
SUM(CASE WHEN g.event_type = 'STARTING' AND g.gap_seconds IS NOT NULL THEN g.gap_seconds ELSE 0 END) AS starting_seconds,
SUM(CASE WHEN g.event_type = 'STOPPED' AND g.gap_seconds IS NOT NULL THEN g.gap_seconds ELSE 0 END) AS stopped_seconds,
-- longest single RUNNING gap = the worst observed idle tail before a stop/next event
MAX(CASE WHEN g.event_type = 'RUNNING' AND g.gap_seconds IS NOT NULL THEN g.gap_seconds END) AS max_running_gap_seconds,
SUM(CASE WHEN g.event_type = 'RUNNING' THEN 1 ELSE 0 END) AS running_events,
SUM(CASE WHEN g.event_type = 'STARTING' THEN 1 ELSE 0 END) AS starting_events,
SUM(CASE WHEN g.event_type = 'STOPPED' THEN 1 ELSE 0 END) AS stopped_events,
-- ADDED cost visibility (see header caveats): exact billed DBUs and list-price $ ESTIMATE for this warehouse
COALESCE(cr.net_dbus, 0) AS net_dbus,
COALESCE(cr.est_usd_list, 0) AS est_usd_list,
-- status: worst-first band on the single longest continuous RUNNING stretch (field heuristic; :warn_idle_hours / :crit_idle_hours).
CASE
WHEN MAX(CASE WHEN g.event_type = 'RUNNING' AND g.gap_seconds IS NOT NULL THEN g.gap_seconds END) IS NULL THEN 'NOT_ASSESSED'
WHEN MAX(CASE WHEN g.event_type = 'RUNNING' AND g.gap_seconds IS NOT NULL THEN g.gap_seconds END) >= :crit_idle_hours * 3600 THEN 'CRITICAL'
WHEN MAX(CASE WHEN g.event_type = 'RUNNING' AND g.gap_seconds IS NOT NULL THEN g.gap_seconds END) >= :warn_idle_hours * 3600 THEN 'WARN'
ELSE 'OK'
END AS status
FROM gaps g
LEFT JOIN cost_rollup cr
ON g.warehouse_id = cr.warehouse_id
GROUP BY g.warehouse_id, cr.net_dbus, cr.est_usd_list
ORDER BY CASE status WHEN 'CRITICAL' THEN 0 WHEN 'WARN' THEN 1 WHEN 'OK' THEN 2 ELSE 3 END, max_running_gap_seconds DESC:period_daysdefault 30rolling window in days:warn_idle_hoursdefault 4hours of a single continuous RUNNING stretch that flags WARN:crit_idle_hoursdefault 24hours that flags CRITICALauto_stop_minutes on the offenders.net_dbus) and a list-price USD estimate (est_usd_list) per warehouse, 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 |
|---|---|
| warehouse_id | UUID of the SQL warehouse (passed through, unmasked). Join key back to system.compute.warehouses / sql_warehouse_config_current to recover name, size and auto_stop_minutes. |
| event_count | Total state-transition events for this warehouse within the :period_days window, including the final event whose forward gap is NULL. A coarse activity/churn indicator, not a gap count. |
| running_seconds | Total seconds the warehouse held the RUNNING state before its next event — the auto-stop idle tail lives here. Higher = more RUNNING-but-idle time to reclaim by lowering auto_stop_minutes. Seconds, not dollars. |
| starting_seconds | Total seconds spent STARTING (cold-start / spin-up tax). Rises as auto-stop is made more aggressive — the counterweight to cutting running_seconds. Seconds. |
| stopped_seconds | Total seconds the warehouse sat STOPPED (suspended, not billing compute). Large values are healthy — the warehouse was correctly off. Seconds. |
| max_running_gap_seconds | The single longest RUNNING gap observed — the worst idle tail before a stop/next event. Compare against configured auto_stop_minutes*60: a value far exceeding it signals mis-config or keep-warm trickle queries. NULL if the warehouse had no non-NULL RUNNING gap. |
| running_events | Count of RUNNING events in the window (each RUNNING transition, whether or not its gap was NULL). Denominator context for running_seconds. |
| starting_events | Count of STARTING events — how many times the warehouse cold-started in the window. |
| stopped_events | Count of STOPPED events — how many times the warehouse suspended in the window. |
| net_dbus | Exact billed DBUs (usage_unit='DBU') attributed to this warehouse (billing warehouse_id) over the :period_days 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. |
status = OK (max_running_gap_seconds below :warn_idle_hours hours) - field heuristic; tune :warn_idle_hours / :crit_idle_hours to your typical auto_stop_minutes settings.
status = WARN or CRITICAL (a single RUNNING stretch at or above :warn_idle_hours / :crit_idle_hours) - field heuristic. status = NOT_ASSESSED means this warehouse had zero RUNNING events in the window (running_seconds=0), which this query still surfaces rather than dropping, since an unused warehouse is itself worth a look.
| warehouse_id | event_count | running_seconds | starting_seconds | stopped_seconds | max_running_gap_seconds | running_events | stopped_events | net_dbus | est_usd_list |
|---|---|---|---|---|---|---|---|---|---|
| a1b2c3d4-0000-4a5b-8c6d-1e2f3a4b5c6d | 14 | 5400 | 240 | 78000 | 1800 | 6 | 3 | 980 | 490.00 |
| f9e8d7c6-1111-4b2a-9d3e-2f1a0b9c8d7e | 4 | 0 | 60 | 259200 | NULL | 0 | 1 | 40 | 20.00 |
| 7c6b5a4d-2222-4c3b-8e4f-3a2b1c0d9e8f | 22 | 900 | 900 | 5400 | 300 | 9 | 9 | 260 | 130.00 |
Rank warehouses by running_seconds and max_running_gap_seconds; for the worst offenders, cross-check auto_stop_minutes in sql_warehouse_config_current and lower it so warehouses suspend sooner, cutting idle-tail waste. A warehouse showing max_running_gap_seconds far larger than its configured auto_stop_minutes*60 is either mis-configured or being kept warm by trickle queries — investigate before tuning.
Feeds into: SQL-warehouse idle time vs auto_stop_minutes — warehouse-idle-1; time a warehouse sat
One row per live classic compute cluster showing its latest configuration — node types, autoscale range, idle auto-stop, runtime, access mode, policy coverage, pool linkage, tags, init scripts and cloud attributes.
system.compute.clustersOne row = the latest known configuration for one classic cluster (all-purpose, job, Lakeflow SDP, or pipeline-maintenance) that has not been deleted. The columns that matter are data_security_mode (access-mode posture) and policy_id (NULL means the cluster is not governed by a compute policy).
RequiresSELECT on system.compute; GA
This is the right-sizing and governance baseline for all classic compute you are paying for. Oversized fixed-size clusters, clusters that never auto-stop, and end-of-life runtimes are where compute spend quietly leaks, while clusters with no compute policy (`policy_id` NULL), a weak `data_security_mode`, or risky init scripts are where governance and security exposure lives. Missing or inconsistent `tags` breaks chargeback so cost lands in an unattributable bucket. The table carries no DBUs or dollars itself — it tells you what is misconfigured; the billing domain tells you what that misconfiguration costs.
SELECT cluster_id,
CASE WHEN cluster_name IS NULL THEN cluster_name ELSE concat(substr(cluster_name, 1, 2), '****') END AS cluster_name,
CASE
WHEN owned_by IS NULL OR owned_by = '__REDACTED__' THEN owned_by
WHEN owned_by LIKE '%@%' THEN concat(substr(owned_by, 1, 2), '****@****')
WHEN owned_by RLIKE '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' THEN owned_by
ELSE concat(substr(owned_by, 1, 2), '****')
END AS owned_by,
driver_node_type, worker_node_type, worker_count,
min_autoscale_workers, max_autoscale_workers, auto_termination_minutes, enable_elastic_disk,
cluster_source, dbr_version, data_security_mode, policy_id, driver_instance_pool_id,
worker_instance_pool_id, tags, init_scripts, aws_attributes, azure_attributes, gcp_attributes,
create_time, delete_time, change_time, workspace_id, account_id
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY cluster_id ORDER BY change_time DESC) AS rn
FROM system.compute.clusters
)
WHERE rn = 1 AND delete_time IS NULL
ORDER BY cluster_idworker_count) or autoscaling (min/max_autoscale_workers), and its idle auto-stop setting (auto_termination_minutes).dbr_version) for EOL/sprawl, access mode (data_security_mode), compute-policy coverage (policy_id), instance-pool linkage, tags for chargeback, and init_scripts.aws_/azure_/gcp_attributes) through whole so you can inspect spot-vs-on-demand mix; identifiers cluster_name and owned_by are masked.| Column | How to read it |
|---|---|
| cluster_id | Stable unique id of the cluster; the join key to billing usage, node_timeline and instance_events. |
| cluster_name | Masked display name (first 2 chars + `****`); for grouping/recognition only, not exact matching. NULL passes through as NULL. |
| owned_by | Masked owner — emails become `da****@****`, UUID service principals pass through intact, `__REDACTED__` and NULL pass through unchanged, and any other value is truncated to its first 2 chars + `****`. |
| driver_node_type | Instance type of the driver node; join to node_types_reference for its vCPU/memory to judge right-sizing. |
| worker_node_type | Instance type of the worker nodes; a large type with low utilization is the oversizing signal. |
| worker_count | Fixed worker count. Populated only for fixed-size clusters; NULL means the cluster autoscales (see the min/max columns). |
| min_autoscale_workers | Lower bound of the autoscale range. NULL for fixed-size clusters. |
| max_autoscale_workers | Upper bound of the autoscale range. NULL for fixed-size clusters; a high ceiling means a high worst-case node bill. |
| auto_termination_minutes | Idle minutes before the cluster auto-stops. A high value or NULL/0 (no auto-stop) is a prime idle-spend candidate. |
| enable_elastic_disk | Whether local-disk autoscaling (elastic disk) is enabled for the cluster. |
| cluster_source | What created the cluster (e.g. all-purpose UI, JOB, pipeline/Lakeflow) — distinguishes interactive from job compute. |
| dbr_version | Databricks Runtime version string; scan for old/EOL runtimes and version sprawl. There is no separate Photon/runtime_engine column. |
| data_security_mode | Access-mode posture enum: USER_ISOLATION / SINGLE_USER / LEGACY_PASSTHROUGH / LEGACY_SINGLE_USER / LEGACY_TABLE_ACL / NONE / null. LEGACY_* and NONE are the weak-governance flags. |
| policy_id | Compute policy governing the cluster. NULL means no compute-policy coverage — an ungoverned cluster. |
| driver_instance_pool_id | Instance pool backing the driver, if any; NULL means the driver is not pool-backed. |
| worker_instance_pool_id | Instance pool backing the workers, if any; NULL means workers are not pool-backed. |
| tags | Tag map used for chargeback/cost attribution; empty or missing tags mean the cluster's spend is unattributable. |
| init_scripts | Configured init scripts; a non-empty value flags bootstrap/customization that carries supply-chain and reproducibility risk. |
| aws_attributes | AWS cloud struct, selected whole. Populated only on AWS accounts; holds spot/on-demand mix. Extracting a named subfield is unverified. |
| azure_attributes | Azure cloud struct, selected whole. Populated only on Azure accounts; subfield extraction is unverified. |
| gcp_attributes | GCP cloud struct, selected whole. Populated only on GCP accounts; subfield extraction is unverified. |
| create_time | When the cluster was first created. |
| delete_time | Deletion timestamp; always NULL in this result set because deleted clusters are excluded. |
| change_time | Timestamp of the configuration change that produced this (latest) row — how fresh the snapshot is for this cluster. |
| workspace_id | Workspace the cluster belongs to; part of the workspace-scoped join key to billing/telemetry. |
| account_id | Databricks account id. |
Inventory / reference query — it returns state to read, not a pass/fail band.
| cluster_id | cluster_name | owned_by | worker_node_type | worker_count | auto_termination_minutes | data_security_mode | policy_id |
|---|---|---|---|---|---|---|---|
| 0712-091203-abcd1234 | et**** | da****@**** | i3.4xlarge | 8 | 0 | NONE | NULL |
| 0518-140755-wxyz5678 | ni**** | 8f4c1a2b-3d6e-4f70-9a12-5b6c7d8e9f01 | r5d.2xlarge | NULL | 60 | USER_ISOLATION | A1B2C3D4E5F60789 |
| 0301-013045-mnop3456 | pr**** | da****@**** | m5.xlarge | 4 | NULL | LEGACY_SINGLE_USER | NULL |
Sort the output for right-sizing and governance triage: fixed-size clusters (non-null `worker_count`) on large `worker_node_type`, rows where `auto_termination_minutes` is high or NULL/0 (no idle auto-stop), `policy_id IS NULL` (no compute-policy coverage), EOL `dbr_version`, weak `data_security_mode` (`NONE`/`LEGACY_*`), and empty `tags`. Then dollarize the worst offenders by joining `cluster_id`/`workspace_id` to `system.billing.usage` in the billing domain.
Feeds into: oversized/right-sizing classic clusters; idle auto-stop config; cluster access-mode
One row per classic cluster over the lookback window, reporting total vs idle minute-slices (idle = CPU under 5%) alongside average and peak CPU and memory and the observed time span - the ranked auto-stop / auto-termination candidate list.
system.compute.node_timelineOne row = one classic cluster over the window. idle_ratio (idle minute-slices / total minute-slices) is the column that matters; est_wasted_usd_list scales the cluster's list-price DBU cost by that idle fraction so you can rank the biggest likely savings. High idle_ratio + meaningful est_wasted_usd_list = an auto-stop / downsizing candidate.
RequiresSELECT on system.compute, system.billing; GA
Classic clusters that sit running but idle burn DBUs (and the underlying cloud VMs) for zero work - the single most common source of avoidable compute spend. This query surfaces exactly which clusters spent most of their runtime below 5% CPU, giving an admin a prioritized list to attach or tighten auto-termination on. Because the source carries no dollars, the finding is a behavioral signal: the fix (shorter `auto_termination_minutes`, right-sizing) is what converts an idle ratio into recovered spend once cross-referenced with billing.
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 (
-- Pre-aggregated per cluster_id (globally-unique GUID -> workspace_id not needed for the join).
SELECT 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, -LEAST(:period_days, 90), current_date())
AND u.usage_date < current_date()
GROUP BY u.usage_metadata.cluster_id
),
finding AS (
SELECT
cluster_id,
-- lexicographically-largest node_type label (representative only, not load-weighted)
MAX(node_type) AS node_type,
COUNT(*) AS total_slices,
-- idle = CPU busy (user+system) below :idle_cpu_pct on that minute-slice; over ALL slices.
SUM(CASE WHEN (cpu_user_percent + cpu_system_percent) < :idle_cpu_pct THEN 1 ELSE 0 END) AS idle_slices,
COUNT(DISTINCT CASE WHEN driver THEN NULL ELSE node_type END) AS worker_node_type_variants,
-- utilization averaged over ALL slices (NOT only idle rows) so the mean is honest.
AVG(cpu_user_percent + cpu_system_percent) AS avg_cpu_pct_all,
MAX(cpu_user_percent + cpu_system_percent) AS peak_cpu_pct_all,
AVG(mem_used_percent) AS avg_mem_pct_all,
MAX(mem_used_percent) AS peak_mem_pct_all,
MIN(start_time) AS first_slice,
MAX(end_time) AS last_slice
FROM system.compute.node_timeline
WHERE start_time >= dateadd(DAY, -LEAST(:period_days, 90), current_date())
GROUP BY cluster_id
)
SELECT
f.cluster_id,
f.node_type,
f.total_slices,
f.idle_slices,
-- idle_ratio = idle minute-slices / total minute-slices (a fraction 0-1, never "hours").
f.idle_slices / NULLIF(f.total_slices, 0) AS idle_ratio,
f.worker_node_type_variants,
f.avg_cpu_pct_all,
f.peak_cpu_pct_all,
f.avg_mem_pct_all,
f.peak_mem_pct_all,
f.first_slice,
f.last_slice,
COALESCE(cr.net_dbus, 0) AS net_dbus,
COALESCE(cr.est_usd_list, 0) AS est_usd_list,
-- directional wasted-$ overlay: list-price cost scaled by the idle fraction.
COALESCE(cr.est_usd_list, 0) * (f.idle_slices / NULLIF(f.total_slices, 0)) AS est_wasted_usd_list,
-- status: worst-first band on idle_ratio (field heuristic). Too few slices -> NOT_ASSESSED.
CASE
WHEN f.total_slices < :min_slices THEN 'NOT_ASSESSED'
WHEN f.idle_slices / NULLIF(f.total_slices, 0) >= :crit_idle_ratio THEN 'CRITICAL'
WHEN f.idle_slices / NULLIF(f.total_slices, 0) >= :warn_idle_ratio THEN 'WARN'
ELSE 'OK'
END AS status
FROM finding f
LEFT JOIN cost_rollup cr
ON f.cluster_id = cr.cluster_id
ORDER BY est_wasted_usd_list DESC
LIMIT :top_n:period_daysdefault 30rolling window in days, capped at 90 in-SQL by node_timeline retention:idle_cpu_pctdefault 5CPU busy percent below which a minute-slice counts as idle:min_slicesdefault 60minimum node-minutes before a cluster is judged (fewer -> NOT_ASSESSED):warn_idle_ratiodefault 0.5idle-slice fraction that flags WARN:crit_idle_ratiodefault 0.8idle-slice fraction that flags CRITICAL:top_ndefault 200row captotal_slices) versus idle (idle_slices, where CPU stayed under 5%), so you can rank clusters by the share of runtime that was genuinely idle.first_slice to last_slice) so you know how recently and how long each cluster was seen. Covers classic compute only - no SQL warehouses or serverless.net_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 |
|---|---|
| cluster_id | The classic cluster (all-purpose / jobs / pipeline) whose per-minute node telemetry was rolled up. Join key back to `system.compute.clusters` for its config and auto-termination setting. |
| node_type | A representative instance-type label for the cluster - the lexicographically-largest `node_type` observed (`MAX(node_type)`), not load-weighted. For a mixed-type cluster this is only one of the types present. |
| total_slices | Total node-minute slices observed for this cluster in the window (driver plus every worker). This is minutes of node-time, not hours and not a node count - use it as the denominator and as a floor to filter out barely-run clusters. |
| idle_slices | How many of those minute-slices were idle - CPU busy (`cpu_user_percent + cpu_system_percent`) under 5%. Divide by `total_slices` to get the idle ratio that ranks auto-stop candidates. |
| worker_node_type_variants | Count of distinct worker (non-driver) node types seen. 1 = homogeneous workers; >1 flags a heterogeneous or reconfigured-over-time cluster whose single `node_type` label is less representative. |
| avg_cpu_pct_all | Mean CPU busy percent (user+system) across ALL slices, 0-100. A low value corroborates a high idle ratio - the cluster was chronically underworked, not just occasionally quiet. |
| peak_cpu_pct_all | Highest single-slice CPU busy percent, 0-100. A low peak means the cluster never got busy at all (safe to shrink/stop); a high peak means it was idle most of the time but did spike (tune auto-stop rather than downsize). |
| avg_mem_pct_all | Mean memory-used percent (`mem_used_percent`) across all slices, 0-100. Includes background processes, so it overstates true application memory pressure - read it as a ceiling, not app demand. |
| peak_mem_pct_all | Highest single-slice memory-used percent, 0-100. High peak memory with low CPU can indicate a memory-bound (not idle) workload despite a high idle-by-CPU ratio - inspect before downsizing. |
| first_slice | Earliest slice start observed (`MIN(start_time)`) for the cluster in the window - the start of the measured span. |
| last_slice | Latest slice end observed (`MAX(end_time)`) - the end of the measured span. With `first_slice` it bounds how recently and how long the cluster was seen; a stale `last_slice` means the cluster hasn't run lately. |
| net_dbus | Exact billed DBUs (usage_unit='DBU') attributed to this cluster (billing cluster_id) over the :lookback_days window, netted across all record_types (COALESCE 0 when no billing row matched). DBUs, NOT dollars. A high net_dbus on a mostly-idle cluster is the spend you reclaim by tightening auto-termination. |
| 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. |
idle_ratio below :warn_idle_ratio (field heuristic - tune :warn_idle_ratio for your account).
idle_ratio at/above :warn_idle_ratio (WARN) or :crit_idle_ratio (CRITICAL) with real est_wasted_usd_list - field heuristic. BEFORE downsizing, check avg_mem_pct_all: high memory + low CPU means the cluster is memory-bound, NOT idle, so shrinking it will spill.
| cluster_id | node_type | total_slices | idle_slices | avg_cpu_pct_all | peak_cpu_pct_all | worker_node_type_variants | net_dbus | est_usd_list |
|---|---|---|---|---|---|---|---|---|
| 0712-201847-abc123 | i3.xlarge | 8640 | 7820 | 3.10 | 42.50 | 1 | 4820 | 2410.00 |
| 0518-093344-def456 | Standard_DS4_v2 | 2400 | 180 | 38.70 | 96.20 | 2 | 1180 | 590.00 |
| 0630-142200-ghi789 | m5d.large | 1440 | 1410 | 1.40 | 6.80 | 1 | 690 | 345.00 |
Sort clusters by `idle_slices` ÷ `total_slices` (highest first) with a floor on `total_slices` so short-lived clusters don't dominate; for each high-idle-ratio, low-`avg_cpu_pct_all` cluster, set or shorten `auto_termination_minutes` on the matching classic cluster config (`system.compute.clusters`), then re-check after a window.
Feeds into: idle clusters by IDLE RATIO (idle minute-slices / total minute-slices) — idle-1;
One row per SQL warehouse over the trailing window, counting its scale-up and scale-down events, the observed span those events cover, and the max/average clusters running — the raw signal for detecting autoscale churn.
system.compute.warehouse_eventsOne row = one SQL warehouse's scale-up/scale-down activity over the window. The columns that matter are scaling_events (SCALED_UP + SCALED_DOWN counts) and observed_hours (the span between this warehouse's first and last event in the window) - a warehouse thrashing clusters up and down wastes cold-start time and DBUs without necessarily showing up as idle.
RequiresSELECT on system.compute, system.billing; GA
A warehouse that repeatedly scales clusters up and back down is paying a hidden tax: every scale-up incurs cluster cold-start and spin-up latency before it does useful work, and rapid up/down cycling ("thrashing") multiplies that waste while degrading query latency for users. This query surfaces the warehouses doing the most scaling so you can widen the gap between `min_clusters` and `max_clusters`, raise `min_clusters` to avoid constant cold starts, or reconsider auto-scaling on spiky workloads. It is a behavioral signal only — `warehouse_events` carries no cost — but churn maps directly to compute you are provisioning and paying for elsewhere.
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.usage_metadata.warehouse_id AS warehouse_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.warehouse_id IS NOT NULL
AND u.usage_date >= current_date() - INTERVAL :period_days DAYS
AND u.usage_date < current_date()
GROUP BY u.usage_metadata.warehouse_id
)
SELECT
we.warehouse_id,
SUM(CASE WHEN event_type = 'SCALED_UP' THEN 1 ELSE 0 END) AS scaled_up_events,
SUM(CASE WHEN event_type = 'SCALED_DOWN' THEN 1 ELSE 0 END) AS scaled_down_events,
SUM(CASE WHEN event_type IN ('SCALED_UP','SCALED_DOWN') THEN 1 ELSE 0 END) AS scaling_events,
COUNT(*) AS total_events,
MIN(event_time) AS first_event_time,
MAX(event_time) AS last_event_time,
-- observed span in hours (first..last event in-window); the driver rate below is scaling_events / this.
(unix_timestamp(MAX(event_time)) - unix_timestamp(MIN(event_time))) / 3600.0 AS observed_hours,
MAX(cluster_count) AS max_cluster_count,
AVG(cluster_count) AS avg_cluster_count,
COALESCE(cr.net_dbus, 0) AS net_dbus,
COALESCE(cr.est_usd_list, 0) AS est_usd_list,
-- status: worst-first band on scaling events per observed hour (field heuristic; :warn_churn_per_hour / :crit_churn_per_hour).
CASE
WHEN (unix_timestamp(MAX(event_time)) - unix_timestamp(MIN(event_time))) / 3600.0 < :min_observed_hours THEN 'NOT_ASSESSED'
WHEN SUM(CASE WHEN event_type IN ('SCALED_UP','SCALED_DOWN') THEN 1 ELSE 0 END)
/ ((unix_timestamp(MAX(event_time)) - unix_timestamp(MIN(event_time))) / 3600.0) >= :crit_churn_per_hour THEN 'CRITICAL'
WHEN SUM(CASE WHEN event_type IN ('SCALED_UP','SCALED_DOWN') THEN 1 ELSE 0 END)
/ ((unix_timestamp(MAX(event_time)) - unix_timestamp(MIN(event_time))) / 3600.0) >= :warn_churn_per_hour THEN 'WARN'
ELSE 'OK'
END AS status
FROM system.compute.warehouse_events we
LEFT JOIN cost_rollup cr
ON we.warehouse_id = cr.warehouse_id
WHERE event_time >= current_timestamp() - INTERVAL :period_days DAYS
GROUP BY we.warehouse_id, cr.net_dbus, cr.est_usd_list
ORDER BY CASE status WHEN 'CRITICAL' THEN 0 WHEN 'WARN' THEN 1 WHEN 'OK' THEN 2 ELSE 3 END, scaling_events DESC:period_daysdefault 30rolling window in days:min_observed_hoursdefault 1minimum hours between a warehouse's first and last event in the window before a churn rate is reported (guards divide-by-zero and short-span spikes):warn_churn_per_hourdefault 4scaling events per observed hour that flags WARN:crit_churn_per_hourdefault 10scaling events per observed hour that flags CRITICAL:period_days window, one row each.net_dbus) and a list-price USD estimate (est_usd_list) per warehouse, 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 |
|---|---|
| warehouse_id | The SQL warehouse identifier (UUID). Join to system.compute.warehouses to recover its name, size, and min/max_clusters config. |
| scaled_up_events | Count of SCALED_UP events in the window — how many times this warehouse added clusters. High values signal repeated cold-start spin-ups. |
| scaled_down_events | Count of SCALED_DOWN events — how many times it shed clusters. Roughly balanced up/down counts on a short span indicate thrashing. |
| scaling_events | scaled_up_events + scaled_down_events — total autoscale activity. This is the numerator of the churn rate. |
| total_events | Count of ALL warehouse events (including STARTING/RUNNING/STOPPING/STOPPED, not just scaling). Context for how much of the activity was autoscaling versus start/stop lifecycle. |
| first_event_time | Timestamp of the earliest event seen for this warehouse within the window. |
| last_event_time | Timestamp of the latest event seen. Together with first_event_time it bounds the observed span. |
| observed_hours | Hours between first and last event (last minus first, in hours). This is the warehouse's own active span, NOT the full window, so a warehouse seen only briefly is not diluted. A single-event warehouse yields ~0 here — divide scaling_events by this only when the span is meaningfully non-zero, or the rate is spurious. |
| max_cluster_count | The most clusters running at any single event — the peak fan-out of this warehouse's autoscaling. |
| avg_cluster_count | Mean clusters running across this warehouse's events — a rough sense of typical width; well below max_cluster_count suggests brief spikes. |
| net_dbus | Exact billed DBUs (usage_unit='DBU') attributed to this warehouse (billing warehouse_id) over the :period_days 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. |
status = OK - field heuristic; tune :warn_churn_per_hour / :crit_churn_per_hour for your account.
status = WARN or CRITICAL (scaling_events / observed_hours at or above the threshold) - field heuristic. status = NOT_ASSESSED means observed_hours is below :min_observed_hours, i.e. too little history to rate, not a clean bill of health.
| warehouse_id | scaled_up_events | scaled_down_events | scaling_events | total_events | observed_hours | max_cluster_count | avg_cluster_count | net_dbus | est_usd_list |
|---|---|---|---|---|---|---|---|---|---|
| a1b2c3d4-5e6f-7890-abcd-ef1234567890 | 48 | 46 | 94 | 112 | 72.5 | 6 | 2.3 | 3120 | 1560.00 |
| b2c3d4e5-6f70-8901-bcde-f01234567891 | 3 | 2 | 5 | 14 | 40.0 | 2 | 1.4 | 240 | 120.00 |
| c3d4e5f6-7081-9012-cdef-012345678902 | 1 | 0 | 1 | 1 | 0.0 | 1 | 1.0 | 60 | 30.00 |
Sort by scaling_events (or scaling_events ÷ observed_hours where observed_hours is non-trivial) to find the top-churning warehouses, then open each in system.compute.warehouses: raise min_clusters to keep warm capacity, widen the min/max gap, or lengthen auto_stop_minutes so warehouses stop thrashing clusters up and down on spiky query load.
Feeds into: autoscaling churn (SCALED_UP/SCALED_DOWN per hour) — autoscale-1; warehouses thrashing
A per-workspace, per-node-type, per-availability/state/event-type roll-up of cloud-VM lifecycle events for classic compute, showing how many events and distinct instances occurred and the first/last time each combination was seen over the trailing window.
system.compute.instance_eventsOne row = a workspace + node_type + availability_type + instance state + event_type combination and how many events/instances hit it over the window. The columns that matter are state (INSTANCE_READY = idle, INSTANCE_PLACED = in use) and instance_count - a node_type with a large INSTANCE_READY instance_count relative to its INSTANCE_PLACED instance_count is spending a lot of launches sitting idle before being used, but this query counts events, not idle minutes, so treat it as a screening signal, not a precise duration.
RequiresSELECT on system.compute; Public Preview (system.compute.instance_events may be empty or disabled per workspace)
This is your first account-level read on how much classic compute is running on cheap interruptible capacity versus full-price on-demand VMs: a low spot share on batch/jobs fleets is money left on the table, and a heavy spot share on latency-sensitive work is a reliability risk. It also gives an early, coarse signal of instances sitting `INSTANCE_READY` (booted but idle) rather than `INSTANCE_PLACED` (attached to a cluster and working), which points at auto-termination and pool-sizing waste. Because the table carries no DBUs or dollars, these are directional signals that only become spend after a billing-domain join. Treat an empty result as "preview not populated," not as "zero spot / zero idle."
SELECT workspace_id, node_type, availability_type, state, event_type,
COUNT(*) AS event_count,
COUNT(DISTINCT instance_id) AS instance_count,
MIN(event_time) AS first_event_time, MAX(event_time) AS last_event_time
FROM system.compute.instance_events
WHERE event_time >= current_timestamp() - INTERVAL :period_days DAYS
GROUP BY workspace_id, node_type, availability_type, state, event_type
ORDER BY event_count DESC, instance_count DESC:period_daysdefault 30rolling window in days:period_days window, broken out by workspace, node type, on-demand-vs-spot availability_type, VM state, and event_type.INSTANCE_READY (booted, idle) against INSTANCE_PLACED (attached and in use) event volumes.| Column | How to read it |
|---|---|
| workspace_id | The workspace the instance events belong to. Use to attribute spot mix and idle signal to a specific workspace. |
| node_type | Cloud instance/node type (e.g. `m5.xlarge`). The right-sizing and cost dimension — pair with the node_types reference for hardware specs. |
| availability_type | Capacity class: `ON_DEMAND` vs `SPOT` (AWS/Azure) or `PREEMPTIBLE` (GCP). The spot-vs-on-demand cost-mix lens. |
| state | VM lifecycle state: `INSTANCE_LAUNCHING`, `INSTANCE_READY` (booted but idle), `INSTANCE_PLACED` (attached to a cluster, in use), or `INSTANCE_TERMINATED`. READY-heavy vs PLACED-heavy is the coarse idle-vs-active read. |
| event_type | Kind of event: `INSTANCE_LAUNCHING` or `STATE_TRANSITION`. Distinguishes an initial launch from a subsequent state change. |
| event_count | Number of events in this workspace/node-type/availability/state/event-type group over the window (`COUNT(*)`). A volume count, not a duration. |
| instance_count | Number of distinct instances (`COUNT(DISTINCT instance_id)`) contributing to this group — how many actual VMs, versus raw event churn. |
| first_event_time | Earliest `event_time` (`MIN`) seen for this combination in the window. Bounds when the pattern started appearing. |
| last_event_time | Most recent `event_time` (`MAX`) for this combination. Recency check — a stale last event means the pattern is no longer active. |
Inventory / reference query — it returns state to read, not a pass/fail band.
| workspace_id | node_type | availability_type | state | event_type | event_count | instance_count | first_event_time | last_event_time |
|---|---|---|---|---|---|---|---|---|
| 1234567890123456 | m5.xlarge | SPOT | INSTANCE_PLACED | STATE_TRANSITION | 842 | 57 | 2026-06-06 03:12:55 | 2026-07-05 22:41:03 |
| 1234567890123456 | m5.xlarge | ON_DEMAND | INSTANCE_READY | STATE_TRANSITION | 119 | 18 | 2026-06-07 11:45:10 | 2026-07-05 20:12:47 |
| 9876543210987654 | r5.2xlarge | ON_DEMAND | INSTANCE_LAUNCHING | INSTANCE_LAUNCHING | 64 | 64 | 2026-06-10 14:22:08 | 2026-07-04 09:03:15 |
Rank node types and workspaces by their on-demand share where the workload is interruptible-tolerant (batch/jobs) to shift them onto spot, and flag node types with high `INSTANCE_READY` event/instance counts as auto-termination and pool-sizing candidates — then confirm the dollar impact with a billing-domain join before acting. If the result is empty, verify the preview table is enabled rather than assuming zero spot or zero idle.
Feeds into: spot-vs-on-demand cost mix (availability_type); instance idle-vs-active minutes
One row per live instance pool showing its always-on idle floor, capacity ceiling, idle auto-termination, preloaded images and node type — the pre-warmed-VM waste and right-sizing baseline.
system.compute.instance_poolsOne row = the latest known configuration for one instance pool that has not been deleted, plus its compute spend over :period_days. The column that matters is min_idle_instances - the number of instances this pool keeps warm and idle at all times, which bills regardless of whether anything is using them.
RequiresSELECT on system.compute, system.billing; Public Preview (system.compute.instance_pools may be empty or disabled per workspace)
Every instance pool holds `min_idle_instances` VMs pre-warmed and always running so clusters attach fast — those idle VMs cost cloud-infra money 24/7 whether or not any job ever borrows them, so a pool with a high idle floor and a low idle-autotermination is a standing bill for capacity nobody is using. `max_capacity` reveals pools sized far above real demand, and preloaded Docker images widen the attack/supply-chain surface and slow warm-up. This is the shortlist for trimming idle floors, tightening idle auto-termination, and catching untagged (unchargeable) pools before they quietly accrue spend.
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.instance_pool_id AS instance_pool_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.instance_pool_id IS NOT NULL
AND u.usage_date >= date_add(current_date(), -CAST(:period_days AS INT))
AND u.usage_date < current_date()
GROUP BY u.workspace_id, u.usage_metadata.instance_pool_id
)
SELECT p.instance_pool_id,
CASE WHEN p.instance_pool_name IS NULL THEN p.instance_pool_name ELSE concat(substr(p.instance_pool_name, 1, 2), '****') END AS instance_pool_name,
p.node_type, p.min_idle_instances, p.max_capacity,
p.idle_instance_autotermination_minutes, p.enable_elastic_disk, p.preloaded_spark_version,
p.preloaded_docker_images, p.tags, p.aws_attributes, p.azure_attributes, p.gcp_attributes, p.disk_spec,
p.create_time, p.delete_time, p.change_time, p.workspace_id, p.account_id,
COALESCE(cr.net_dbus, 0) AS net_dbus,
COALESCE(cr.est_usd_list, 0) AS est_usd_list,
-- status: worst-first band on the standing idle-instance floor (field heuristic; :warn_min_idle_instances / :crit_min_idle_instances).
CASE
WHEN p.min_idle_instances IS NULL THEN 'NOT_ASSESSED'
WHEN p.min_idle_instances >= :crit_min_idle_instances THEN 'CRITICAL'
WHEN p.min_idle_instances >= :warn_min_idle_instances THEN 'WARN'
ELSE 'OK'
END AS status
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY instance_pool_id ORDER BY change_time DESC) AS rn
FROM system.compute.instance_pools
) p
LEFT JOIN cost_rollup cr
ON cr.workspace_id = p.workspace_id
AND cr.instance_pool_id = p.instance_pool_id
WHERE p.rn = 1 AND p.delete_time IS NULL
ORDER BY p.min_idle_instances DESC:period_daysdefault 30rolling window in days for the cost rollup (this is a config snapshot with no time window of its own):warn_min_idle_instancesdefault 5standing idle-instance floor that flags WARN:crit_min_idle_instancesdefault 20standing idle-instance floor that flags CRITICALmin_idle_instances) and the capacity ceiling (max_capacity), alongside how many idle minutes before a spare VM is released.preloaded_spark_version, preloaded_docker_images, elastic-disk setting, and the tags map for chargeback.net_dbus) and a list-price USD estimate (est_usd_list) per pool, 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 |
|---|---|
| instance_pool_id | Stable unique identifier of the pool; the join key to clusters' driver/worker_instance_pool_id and to billing usage in other domains. |
| instance_pool_name | Human label, masked to its first 2 characters plus `****` (NULL stays NULL); for recognition only, not a lookup key. |
| node_type | The cloud instance/VM type this pool pre-warms; every idle instance and every borrowing cluster runs on this SKU. |
| min_idle_instances | The always-on idle floor — this many VMs stay running at all times. The primary waste signal; higher = more standing cost regardless of use. |
| max_capacity | The ceiling on total (idle + used) instances the pool may hold; compare against real demand to spot pools sized far too large. |
| idle_instance_autotermination_minutes | Minutes a spare VM sits idle before the pool releases it; low tightens waste, 0/high keeps idle VMs alive longer. |
| enable_elastic_disk | Whether pooled VMs auto-expand local disk under spill; a configuration/cost attribute, not a waste metric. |
| preloaded_spark_version | Databricks runtime pre-baked into idle VMs for fast attach; a stale value here can pin clusters to an aging/EOL runtime. |
| preloaded_docker_images | Container images pre-pulled onto idle VMs; non-empty = Docker/preload supply-chain surface and slower warm-up to review. |
| tags | Key/value map for chargeback and ownership; an empty/absent map means this pool's spend can't be attributed. |
| aws_attributes | AWS-specific config struct (selected whole); populated only on AWS, holds spot/availability settings — subfields are example-documented only. |
| azure_attributes | Azure-specific config struct (selected whole); populated only on Azure. |
| gcp_attributes | GCP-specific config struct (selected whole); populated only on GCP. |
| disk_spec | Struct describing attached-disk type/size/count for pooled VMs (selected whole); subfield extraction is unverified. |
| create_time | When the pool was first created. |
| delete_time | Deletion timestamp — always NULL in this result because the query keeps only live (non-deleted) pools. |
| change_time | Timestamp of this (latest) configuration revision; how the newest row per pool was chosen. |
| workspace_id | Workspace the pool belongs to; the scope key when correlating with workspace-scoped cluster or billing rows. |
| account_id | Databricks account the pool belongs to. |
| net_dbus | Exact billed DBUs (usage_unit='DBU') attributed to this instance pool (workspace_id + instance_pool_id) over the :period_days window, netted across all record_types (COALESCE 0 when no billing row matched). DBUs, NOT dollars. This is the pool’s whole compute spend (clusters billed via the pool, tagged instance_pool_id), NOT idle-only waste. |
| 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. |
status = OK (min_idle_instances below :warn_min_idle_instances) - field heuristic; tune for your account's pool sizes.
status = WARN or CRITICAL (min_idle_instances at or above :warn_min_idle_instances / :crit_min_idle_instances) - field heuristic. Also worth a look regardless of status: idle_instance_autotermination_minutes NULL or 0, which means instances above the floor never get reclaimed.
| instance_pool_id | instance_pool_name | node_type | min_idle_instances | max_capacity | idle_instance_autotermination_minutes | preloaded_docker_images | tags | workspace_id | net_dbus | est_usd_list |
|---|---|---|---|---|---|---|---|---|---|---|
| 0713-abc123-pool-91a2 | et**** | i3.xlarge | 5 | 20 | 60 | [] | {"team":"etl","cost_center":"data-eng"} | 1234567890123456 | 1450 | 725.00 |
| 0713-def456-pool-77c4 | ml**** | g4dn.xlarge | 0 | 8 | 15 | [{"url":"reg.internal/ml-base:2.1"}] | {} | 1234567890123456 | 320 | 160.00 |
| 0620-ghi789-pool-04f8 | st**** | Standard_DS3_v2 | 2 | 10 | 30 | [] | {"env":"staging"} | 9876543210987654 | 610 | 305.00 |
Sort by `min_idle_instances` (highest first) to find pools burning always-on idle VMs; for each, cut the idle floor toward zero and/or lower `idle_instance_autotermination_minutes`, right-size `max_capacity` to real demand, review any non-empty `preloaded_docker_images`, and tag untagged pools — then quantify the savings by joining `node_type` to node cost in the billing domain.
Feeds into: instance-pool idle waste (min_idle_instances × node cost); pool right-sizing
One row per cluster / node-type / driver-vs-worker role summarizing observed CPU, memory, CPU-wait and network from per-minute classic-compute hardware telemetry over a trailing window (≤90 days).
system.compute.node_timelineOne row = one cluster + node_type + driver/worker role, with average and peak CPU and memory over the window. avg_cpu_pct and avg_mem_pct are the columns that matter: both low over many slices means the node is oversized for its workload. This is a right-sizing profile; compute_idle_node_ratio is the dedicated idle finding.
RequiresSELECT on system.compute; GA
This is the hard evidence that turns a right-sizing hunch into a defensible action: a cluster whose CPU and memory sit chronically low across thousands of observed minutes is genuinely oversized and can drop to a smaller node type or a tighter autoscale range. High CPU-wait flags I/O-bound work that a bigger CPU will not fix (so you avoid upsizing waste), and the network totals are a coarse egress proxy worth watching for cross-region transfer cost. None of these numbers are DBUs or dollars, so savings are realized only after you feed the right-sizing decision back into cluster config and re-price against billing.
SELECT cluster_id, node_type, driver,
COUNT(*) AS minute_rows,
MIN(start_time) AS first_minute, MAX(end_time) AS last_minute,
AVG(cpu_user_percent + cpu_system_percent) AS avg_cpu_pct,
MAX(cpu_user_percent + cpu_system_percent) AS peak_cpu_pct,
AVG(mem_used_percent) AS avg_mem_pct, MAX(mem_used_percent) AS peak_mem_pct,
AVG(cpu_wait_percent) AS avg_cpu_wait_pct,
SUM(network_sent_bytes) AS total_network_sent_bytes,
SUM(network_received_bytes) AS total_network_received_bytes,
-- status: oversized-candidate band (field heuristic). Too few slices -> NOT_ASSESSED.
CASE
WHEN COUNT(*) < :min_slices THEN 'NOT_ASSESSED'
WHEN AVG(cpu_user_percent + cpu_system_percent) < :oversized_cpu_pct
AND AVG(mem_used_percent) < :oversized_mem_pct THEN 'WARN'
ELSE 'OK'
END AS status
FROM system.compute.node_timeline
WHERE start_time >= dateadd(DAY, -LEAST(:period_days, 90), current_date())
GROUP BY cluster_id, node_type, driver
ORDER BY avg_cpu_pct ASC
LIMIT :top_n:period_daysdefault 30rolling window in days, capped at 90 in-SQL by node_timeline retention:min_slicesdefault 60minimum node-minutes before a cluster is judged (fewer -> NOT_ASSESSED):oversized_cpu_pctdefault 20avg CPU percent below which a cluster looks oversized:oversized_mem_pctdefault 30avg memory percent below which a cluster looks oversized:top_ndefault 200row cap| Column | How to read it |
|---|---|
| cluster_id | The classic cluster the telemetry belongs to. Not masked — join to the config snapshot (system.compute.clusters) to recover the (masked) cluster name and owner. |
| node_type | The instance/node type observed. Join to `system.compute.node_types` for the vCPU/memory denominator behind the utilization percentages. |
| driver | Boolean: true = the driver node's rows, false = worker rows. Driver and workers are summarized separately because their utilization profiles differ. |
| minute_rows | Count of node-minute slices behind this row — the evidence weight. A raw row is one minute, not one hour; low counts mean a thin sample (and sub-10-minute nodes may not appear at all). |
| first_minute | Earliest observed minute-slice start (MIN(start_time)) in this grouping — the beginning of the measured span. |
| last_minute | Latest observed minute-slice end (MAX(end_time)) in this grouping — the end of the measured span. Combined with first_minute it bounds how recent and how long the observation is. |
| avg_cpu_pct | Average CPU busy percent (user + system) across all minutes. Chronically low (e.g. single digits) is the primary oversized-cluster signal. |
| peak_cpu_pct | Maximum CPU busy percent (user + system) seen in any single minute. A low peak confirms headroom to downsize; a high peak warns that the node genuinely needs the capacity at times. |
| avg_mem_pct | Average memory-used percent (0-100). Note it includes background processes, so it never reads truly zero even on an idle node. |
| peak_mem_pct | Maximum memory-used percent in any minute. A peak near 100 warns against downsizing to a smaller-memory node even if the average looks low. |
| avg_cpu_wait_pct | Average percent of time the CPU waited on I/O. High values mean the workload is I/O-bound — a faster/bigger CPU will not help, so treat it separately from CPU-bound sizing. |
| total_network_sent_bytes | Sum of bytes sent across all minutes in this grouping — a coarse egress proxy, in bytes (not dollars). Useful for spotting heavy cross-region/data-transfer clusters. |
| total_network_received_bytes | Sum of bytes received across all minutes in this grouping, in bytes. Read alongside sent bytes for total network movement. |
avg_cpu_pct at/above :oversized_cpu_pct OR avg_mem_pct at/above :oversized_mem_pct - the box is being used (field heuristic).
avg_cpu_pct below :oversized_cpu_pct AND avg_mem_pct below :oversized_mem_pct across enough slices (WARN = oversized candidate) - field heuristic; peak_* matters too, a low average with a high peak may just be bursty.
| cluster_id | node_type | driver | minute_rows | avg_cpu_pct | peak_cpu_pct | avg_mem_pct | total_network_sent_bytes |
|---|---|---|---|---|---|---|---|
| 0714-201530-abc123 | m5d.2xlarge | true | 4320 | 6.4 | 22.1 | 41.8 | 3221225472 |
| 0714-201530-abc123 | m5d.2xlarge | false | 51840 | 8.9 | 74.3 | 55.2 | 48318382080 |
| 0902-118844-xyz789 | r5.4xlarge | false | 2100 | 71.5 | 98.2 | 83.6 | 10737418240 |
Rank clusters by low avg_cpu_pct and avg_mem_pct with high minute_rows (well-evidenced), confirm peak_cpu_pct / peak_mem_pct also leave headroom, then downsize the node type or tighten the autoscale range on those clusters; treat high avg_cpu_wait_pct rows as I/O-bound (do not upsize CPU) and route persistently idle clusters to the auto-stop candidate review.
Feeds into: oversized/right-sizing (CPU/mem utilization vs provisioned capacity);
A static reference list of every node/instance type available in the account with its vCPU, memory, and GPU specs — the hardware denominator for right-sizing math.
system.compute.node_typesOne row = one node type's fixed specs (vCPU, memory, GPU count). Join node_type to classic_clusters_config_current.driver_node_type/worker_node_type or to node_timeline_utilization.node_type to turn a node type name into actual capacity.
RequiresSELECT on system.compute; GA
Right-sizing and idle findings are meaningless without a capacity baseline: knowing a cluster's CPU sat at 8% only becomes a dollar decision once you know how many vCPUs and how much memory that node type actually provides. This table is that denominator — it lets an admin translate "chronically low utilization" on a specific node type into "downsize from a 96-core box to a 16-core box." It also surfaces which node types carry GPUs (gpu_count), the most expensive capacity to leave idle. It carries no DBUs or dollars itself; it is a lookup that makes the behavioral tables interpretable.
SELECT node_type, core_count, memory_mb, gpu_count, account_id
FROM system.compute.node_types
ORDER BY node_typeclusters (driver/worker node type) and node_timeline (per-minute utilization) to turn raw utilization into right-sizing decisions.gpu_count so GPU-bearing types can be identified and their idle-GPU spend prioritized.| Column | How to read it |
|---|---|
| node_type | The cloud instance-type identifier (e.g. an AWS/Azure/GCP SKU name). This is the join key to clusters.driver_node_type / worker_node_type and node_timeline.node_type — match on it to attach specs to actual running compute. |
| core_count | vCPU cores per single node of this type. Stored as a double. Multiply by node/worker count to get a cluster's total cores for right-sizing math. |
| memory_mb | RAM per single node in megabytes (a long, NOT bytes and NOT GB — divide by 1024 for GB). Per-node, so scale by node count for cluster totals. |
| gpu_count | GPUs attached to one node of this type (long). 0 for CPU-only types; >0 flags GPU capacity, the priciest to leave idle. |
| account_id | The Databricks account UUID the catalog belongs to. Constant within an account; useful only when unioning exports across accounts. |
Inventory / reference query — it returns state to read, not a pass/fail band.
| node_type | core_count | memory_mb | gpu_count | account_id |
|---|---|---|---|---|
| m5d.2xlarge | 8.0 | 32768 | 0 | e2c1a4b8-1f2d-4c3e-9a7b-0d5f6e8c9a1b |
| Standard_NC24ads_A100_v4 | 24.0 | 229376 | 1 | e2c1a4b8-1f2d-4c3e-9a7b-0d5f6e8c9a1b |
| r5d.24xlarge | 96.0 | 786432 | 0 | e2c1a4b8-1f2d-4c3e-9a7b-0d5f6e8c9a1b |
Load this as a lookup table and join it to classic_clusters_config_current (on driver/worker node type) and node_timeline_utilization (on node_type): where a cluster's average CPU and memory usage sits far below the node's core_count / memory_mb over the window, downsize it to a smaller node type; prioritize any type with gpu_count > 0 that shows low utilization, since idle GPUs are the costliest to keep running.
Feeds into: oversized/right-sizing (vCPU/memory/GPU-per-node-type denominator)
One row per live (non-deleted) SQL warehouse showing its latest configuration — type, channel, size, autoscale range, idle auto-stop, and chargeback tags.
system.compute.warehousesOne row = the latest known configuration for one SQL warehouse that has not been deleted. The columns that matter are warehouse_size, min_clusters/max_clusters (autoscaling bounds), and auto_stop_minutes (how long the warehouse waits before suspending).
RequiresSELECT on system.compute; GA
SQL warehouses bill continuously while RUNNING, so an oversized `warehouse_size` or a high `auto_stop_minutes` (or none at all) quietly burns compute during idle windows between queries. This snapshot is the right-sizing and hygiene baseline: it surfaces warehouses that are too big for their workload, warehouses that never auto-suspend, and warehouses with empty `tags` that can't be charged back to a team or cost center. Fixing these is a direct, recurring compute saving — but note this table carries no DBUs or dollars, so the savings must be quantified later against the billing domain.
SELECT warehouse_id, CASE WHEN warehouse_name IS NULL THEN warehouse_name ELSE concat(substr(warehouse_name, 1, 2), '****') END AS warehouse_name, workspace_id, account_id, warehouse_type, warehouse_channel,
warehouse_size, min_clusters, max_clusters, auto_stop_minutes, tags, change_time, delete_time
FROM (
SELECT warehouse_id, warehouse_name, workspace_id, account_id, warehouse_type, warehouse_channel,
warehouse_size, min_clusters, max_clusters, auto_stop_minutes, tags, change_time, delete_time,
ROW_NUMBER() OVER (PARTITION BY warehouse_id ORDER BY change_time DESC) AS rn
FROM system.compute.warehouses
)
WHERE rn = 1 AND delete_time IS NULL
ORDER BY warehouse_idCLASSIC/PRO/SERVERLESS), release channel, and size (XS … 4X_LARGE, plus Beta 5X_LARGE) so you can spot oversized or off-standard warehouses.min_clusters/max_clusters) and the idle auto-stop setting so never-suspending or over-provisioned warehouses stand out.tags map so untagged warehouses are visible for cost allocation.| Column | How to read it |
|---|---|
| warehouse_id | Stable unique identifier of the SQL warehouse; use it to join to warehouse_events or billing usage (workspace-scoped). |
| warehouse_name | Display name, masked in-query to the first 2 characters plus `****` (e.g. `An****`); NULL passes through as NULL, so it is a label hint, not the full name. |
| workspace_id | The workspace that owns the warehouse; the correct grain for scoping and for joining warehouse-level keys. |
| account_id | The Databricks account the warehouse belongs to. |
| warehouse_type | Compute tier: `CLASSIC`, `PRO`, or `SERVERLESS` — drives pricing model and which features/sizes apply. |
| warehouse_channel | Release channel (e.g. current vs preview) the warehouse runs on. |
| warehouse_size | T-shirt size from `XS` to `4X_LARGE` (plus Beta `5X_LARGE` on PRO/SERVERLESS); the primary oversizing signal. |
| min_clusters | Lower bound of the autoscaling cluster range — the always-on floor of clusters when running. |
| max_clusters | Upper bound of the autoscaling cluster range — the ceiling the warehouse can scale out to under load. |
| auto_stop_minutes | Idle minutes before the warehouse suspends; a high value (or effectively disabled) means a long idle-billing tail. |
| tags | Map of chargeback/governance tags; an empty map means the warehouse is untagged and cannot be attributed to a team or cost center. |
| change_time | Timestamp of this (latest) configuration change — how current the snapshot is for this warehouse. |
| delete_time | Deletion timestamp; always NULL here because the query keeps only live warehouses. |
Inventory / reference query — it returns state to read, not a pass/fail band.
| warehouse_id | warehouse_name | warehouse_type | warehouse_size | min_clusters | max_clusters | auto_stop_minutes |
|---|---|---|---|---|---|---|
| a1b2c3d4e5f60718 | An**** | PRO | LARGE | 1 | 4 | 10 |
| f9e8d7c6b5a41320 | Fi**** | SERVERLESS | X_LARGE | 1 | 1 | 120 |
| 0011223344556677 | Le**** | CLASSIC | MEDIUM | 1 | 2 | 10 |
Sort the output by `warehouse_size` and `auto_stop_minutes` to build a shortlist of oversized or never-suspending warehouses and any with empty `tags`; downsize or lower auto-stop on the worst offenders, add chargeback tags to the untagged ones, then confirm the savings against the billing domain.
Feeds into: idle/oversized SQL warehouses; warehouse resume/suspend (config side);
One row per SQL warehouse and event type over the window, giving the event count, the first and last time that event fired, and the peak and average number of clusters running when it did.
system.compute.warehouse_eventsOne row = one warehouse_id + event_type combination and its event count/cluster-count stats over the window. The columns that matter are event_type (RUNNING/STARTING show the warehouse was actually used; SCALED_UP/SCALED_DOWN show autoscaling churn; STOPPED/STOPPING show suspend behavior) and last_event_time - for a RUNNING or STARTING row, an old last_event_time means this warehouse has gone quiet.
RequiresSELECT on system.compute; GA
A SQL warehouse bills for every second it is RUNNING, whether or not a query is on it, so a warehouse that resumes and suspends dozens of times a day, or scales its cluster count up and back repeatedly, is quietly burning spin-up time and DBUs. This view lets an admin see, per warehouse, how often it started/stopped/scaled and how many clusters it peaked at, so they can flag chronic resume/suspend churn, spot warehouses that never went RUNNING (idle or abandoned), and catch autoscaling that thrashes. It is the behavioral evidence for tuning auto_stop_minutes and max_clusters. Note it carries no DBUs or dollars itself; the churn it exposes must be dollarized against billing.
SELECT warehouse_id, event_type,
COUNT(*) AS event_count,
MAX(event_time) AS last_event_time,
MIN(event_time) AS first_event_time,
MAX(cluster_count) AS max_cluster_count,
AVG(cluster_count) AS avg_cluster_count,
-- status: only meaningful on RUNNING/STARTING rows - days since last RUNNING/STARTING event (field heuristic; :warn_stale_days / :crit_stale_days).
CASE
WHEN event_type NOT IN ('RUNNING', 'STARTING') THEN 'NOT_ASSESSED'
WHEN (unix_timestamp(current_timestamp()) - unix_timestamp(MAX(event_time))) / 86400.0 >= :crit_stale_days THEN 'CRITICAL'
WHEN (unix_timestamp(current_timestamp()) - unix_timestamp(MAX(event_time))) / 86400.0 >= :warn_stale_days THEN 'WARN'
ELSE 'OK'
END AS status
FROM system.compute.warehouse_events
WHERE event_time >= current_timestamp() - INTERVAL :period_days DAYS
GROUP BY warehouse_id, event_type
ORDER BY CASE status WHEN 'CRITICAL' THEN 0 WHEN 'WARN' THEN 1 WHEN 'OK' THEN 2 ELSE 3 END, last_event_time ASC:period_daysdefault 30rolling window in days:warn_stale_daysdefault 7days since a warehouse's last RUNNING/STARTING event that flags WARN:crit_stale_daysdefault 14days that flags CRITICALSTARTING, RUNNING, STOPPING, STOPPED, SCALED_UP, SCALED_DOWN) and counts how many times each fired in the window.RUNNING/STARTING reads as idle or abandoned.warehouse_id and event_type over a trailing window; it does not stitch events into durations or costs.| Column | How to read it |
|---|---|
| warehouse_id | The SQL warehouse the events belong to. Workspace/region-scoped identifier; pass through as-is. |
| event_type | The state transition being counted. Trust only the 6 documented values (SCALED_UP, SCALED_DOWN, STOPPING, RUNNING, STARTING, STOPPED); SCALING_UP/SCALING_DOWN are undocumented and not expected here. |
| event_count | How many times this event fired for this warehouse in the window. High STARTING/STOPPED counts = resume/suspend churn; high SCALED_UP/SCALED_DOWN = autoscale thrash. |
| last_event_time | Most recent occurrence of this event. If the newest RUNNING/STARTING is stale, the warehouse has likely been idle since. |
| first_event_time | Earliest occurrence of this event within the window — the start of the observed activity span for that event. |
| max_cluster_count | The highest number of clusters running at any occurrence of this event — the autoscaling peak. Compare to the warehouse's configured max_clusters. |
| avg_cluster_count | Average clusters running across occurrences of this event; a value well below max_cluster_count means the peak was rare. |
status = OK on the RUNNING/STARTING rows (last RUNNING or STARTING event within :warn_stale_days) - field heuristic; tune for your account's usage cadence.
status = WARN or CRITICAL on a RUNNING/STARTING row (no RUNNING or STARTING event for :warn_stale_days / :crit_stale_days) - the warehouse has likely gone dormant. status is NOT_ASSESSED on SCALED_UP/SCALED_DOWN/STOPPED/STOPPING rows - staleness on those event types is not inherently good or bad from this table alone.
| warehouse_id | event_type | event_count | last_event_time | first_event_time | max_cluster_count | avg_cluster_count |
|---|---|---|---|---|---|---|
| a1b2c3d4e5f60718 | RUNNING | 142 | 2026-07-05 18:22:41 | 2026-06-06 08:14:03 | 1 | 1.0 |
| a1b2c3d4e5f60718 | SCALED_UP | 57 | 2026-07-05 17:40:10 | 2026-06-06 09:02:55 | 4 | 2.3 |
| 9f8e7d6c5b4a3210 | STARTING | 318 | 2026-07-05 23:57:12 | 2026-06-06 00:11:47 | 1 | 1.0 |
Sort by event_count to find warehouses with the most STARTING/STOPPED cycles (resume/suspend churn) and the highest SCALED_UP counts (autoscale thrash); for churners, lower auto_stop_minutes or cap max_clusters, and for warehouses whose newest RUNNING/STARTING last_event_time is stale, investigate decommissioning. Then dollarize the worst offenders against billing.
Feeds into: resume/suspend churn; idle warehouses (no recent RUNNING/STARTING);