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:

  1. Parses your SQL and creates an execution plan
  2. Decomposes the plan into stages (logical units of work)
  3. Breaks each stage into steps that can run in parallel
  4. Assigns slots to execute those steps
  5. Shuffles intermediate results between stages
  6. 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 us can’t run jobs in eu)
  • 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 prod reservation with 500 slots for production pipelines
  • A bi reservation with 300 slots for dashboard queries
  • A dev reservation 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 TypeWhat It Covers
QUERYInteractive and batch queries
PIPELINELoad, export, and copy jobs
ML_EXTERNALBigQuery ML with external models
BACKGROUNDMaterialized view refresh, etc.
CONTINUOUSContinuous 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

FeatureStandardEnterpriseEnterprise PlusOn-Demand
Max slots1,600Quota-basedQuota-based~2,000 shared
Baseline slotsN/A
AutoscalingN/A
Idle slot sharingN/A
BigQuery ML
CommitmentsN/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 100
12:00:05 - Query completes → Still at 100 (window active)
12:01:00 - Window expires, no new demand → Scales to 0
12:01:02 - New query needs 50 slots → Scales to 50
12:01:03 - Query completes → Still at 50 (new window)
12:02:03 - Window expires → Scales to 0

This 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:

  1. Baseline slots (guaranteed, use first)
  2. Idle slots from other reservations (if sharing enabled)
  3. Autoscaling slots (if baseline + idle exhausted)
  4. 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:

  1. Divide slots equally among projects running jobs in the reservation
  2. 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 AProject B
Slots allocated500500
Queries120
Slots per query50025

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
end

Contention Priority During Regional Constraints

When an entire region is under capacity pressure (rare, but possible), BigQuery prioritizes requests:

  1. Enterprise Plus & Enterprise baseline/committed slots
  2. Enterprise Plus autoscaled slots
  3. Enterprise autoscaled slots
  4. 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 200
  • dev: 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:

  1. Running queries (determines which reservation’s slots are used)
  2. 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.

profiles.yml
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: 4

Then run with the appropriate target:

Terminal window
# Production incremental run
dbt run --target prod --select state:modified+
# Full refresh backfill
dbt run --target batch --full-refresh
# Development iteration
dbt run --target dev --select my_model

Best 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_slots
FROM `region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
AND (SELECT value FROM UNNEST(labels) WHERE key = 'dbt_invocation_id') IS NOT NULL
GROUP BY invocation, model
ORDER BY total_slot_minutes DESC
LIMIT 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

TableWhat It Shows
JOBS_BY_PROJECTPer-job metrics: slots, bytes, runtime
JOBS_BY_ORGANIZATIONSame, but across all projects (requires org permissions)
JOBS_TIMELINESecond-by-second slot consumption
RESERVATION_CHANGES_BY_PROJECTReservation modifications
RESERVATIONS_TIMELINEReservation 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_id
FROM `region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
AND job_type = 'QUERY'
AND state = 'DONE'
ORDER BY total_slot_ms DESC
LIMIT 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_hours
FROM `region-us`.INFORMATION_SCHEMA.JOBS_TIMELINE_BY_PROJECT
WHERE period_start > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
AND (statement_type != 'SCRIPT' OR statement_type IS NULL)
GROUP BY hour
ORDER BY hour;

The Slot Estimator Tool

BigQuery’s built-in Slot Estimator is useful for capacity planning.

To access it:

  1. Go to BigQuery Console
  2. Click Capacity management in the left nav
  3. 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/allocated
  • bigquery.googleapis.com/slots/available
  • bigquery.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 days
SELECT
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_job
FROM `region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
AND job_type = 'QUERY'
GROUP BY date
ORDER BY date;

Understanding your current usage is the first step toward optimizing it.


Further Reading

Google Cloud Documentation

Community Resources