← Back to Blog

FinOps for Data Platforms: Real Cost Governance on Snowflake, Databricks, BigQuery, and Fabric

Most organizations understand their cloud compute and storage costs reasonably well. Data platform costs are a different animal. A single misconfigured Snowflake warehouse running on auto-resume can cost more in a weekend than a month of planned workloads. A BigQuery query without a partition filter on a 50 TB table bills $250 per run. A Databricks job that should finish in 20 minutes but hits a skew problem runs for 6 hours — on a cluster that auto-scaled to 50 nodes.

Data platform FinOps is the practice of making these costs visible, attributable, and governable — before the bill arrives. Each major platform has a different cost model with different levers, different failure modes, and different tooling. This article covers the specifics: what drives costs on each platform, what the query-level monitoring looks like, and the governance patterns that actually change behavior.

Snowflake: Credits, Warehouses, and the Idle Problem

Snowflake charges in credits, not dollars directly — but credits have a dollar price based on your contract (typically $2–4 per credit on-demand, $1.50–2 with annual commitment). One credit = one hour of a single-node XS warehouse, or 1/16 of an hour of a 4XL warehouse. The math compounds fast: an unattended XL warehouse (8 credits/hr) left running over a long weekend costs 8 × 60 hours × $3/credit = $1,440.

The Five Snowflake Cost Levers

1. Warehouse auto-suspend: Every warehouse should have AUTO_SUSPEND = 60 (or less for development). The default is 600 seconds (10 minutes idle before suspend). On a multi-tenant warehouse with bursty usage, 600 seconds of idle time per query session adds up to enormous waste.

-- Audit all warehouses missing short auto-suspend
SELECT warehouse_name, auto_suspend, warehouse_size
FROM information_schema.warehouses
WHERE auto_suspend > 120 OR auto_suspend IS NULL
ORDER BY warehouse_size DESC;

-- Fix: set aggressive auto-suspend for all non-production warehouses
ALTER WAREHOUSE analytics_dev SET AUTO_SUSPEND = 30;

-- Set query timeout to prevent runaway queries
ALTER WAREHOUSE analytics_prod SET STATEMENT_TIMEOUT_IN_SECONDS = 3600; -- 1 hour hard limit

2. Warehouse rightsizing: Snowflake warehouses double in cost with each size increase (S=1 credit/hr, M=2, L=4, XL=8, 2XL=16). Many teams run everything on XL "to be safe." In practice, most ad-hoc analyst queries run identically on M vs XL — they're limited by query complexity and data scan volume, not raw compute. Run queries on S first; only size up if the query exceeds memory or time limits.

3. Query scanning: Snowflake's micro-partition pruning means well-clustered tables scan a fraction of total data. A query on a 10 TB table that hits only 50 GB in pruned partitions costs 200x less than a full scan. Monitor bytes_scanned vs bytes_partitions_pruned ratio in QUERY_HISTORY. Poor clustering = high scan cost.

4. Credit attribution with Resource Monitors: Resource monitors cap credit consumption per warehouse per time period and alert (or suspend) when limits are hit. Assign each team their own warehouse with a monthly credit budget. This converts a shared bill into team-level accountability.

-- Create a resource monitor for a team
CREATE OR REPLACE RESOURCE MONITOR analytics_team_monitor
    WITH CREDIT_QUOTA = 500           -- 500 credits per month
    FREQUENCY = MONTHLY
    START_TIMESTAMP = IMMEDIATELY
    TRIGGERS
        ON 75 PERCENT DO NOTIFY       -- email alert at 75%
        ON 90 PERCENT DO NOTIFY
        ON 100 PERCENT DO SUSPEND;    -- hard stop at 100%

ALTER WAREHOUSE analytics_wh SET RESOURCE_MONITOR = analytics_team_monitor;

5. Query history forensics: The SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY view is your cost forensics database. Sort by credits_used_cloud_services to find expensive queries; filter by partitions_scanned / partitions_total > 0.5 to find queries not using clustering; group by user_name to find credit-heavy users.

Databricks: DBU Tiers and Cluster Lifecycle

Databricks billing combines DBU (Databricks Unit) charges with the underlying cloud VM cost. DBU rate depends on cluster tier: Jobs Compute (cheapest, no interactive use) vs All-Purpose Compute (interactive notebooks, higher DBU rate). A job that could run on Jobs Compute at $0.10/DBU costs 3x more if someone schedules it on an All-Purpose cluster at $0.30/DBU.

The Four Databricks Cost Killers

  • All-purpose clusters left running: Interactive clusters have no auto-terminate by default in some configurations. A data scientist's cluster running overnight on 10 × m5.4xlarge instances costs ~$50–80/hour in combined DBU + EC2 charges. Set cluster policies that enforce autotermination_minutes: 30 for all interactive clusters.
  • Jobs on All-Purpose instead of Jobs Compute: Always create a dedicated job cluster for scheduled pipelines. The DBU rate difference (Jobs: $0.10–0.15/DBU vs All-Purpose: $0.30–0.40/DBU) is 2–3x. On a pipeline running 8 hours/day, that's 60–70% cost reduction for zero performance change.
  • Over-provisioned clusters: Use Databricks' cluster autoscaling (min_workers: 2, max_workers: 20) instead of fixed clusters. A job that peaks at 20 workers for 10 minutes doesn't need 20 workers for its full 2-hour run.
  • Spot/preemptible underuse: Spot instances (AWS) or preemptible VMs (GCP/Azure) cost 60–80% less than on-demand. For batch jobs with checkpointing, spot instance interruptions are handled gracefully. Use spot for worker nodes; keep the driver on on-demand to prevent total job failure on interruption.
{
  "cluster_name": "etl-pipeline-prod",
  "spark_version": "14.3.x-scala2.12",
  "node_type_id": "m5d.2xlarge",
  "driver_node_type_id": "m5d.xlarge",
  "autoscale": { "min_workers": 2, "max_workers": 16 },
  "aws_attributes": {
    "availability": "SPOT_WITH_FALLBACK",
    "spot_bid_price_percent": 100,
    "first_on_demand": 1
  },
  "autotermination_minutes": 20,
  "cluster_log_conf": { "dbfs": { "destination": "dbfs:/cluster-logs" } }
}

BigQuery: Slots, On-Demand, and the Partition Filter Rule

BigQuery has two billing models: on-demand ($6.25/TB scanned) and capacity-based (slot reservations). On-demand is excellent for variable/unpredictable workloads; capacity is better for predictable high-volume workloads.

The single most impactful BigQuery cost control: always filter on partitioned columns. A 50 TB table partitioned by date, queried without a date filter, scans all 50 TB at $312.50 per run. With a date filter (WHERE event_date >= '2024-01-01'), the same query scans maybe 2 TB — $12.50. Enforce this at the table level with require_partition_filter = TRUE:

-- Enforce partition filter at table creation
CREATE TABLE analytics.events
PARTITION BY DATE(event_timestamp)
OPTIONS (require_partition_filter = TRUE)  -- queries without filter will ERROR
AS SELECT ...;

-- Find expensive queries using INFORMATION_SCHEMA
SELECT
    user_email,
    query,
    total_bytes_processed / POW(1024,4) AS tb_scanned,
    (total_bytes_processed / POW(1024,4)) * 6.25 AS estimated_cost_usd,
    creation_time
FROM `region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
  AND statement_type = 'SELECT'
ORDER BY total_bytes_processed DESC
LIMIT 50;

BigQuery slot reservations (Editions: Standard, Enterprise, Enterprise+) provide committed capacity at 25–50% discount vs on-demand for teams running sustained workloads. The break-even point: if your team spends over ~$3,000/month on BigQuery on-demand compute, reservations start saving money.

Microsoft Fabric: CU Smoothing and the Burst Trap

Fabric's F SKU capacity model is the most opaque of the four platforms for cost governance. Unlike Snowflake (credits consumed) or BigQuery (bytes scanned), Fabric capacity is a flat monthly rate for a pool of Capacity Units (CUs) — but with a "smoothing" mechanism that allows short bursts above the provisioned capacity. What this means in practice:

  • An F64 capacity provides 64 CUs continuously.
  • A short workload burst can use more than 64 CUs — Fabric smooths the overage over a 24-hour window.
  • If sustained workloads consistently exceed 64 CUs, new requests get throttled (queued) rather than auto-scaling to higher cost. You pay flat but get unpredictable latency.
  • There's no per-query billing visibility comparable to Snowflake's QUERY_HISTORY or BigQuery's INFORMATION_SCHEMA. The primary cost tool is Fabric Capacity Metrics App (a Power BI report that Fabric provides).

The Fabric cost trap: Fabric's flat monthly price feels predictable, but it creates a hidden trade-off: teams that add workloads gradually don't see a cost spike (the price is fixed), so there's no financial signal to investigate when the capacity is exhausted. Instead, queries slow down or get throttled. The governance challenge is workload isolation: make sure Power BI usage doesn't starve Spark ETL jobs, and vice versa, by assigning workloads to separate Fabric items with controlled concurrency settings.

The FinOps Governance Stack

graph TD
    subgraph Visibility["1. Visibility (Week 1)"]
        QH["Query-level cost attribution\n(QUERY_HISTORY, JOBS schema)"]
        Tag["Team/project tagging\non all warehouses & clusters"]
        Dashboard["Cost dashboard\n(weekly report to team leads)"]
    end

    subgraph Control["2. Control (Month 1)"]
        Budget["Budget limits\n(Resource Monitors, Alerts)"]
        Policy["Cluster policies\n(auto-suspend, max size)"]
        PartFilter["Partition filter enforcement\n(table DDL, CI checks)"]
    end

    subgraph Optimize["3. Optimization (Quarter 1)"]
        Commit["Committed use / reservations\n(savings 25-50%)"]
        Cluster["Cluster rightsizing\n(spot, autoscale)"]
        Cache["Result caching &\nmaterialized views"]
    end

    Visibility --> Control --> Optimize
          

FinOps for data platforms has three phases. Visibility comes first — you can't govern what you can't measure. Control follows with hard limits and policy enforcement. Optimization (reserved capacity, spot instances) comes last, after you understand the baseline spend profile.

The most effective data platform FinOps pattern I've seen in practice: weekly cost digest emails to team leads, showing their team's spend vs budget, top 5 most expensive queries, and the change from last week. No dashboards, no training required — just a weekly email. Teams that didn't know their queries were expensive start caring about query optimization within two weeks of receiving this email. Visibility changes behavior faster than any governance control.