Adrienne Vermorel
BigQuery Slots and Reservations Explained
You’ve optimized your queries, partitioned your tables, clustered your columns, but your dbt runs still crawl during peak hours. The dashboards timeout. Your stakeholders complain. What’s going on?
The culprit is often slot contention (too many queries competing for too little compute capacity). Most analytics engineers have only a vague sense of what slots actually are, let alone how to manage them.
This article explains BigQuery’s compute model from the ground up, covers the Editions pricing tiers, and provides practical strategies for managing slots in dbt workflows. By the end, you’ll understand not just what slots are, but how to architect your projects so your pipelines run reliably.
We won’t cover pricing calculations here (that’s tomorrow’s article). Today is about understanding the machinery.
What Is a BigQuery Slot?
A BigQuery slot is a virtual compute unit. Think of it as a combination of CPU, memory, and network resources bundled together. When you run a query, BigQuery assembles a team of these slots to process your request in parallel.
The more slots available, the more work BigQuery can do simultaneously. Complex queries benefit from more slots. High concurrency (many users querying at once) benefits from more slots. The catch: slots aren’t unlimited, and when demand exceeds supply, queries slow down.
How Queries Use Slots
When you submit a SQL query, BigQuery doesn’t just run it line by line. Instead, it:
- Parses your SQL and creates an execution plan
- Decomposes the plan into stages (logical units of work)
- Breaks each stage into steps that can run in parallel
- Assigns slots to execute those steps
- Shuffles intermediate results between stages
- Reassigns slots dynamically as the query progresses
This is why BigQuery can process terabytes in seconds. It’s not one machine working hard; it’s thousands of slots working together.
┌─────────────────────────────────────────────────────────────┐│ Your SQL Query │└─────────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────┐│ Execution Plan ││ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ││ │ Stage 1 │ -> │ Stage 2 │ -> │ Stage 3 │ -> │ Stage 4 │ ││ │ (Scan) │ │ (Filter)│ │ (Agg) │ │ (Sort) │ ││ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │└─────────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────┐│ Slots Execute Stages in Parallel ││ [Slot 1] [Slot 2] [Slot 3] ... [Slot N] │└─────────────────────────────────────────────────────────────┘What Happens When Slots Run Out
If a query stage needs 2,000 slots but only 1,000 are available, BigQuery doesn’t fail. Instead:
- It uses all 1,000 available slots
- The remaining work units queue up
- As slots complete their tasks, they pick up queued work
- The query finishes, just slower than it could have
This is slot contention. Your query still runs, but it takes longer because it’s waiting for resources. During peak hours (when everyone’s running reports and your dbt jobs are transforming data), contention can turn a 30-second query into a 5-minute one.
Two Ways to Get Slots
BigQuery offers two fundamental pricing models:
On-demand pricing gives you access to a shared pool of approximately 2,000 slots. You pay per terabyte of data scanned, and BigQuery dynamically allocates slots from this shared pool. It’s simple, requires no planning, and works well for unpredictable workloads.
Capacity-based pricing (via BigQuery Editions) lets you reserve your own dedicated slots. You pay for the slots themselves, regardless of how much data you scan. This gives you predictable performance and (for heavy workloads) often costs less than on-demand.
The rest of this article focuses on capacity-based pricing, because that’s where the complexity (and the optimization opportunities) live.
The Reservation Hierarchy
BigQuery’s capacity model has three layers: commitments, reservations, and assignments. Understanding this hierarchy is essential for managing slots effectively.
Commitments: Buying Capacity
A commitment is a purchase of compute capacity for a minimum duration. You’re essentially saying, “I’ll pay for X slots for at least Y time.”
Commitments are optional when using BigQuery Editions, but they provide significant discounts:
- 1-year commitment: 20% discount
- 3-year commitment: 40% discount
Without a commitment, you pay the standard pay-as-you-go rate for your edition. Commitments make sense when you have predictable, steady workloads.
Key constraints:
- Commitments are regional (slots in
uscan’t run jobs ineu) - Commitments cannot be moved between projects after creation
- Only available for Enterprise and Enterprise Plus editions
Reservations: Creating Slot Pools
A reservation is a pool of slots carved out for specific workloads. Think of it as a “bucket” that holds a portion of your committed capacity.
Why create multiple reservations? Isolation. You might create:
- A
prodreservation with 500 slots for production pipelines - A
bireservation with 300 slots for dashboard queries - A
devreservation with 100 slots for development work
Each reservation operates independently. Jobs in dev won’t steal slots from prod, so your production pipelines always have the resources they need.
Assignments: Connecting Projects to Reservations
An assignment links a Google Cloud project (or folder, or organization) to a reservation. Once assigned, all BigQuery jobs in that project use slots from that reservation.
The hierarchy matters:
- Project assignment overrides folder assignment
- Folder assignment overrides organization assignment
- No assignment causes the project to use on-demand pricing
You can also assign by job type. BigQuery supports several assignment types:
| Job Type | What It Covers |
|---|---|
| QUERY | Interactive and batch queries |
| PIPELINE | Load, export, and copy jobs |
| ML_EXTERNAL | BigQuery ML with external models |
| BACKGROUND | Materialized view refresh, etc. |
| CONTINUOUS | Continuous queries |
This means you could assign a project’s QUERY jobs to one reservation while its PIPELINE jobs use another.
The Administration Project
Best practice: create a dedicated administration project that holds all your commitments and reservations. This project doesn’t run queries or store data; it just manages capacity.
Name it something obvious like bq-yourcompany-admin. This approach:
- Centralizes billing for compute resources
- Simplifies capacity management
- Keeps your data projects clean
One critical rule: idle slots can only be shared across reservations in the same admin project. If you use multiple admin projects, their slots remain completely isolated.
flowchart LR C[/"1000 slots (1-year)"/]
C -->|"assigned to"| R1["Reservation: prod<br/>500 slots"] C -->|"assigned to"| R2["Reservation: analytics<br/>300 slots"] C -->|"assigned to"| R3["Reservation: dev<br/>200 slots"]
R1 -->|"used by"| P1([Project: dbt-prod]) R2 -->|"used by"| P2([Project: looker-prod]) R3 -->|"used by"| P3([Project: analytics-dev])BigQuery Editions Explained
In March 2023, Google replaced the legacy flat-rate pricing model with BigQuery Editions. There are now three tiers, each designed for different workload types.
Standard Edition
Standard is the entry-level tier, designed for ad-hoc queries and development work.
Key characteristics:
- Maximum 1,600 slots (hard limit, no exceptions)
- Autoscaling only (no baseline slots)
- No idle slot sharing between reservations
- No BigQuery ML
- No VPC Service Controls
- Maximum 10 reservations per admin project
- Project-level assignments only (no folder or org)
Best for: Development environments, proof-of-concept work, small teams with light query loads.
Watch out for: The 1,600 slot cap is strict. If your workload grows beyond this, you’ll need to upgrade to Enterprise.
Enterprise Edition
Enterprise is the workhorse tier, suitable for most production workloads.
Key characteristics:
- No hard slot limit (subject to quota)
- Baseline + autoscaling slots
- Idle slot sharing enabled
- BigQuery ML included
- VPC Service Controls supported
- Continuous queries supported
- Up to 200 reservations per admin project
- Project, folder, or organization assignments
- 1-year and 3-year commitments available (20%/40% discount)
Best for: Production data pipelines, BI workloads, teams running dbt at scale.
Enterprise Plus Edition
Enterprise Plus is for mission-critical workloads with strict compliance or availability requirements.
Everything in Enterprise, plus:
- Managed disaster recovery (cross-region failover)
- Customer-managed encryption keys (CMEK)
- Compliance controls via Assured Workloads (FedRAMP, CJIS, IL4, ITAR)
- 99.99% SLA (vs. 99.9% for Standard)
Best for: Regulated industries, financial services, healthcare, government workloads.
Feature Comparison
| Feature | Standard | Enterprise | Enterprise Plus | On-Demand |
|---|---|---|---|---|
| Max slots | 1,600 | Quota-based | Quota-based | ~2,000 shared |
| Baseline slots | ❌ | ✅ | ✅ | N/A |
| Autoscaling | ✅ | ✅ | ✅ | N/A |
| Idle slot sharing | ❌ | ✅ | ✅ | N/A |
| BigQuery ML | ❌ | ✅ | ✅ | ✅ |
| Commitments | ❌ | ✅ | ✅ | N/A |
| VPC-SC | ❌ | ✅ | ✅ | ✅ |
| CMEK | ❌ | ✅ | ✅ | ✅ |
| Managed disaster recovery | ❌ | ❌ | ✅ | ❌ |
On-Demand Is Still an Option
You don’t have to choose an edition. On-demand pricing remains available with feature parity matching Enterprise Plus (except disaster recovery). You pay per terabyte scanned, get access to approximately 2,000 shared slots, and don’t worry about capacity planning.
The trade-off is that performance can vary based on regional demand, and costs can spike with inefficient queries.
Many organizations use a hybrid approach: Editions for predictable production workloads, on-demand for development and ad-hoc analysis.
Baseline vs. Autoscaling Slots
Within Enterprise and Enterprise Plus editions, you configure reservations with two types of capacity: baseline and autoscaling.
Baseline Slots
Baseline slots are always allocated to your reservation. They’re guaranteed and immediately available, and you pay for them whether you use them or not.
Think of baseline as your “always-on” capacity. If your production dbt run needs 200 slots every morning at 6 AM, baseline ensures those slots are ready and waiting.
Autoscaling Slots
Autoscaling slots are allocated on-demand as your workload grows. You set a maximum, and BigQuery scales up (and down) automatically.
Key behaviors:
- Scales in multiples of 50 slots.
- Scales up nearly instantly.
- Scales down after a 60-second minimum window.
- Subject to regional capacity availability.
- Billed per second while allocated.
The 60-Second Autoscale Window
An important detail: when BigQuery scales up, it keeps those slots allocated for at least 60 seconds, even if your query finishes in 5 seconds.
Why? To prevent thrashing. Without this window, a burst of short queries would cause constant scale-up/scale-down cycles, hurting performance and complicating billing.
Example timeline:
12:00:00 - Query needs 100 slots → Scales to 10012:00:05 - Query completes → Still at 100 (window active)12:01:00 - Window expires, no new demand → Scales to 012:01:02 - New query needs 50 slots → Scales to 5012:01:03 - Query completes → Still at 50 (new window)12:02:03 - Window expires → Scales to 0This has implications for dbt, which runs many sequential queries. Each query that triggers autoscaling starts a new 60-second window.
Slot Usage Priority
When a job runs, BigQuery allocates capacity in this order:
- Baseline slots (guaranteed, use first)
- Idle slots from other reservations (if sharing enabled)
- Autoscaling slots (if baseline + idle exhausted)
- Queue (if all capacity sources exhausted)
flowchart TB Q[Query Submitted] --> B{Baseline<br/>Available?} B -->|Yes| UB[Use Baseline] B -->|No| I{Idle Slots<br/>Available?} I -->|Yes| UI[Borrow Idle] I -->|No| A{Autoscale<br/>Room?} A -->|Yes| UA[Scale Up] A -->|No| W[Queue Work]
UB --> RUN[Execute] UI --> RUN UA --> RUN W --> WAIT[Wait for Slots]When to Use Baseline
Set a baseline when:
- You have predictable, steady workloads
- Jobs are SLA-critical and can’t wait for autoscale
- You’re combining with commitments for discounts
A common pattern: analyze your slot usage over 30 days, identify your P50 (median) usage, and set that as baseline. Autoscaling handles the peaks.
Fair Scheduling: How Slots Are Distributed
When multiple queries compete for the same reservation, BigQuery uses fair scheduling to distribute slots equitably.
The Algorithm
Fair scheduling works in two steps:
- Divide slots equally among projects running jobs in the reservation
- Divide each project’s share among its jobs
This is crucial: the first division is by project, not by query count.
Example Scenario
Consider a reservation with 1,000 slots:
- Project A runs 1 complex query
- Project B runs 20 simple queries
How are slots distributed?
| Project A | Project B | |
|---|---|---|
| Slots allocated | 500 | 500 |
| Queries | 1 | 20 |
| Slots per query | 500 | 25 |
Project A’s single query gets 500 slots. Project B’s 20 queries share 500 slots (25 each).
The takeaway: project architecture matters. If you run production dbt and ad-hoc analyst queries in the same project, they compete for the same slot pool. Separate them into different projects (assigned to different reservations) and they’re isolated.
flowchart TB subgraph Reservation["Reservation: 1,000 slots"] subgraph PA["Project A: 500 slots"] QA[Query 1<br/>500 slots] end
subgraph PB["Project B: 500 slots"] QB1[Q1: 25] QB2[Q2: 25] QB3[Q3: 25] QBN["... 20 queries total ..."] end endContention Priority During Regional Constraints
When an entire region is under capacity pressure (rare, but possible), BigQuery prioritizes requests:
- Enterprise Plus & Enterprise baseline/committed slots
- Enterprise Plus autoscaled slots
- Enterprise autoscaled slots
- Standard edition and on-demand
Standard and on-demand users feel the squeeze first.
Idle Slot Sharing
One of Enterprise edition’s key features is idle slot sharing: unused slots from one reservation automatically become available to others.
How It Works
Suppose you have two reservations:
prod: 500 baseline slots, currently using 200dev: 100 baseline slots, currently using 150
The 300 idle slots in prod can be borrowed by dev, giving it access to up to 400 slots total (100 baseline + 300 borrowed).
The catch is that if prod needs its slots back, it reclaims them instantly. Jobs in dev using borrowed capacity may slow down or get queued.
Requirements for Idle Slot Sharing
Reservations can share idle slots only if they:
- Are in the same admin project
- Are in the same region
- Use the same edition
Autoscaled slots are NOT shareable; only baseline and committed slots participate in sharing.
Configuration
Each reservation has an ignore_idle_slots setting:
false(default): Participates in sharing (can borrow and lend)true: Isolated, uses only its own capacity
When to Disable Sharing
Consider disabling idle slot sharing for:
- Proof-of-concept testing: You want to see how a reservation performs with exactly its allocated capacity
- Strict workload isolation: Regulatory or security requirements
- Capacity planning: Understanding actual slot needs before optimizing
For most production use cases, leave sharing enabled. It improves overall utilization and helps non-critical workloads without hurting critical ones.
dbt and BigQuery Slots
If you’re running dbt on BigQuery, slot management requires extra consideration. dbt’s execution pattern (many sequential SQL statements) interacts with slots differently than a single complex query.
Why dbt Is Compute-Heavy
A typical dbt run might execute hundreds of models. Each model is a separate BigQuery job. Each job:
- Requests slots
- Potentially triggers autoscaling
- Holds autoscaled slots for the 60-second minimum
- Completes, releases slots, then the next model starts
For sequential execution, you might have one job running at a time, but each can trigger a new autoscale window. For parallel execution (threads > 1), multiple jobs compete for your reservation’s capacity.
Heavy dbt runs (especially full refreshes) can consume significant slot capacity.
Current dbt Limitations
As of now, dbt-bigquery uses a single project for both:
- Running queries (determines which reservation’s slots are used)
- Storing output tables
There’s no native way to say “run this model using reservation X.” GitHub issues #2918, #3708, and #1228 track requests for runtime reservation selection, but it’s not implemented yet.
Workaround: Separate Projects by Workload
The practical solution is multiple GCP projects, each assigned to an appropriate reservation.
jaffle_shop: target: prod outputs:
# High-priority production runs prod: type: bigquery method: service-account project: mycompany-dbt-prod # Assigned to 'prod' reservation (500 slots) dataset: analytics threads: 8
# Batch/backfill jobs batch: type: bigquery method: service-account project: mycompany-dbt-batch # Assigned to 'batch' reservation (200 slots) dataset: analytics threads: 4
# Development dev: type: bigquery method: service-account project: mycompany-dbt-dev # On-demand pricing dataset: dev_adrienne threads: 4Then run with the appropriate target:
# Production incremental rundbt run --target prod --select state:modified+
# Full refresh backfilldbt run --target batch --full-refresh
# Development iterationdbt run --target dev --select my_modelBest Practices for dbt + Slots
1. Use incremental models aggressively
Incremental models process only new/changed data, dramatically reducing slot consumption compared to full refreshes.
2. Right-size your threads
More threads means more concurrent jobs and higher slot demand. Match your threads setting to your reservation’s capacity. If you have 200 slots and run 16 threads of complex models, you’ll hit contention.
3. Consider on-demand for dev
Development work is unpredictable. On-demand pricing means you don’t waste reserved capacity on sporadic dbt run commands.
4. Set realistic baselines for production
If your production dbt runs at 6 AM daily and consistently uses 300 slots for 45 minutes, set baseline at 300. You’ll get guaranteed capacity when you need it.
5. Monitor slot usage by model
dbt automatically labels jobs. Query INFORMATION_SCHEMA to find your most expensive models:
SELECT (SELECT value FROM UNNEST(labels) WHERE key = 'dbt_invocation_id') AS invocation, (SELECT value FROM UNNEST(labels) WHERE key = 'dbt_model') AS model, COUNT(*) AS job_count, SUM(total_slot_ms) / 1000 / 60 AS total_slot_minutes, AVG(SAFE_DIVIDE(total_slot_ms, TIMESTAMP_DIFF(end_time, start_time, MILLISECOND))) AS avg_slotsFROM `region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECTWHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY) AND (SELECT value FROM UNNEST(labels) WHERE key = 'dbt_invocation_id') IS NOT NULLGROUP BY invocation, modelORDER BY total_slot_minutes DESCLIMIT 20;This surfaces models that consume the most slot-time, making them prime candidates for optimization.
Monitoring Your Slot Usage
Before making any capacity decisions, understand your current usage. BigQuery provides observability through INFORMATION_SCHEMA views and built-in tools.
Key INFORMATION_SCHEMA Tables
| Table | What It Shows |
|---|---|
JOBS_BY_PROJECT | Per-job metrics: slots, bytes, runtime |
JOBS_BY_ORGANIZATION | Same, but across all projects (requires org permissions) |
JOBS_TIMELINE | Second-by-second slot consumption |
RESERVATION_CHANGES_BY_PROJECT | Reservation modifications |
RESERVATIONS_TIMELINE | Reservation capacity over time |
Example Query: Average Slots Per Job
This query shows your most slot-intensive jobs over the past 7 days:
SELECT job_id, user_email, SAFE_DIVIDE(total_slot_ms, TIMESTAMP_DIFF(end_time, start_time, MILLISECOND)) AS avg_slots, TIMESTAMP_DIFF(end_time, start_time, SECOND) AS runtime_seconds, total_bytes_processed / POW(10, 9) AS gb_processed, reservation_idFROM `region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECTWHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY) AND job_type = 'QUERY' AND state = 'DONE'ORDER BY total_slot_ms DESCLIMIT 25;Hourly Slot Utilization
See how your slot usage varies throughout the day:
SELECT TIMESTAMP_TRUNC(period_start, HOUR) AS hour, SUM(period_slot_ms) / 1000 / 3600 AS slot_hoursFROM `region-us`.INFORMATION_SCHEMA.JOBS_TIMELINE_BY_PROJECTWHERE period_start > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY) AND (statement_type != 'SCRIPT' OR statement_type IS NULL)GROUP BY hourORDER BY hour;The Slot Estimator Tool
BigQuery’s built-in Slot Estimator is useful for capacity planning.
To access it:
- Go to BigQuery Console
- Click Capacity management in the left nav
- Select the Slot estimator tab
What it shows:
- Slot utilization over the past 30 days
- Peak usage periods
- Job latency percentiles (P90, P95, etc.)
- Performance impact modeling (what if you added 200 slots?)
- Cost-optimal recommendations
Use the estimator before purchasing commitments or adjusting baselines. It’s based on your actual historical usage.
Cloud Monitoring Integration
For ongoing observability, BigQuery exposes metrics to Cloud Monitoring:
bigquery.googleapis.com/slots/allocatedbigquery.googleapis.com/slots/availablebigquery.googleapis.com/job/num_in_flight
You can build dashboards showing real-time slot usage and set up alerts for slot contention.
Common Misconceptions
Several aspects of BigQuery slots are frequently misunderstood.
”More slots always means faster queries”
Not necessarily. Simple queries that scan small amounts of data may not benefit from additional parallelism. A query scanning 1 GB might run in 2 seconds with either 100 slots or 500 slots. The overhead of coordination can outweigh the parallelism benefits.
Before adding slots, optimize your SQL. Partitioning, clustering, and avoiding SELECT * often deliver bigger wins than raw compute.
”Reservations guarantee capacity”
Partially true. Baseline slots are guaranteed (they’re always allocated to your reservation). But autoscaling slots are best-effort, subject to regional capacity. During rare regional constraints, autoscale requests might be delayed.
If you need guaranteed capacity, put it in your baseline.
”Idle slots are free capacity”
Idle slot sharing is powerful, but borrowed slots can be reclaimed instantly. A job running on borrowed idle capacity might slow down mid-execution if the owning reservation needs its slots back.
Don’t rely on idle slots for SLA-critical workloads. Use them for dev, batch processing, or workloads that can tolerate variable performance.
”Standard Edition is cheapest”
Not always. Standard Edition doesn’t support commitments, so you can’t get the 20-40% discount. For steady, predictable workloads, Enterprise Edition with a 1-year commitment often costs less than Standard’s pay-as-you-go rate.
Also, Standard’s 1,600 slot cap can force you into a more expensive tier if your workload grows.
”1,000 slots = 1,000 concurrent queries”
Slots are shared across all jobs in a reservation via fair scheduling. 1,000 slots might run 1 query at 1,000 slots, or 100 queries at 10 slots each, or any combination in between.
The actual concurrency depends on query complexity, slot availability, and fair scheduling dynamics.
Key Takeaways
- Slots are virtual compute units (CPU + memory + network) that process your queries in parallel
- Reservations create isolated pools of slots for different workloads
- Assignments link projects to reservations, determining which slots they use
- Editions (Standard, Enterprise, Enterprise Plus) offer different feature sets and capacity limits
- Baseline slots provide guaranteed capacity; autoscaling provides flexibility
- Fair scheduling divides slots equally among projects first, then among jobs within each project
- Idle slot sharing lets reservations borrow unused capacity (but it’s preemptible)
- dbt users should use separate projects for different workload priorities (native reservation selection is not yet available)
- Monitor before optimizing: Use INFORMATION_SCHEMA and the Slot Estimator to understand your actual usage
What’s Next
Tomorrow’s article, “On-Demand vs. Editions Pricing: When to Switch”, covers the cost math. We’ll walk through actual pricing calculations, break-even analysis, and a decision framework for choosing your pricing model.
In the meantime, start exploring your slot usage:
-- Quick slot usage summary for the past 7 daysSELECT DATE(creation_time) AS date, COUNT(*) AS job_count, SUM(total_slot_ms) / 1000 / 60 / 60 AS total_slot_hours, AVG(SAFE_DIVIDE(total_slot_ms, TIMESTAMP_DIFF(end_time, start_time, MILLISECOND))) AS avg_slots_per_jobFROM `region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECTWHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY) AND job_type = 'QUERY'GROUP BY dateORDER BY date;Understanding your current usage is the first step toward optimizing it.
Further Reading
Google Cloud Documentation
- Understand Slots
- BigQuery Editions
- Introduction to Slots Autoscaling
- Workload Management with Reservations
Community Resources