The gcp data engineering certification — officially the Google Cloud Professional Data Engineer, or PDE — is the credential that says you can look at a vague business requirement and pick the right managed service under a cost, latency, or operational-overhead constraint, without reaching for the one you happen to know best. That is the entire skill the exam measures, and it is also the skill that makes you useful the day after you pass: the exam is not a trivia quiz about bq flags, it is two hours of "here is a scenario, here are four services that would all technically work, choose the one that is cheapest / fastest / least operational burden." Miss the constraint and every answer looks correct; read the constraint and three of the four answers eliminate themselves.
This roadmap is the guide you wished existed the first time you opened the exam blueprint and saw five domains, forty-plus services, and no clear order to study them in. It walks the whole professional data engineer exam end to end: the exam facts (format, cost, validity, how questions are written), the five weighted domains and what each one actually tests, a concrete six-to-eight-week study plan you can start on Monday, and a service-selection reflex for every decision the exam throws at you — Pub/Sub versus Dataflow versus Dataproc for ingestion, the BigQuery / Bigtable / Spanner / Cloud Storage / Firestore storage decision tree, BigQuery cost control, and the orchestration and security questions that decide the "maintain and automate" domain. Each domain pairs a teaching block with a worked exam scenario — the four answer choices, the elimination trace, the correct answer, and a concept-by-concept breakdown of why it is correct.
When you want hands-on reps alongside the reading, drill the SQL practice library →, rehearse pipeline design on the ETL practice library →, and sharpen the real-time axis with the streaming practice library →.
On this page
- What the PDE certification is — and how to read the exam blueprint
- Design & ingest — Dataflow, Pub/Sub, Dataproc
- Store the data — BigQuery, Bigtable, Spanner, Cloud Storage
- Prepare & analyze — BigQuery SQL, Dataform, Looker, BQML
- Maintain, automate & the exam-day playbook
- Cheat sheet — PDE service-selection recipes
- Frequently asked questions
- Practice on PipeCode
1. What the PDE certification is — and how to read the exam blueprint
The exam measures service selection under a constraint, not memorised syntax
The one-sentence framing that changes how you study: the Professional Data Engineer exam is a two-hour, roughly fifty-question test where nearly every question is a scenario with a hidden constraint — cost, latency, operational overhead, or governance — and your job is to pick the one managed service or configuration that satisfies that constraint, not the one that would merely work. Almost every wrong answer on the exam is a service that would function — it is just more expensive, higher-latency, or more operationally burdensome than the intended answer. That is why rote memorisation of gcloud flags fails and why building a service-selection reflex succeeds.
The exam facts you need before you book.
- Format. ~50 questions, mix of multiple-choice (one answer) and multiple-select (choose two or three). Two hours. Delivered at a test centre or via online proctoring.
- Cost. ~$200 USD (plus tax). A retake costs the same; there is a mandatory waiting period after a fail.
- Validity. The certification is valid for two years; you recertify by re-taking the current exam before it expires.
- Passing score. Google does not publish a numeric passing score and does not give you a percentage — you get a pass/fail result. Treat "consistently ~80%+ on quality practice exams" as your readiness bar, because the practice-question difficulty is your only calibration signal.
- Prerequisites. None required, but Google recommends 3+ years of industry experience including 1+ year on Google Cloud. In practice, a data engineer with 1–2 years of hands-on GCP and a disciplined study plan passes comfortably.
The five domains and their weights. The blueprint splits the exam into five sections; the weights are what tell you where to spend your hours.
- Designing data processing systems (~22%). Choosing architectures, storage, and processing frameworks; migration and modernisation; reliability and fault tolerance.
- Ingesting and processing the data (~25%). The single biggest domain. Streaming vs batch, Pub/Sub, Dataflow (Apache Beam), Dataproc (Spark/Hadoop), Data Fusion, windowing and late data.
- Storing the data (~20%). BigQuery, Bigtable, Spanner, Cloud SQL, Cloud Storage, Firestore — matching the store to access pattern, latency, and consistency.
- Preparing and using data for analysis (~15%). BigQuery analytics, Looker, Dataform, BigQuery ML, materialized views, governance.
- Maintaining and automating data workloads (~18%). Orchestration (Composer/Workflows), monitoring, autoscaling, cost optimisation, IAM and security.
How PDE questions are written — the keyword tells. Learn to spot the constraint keyword because it is the answer key hiding in plain sight.
- "Most cost-effective" → the answer trades some latency or convenience for lower spend (serverless, autoscaling to zero, partitioning to scan fewer bytes).
- "Least operational overhead / fully managed" → prefer the serverless option (Dataflow over self-managed Spark, BigQuery over a self-run warehouse, Workflows over a Composer cluster for simple chains).
- "Real-time / lowest latency" → streaming path (Pub/Sub + Dataflow, Bigtable for reads), never a nightly batch.
- "Minimal code / no infrastructure to manage" → a template or a managed connector (Dataflow templates, Data Fusion, BigQuery Data Transfer Service).
- "Global / strong consistency" → Spanner, not BigQuery and not Bigtable.
What separates a pass from a fail.
- Can you name the constraint keyword in every scenario before you look at the answers? — the single highest-leverage habit.
- Do you know the latency and consistency profile of each storage service cold? — required.
- Can you eliminate two wrong answers immediately and then decide between the last two on cost vs ops overhead? — the technique that wins the close calls.
Worked example — the six-to-eight-week study roadmap
Detailed explanation. The most useful artifact for this exam is a week-by-week plan that front-loads the two biggest domains (ingest/process and store, ~45% combined) and leaves the last week for full-length practice exams and gap-closing. Build the plan around doing, not reading: for every service, spin up a free-tier or trial resource and run one end-to-end job, because the scenarios are written by people who have watched a Dataflow job autoscale and a BigQuery query bill you for a full-table scan.
- Weeks 1–2. Ingest & process (Pub/Sub, Dataflow windowing, Dataproc). Build one streaming pipeline and one batch job.
- Weeks 3–4. Storage (BigQuery deep-dive, Bigtable schema design, Spanner vs Cloud SQL). Load data; run partitioned/clustered queries; watch the bytes-scanned meter.
- Week 5. Analyze (BigQuery SQL, Dataform, Looker, BQML) + governance (IAM, authorized views, Dataplex, DLP).
- Week 6. Maintain & automate (Composer, Workflows, Monitoring, cost controls, CMEK/VPC-SC).
- Weeks 7–8 (buffer). Two or three full-length timed practice exams; review every wrong answer until you can articulate why the right answer beat the runner-up.
Question. You have 6 weeks and ~8 hours/week. How should you allocate study time across the five domains to maximise expected score?
Input.
| Domain | Exam weight | Suggested share of hours | Why |
|---|---|---|---|
| Ingesting & processing | ~25% | ~28% | Biggest domain; most services; hardest scenarios |
| Storing the data | ~20% | ~22% | Storage decision tree is high-frequency |
| Designing systems | ~22% | ~20% | Cross-cutting; reinforced by the other domains |
| Maintain & automate | ~18% | ~18% | Orchestration + security + cost |
| Prepare & analyze | ~15% | ~12% | Smallest; mostly BigQuery you already touch |
Code.
# A weekly cadence that has worked for busy engineers
Mon–Wed : read the domain's exam-guide bullet points + official docs for 2 services
Thu : hands-on — build ONE thing (a pipeline, a table, a job) end to end
Fri : 15–20 practice questions on that domain; log every miss with the reason
Weekend : re-attempt only the missed questions; write the one-line rule you got wrong
# Readiness bar before booking:
# - >= 80% on two different full-length practice exams
# - you can state the constraint keyword -> service mapping from memory
Step-by-step trace.
- Multiply each domain weight by total hours (48) to get a baseline: ingest 12h, design 10.5h, store 9.6h, maintain 8.6h, analyze 7.2h.
- Shift ~2h from analyze (you already use BigQuery daily) into ingest/store (the highest-variance domains).
- Reserve weeks 7–8 entirely for timed exams — no new material — because exam stamina and elimination speed are themselves skills.
- After each practice exam, sort misses by domain; the domain with the most misses gets the next week's Thursday hands-on session.
Output:
| Week | Focus | Deliverable |
|---|---|---|
| 1–2 | Ingest & process | 1 streaming + 1 batch pipeline built |
| 3–4 | Storage | Partitioned/clustered BQ + Bigtable schema |
| 5 | Analyze + governance | Dataform DAG + authorized view |
| 6 | Maintain & automate | Composer DAG + monitoring alert |
| 7–8 | Timed practice | ≥80% on 2 full exams |
Why this works — concept by concept:
- Weight-proportional allocation — spending hours in proportion to domain weight maximises expected marks per hour, then a small shift toward high-variance domains (ingest, store) reduces the chance of a catastrophic section.
- Build one thing per domain — the scenarios are written from operational experience; a single end-to-end job teaches you the failure modes (autoscaling, bytes scanned, hotspotting) that trivia cannot.
- Miss-logging — recording why you missed a question converts a wrong answer into a durable rule ("BigQuery is never the answer for a 10 ms single-row lookup"), which is exactly the reflex the exam rewards.
- Timed dress rehearsals — the last two weeks build stamina and elimination speed, the two things that decide close questions under a ticking clock.
- Cost — the plan is ~48 study hours; the exam is $200 and two years valid, so the expected cost per year of credential is modest relative to the salary signal it carries.
The constraint-keyword drill — reading a PDE question before you read the answers
Detailed explanation. The single most transferable exam skill is extracting the constraint from a question stem before your eyes touch the four options, because the options are engineered to all look plausible. Every PDE stem hides its answer in one of a small number of constraint families — latency, cost, operational overhead, consistency, or governance — and once you can name the family, three options usually collapse. Practise this as a mechanical drill: read the stem, underline the constraint clause (often the last sentence), map it to a service class, then read the options and eliminate anything that violates the constraint.
- Latency words — "real-time", "sub-second", "lowest latency", "immediately" → streaming path or a low-latency store (Pub/Sub+Dataflow, Bigtable, BI Engine).
- Cost words — "most cost-effective", "minimise cost", "cheapest" → serverless / autoscale-to-zero / scan-fewer-bytes (partitioning, storage classes, on-demand slots).
- Ops words — "least operational overhead", "fully managed", "no infrastructure to manage" → the serverless option over the cluster (Dataflow > Dataproc, Workflows > Composer).
- Consistency words — "globally consistent", "strong consistency", "ACID across regions" → Spanner.
- Governance words — "without exposing PII", "restrict access", "must not copy data" → authorized views / column-level security / VPC-SC.
Question. For each stem, name the constraint family and the service class it points to.
Input.
| Question stem (last clause) | Constraint family | Service class |
|---|---|---|
| "…with the least operational overhead." | operational overhead | serverless |
| "…the most cost-effective storage for data older than 1 year." | cost | GCS Coldline/Archive |
| "…serve single-row reads in under 10 ms at 1M writes/sec." | latency + scale | Bigtable |
| "…globally consistent financial transactions." | consistency | Spanner |
| "…analysts must not see PII columns." | governance | column-level security |
Code.
# The 4-step drill, applied to every question:
1. READ the last sentence first -> find the constraint clause
2. NAME the constraint family -> latency|cost|ops|consistency|governance
3. MAP to a service class -> (see table above)
4. ELIMINATE options that violate it -> usually 2 die immediately; decide the last 2
Step-by-step trace.
- Take stem 3: the last clause is "under 10 ms at 1M writes/sec" → constraint family = latency + write-scale.
- Map: single-digit-ms point reads at massive write volume = Bigtable's exact profile.
- Read the options: any that name BigQuery (analytical, seconds) or Cloud SQL (won't scale to 1M/sec) violate the constraint → eliminate.
- The surviving option that names Bigtable with a well-distributed row key is the answer.
Output:
| Stem | Named constraint | Winning service |
|---|---|---|
| least ops overhead | operational overhead | serverless (Dataflow/Workflows) |
| storage > 1 year | cost | GCS Coldline/Archive |
| 10 ms @ 1M/sec | latency + scale | Bigtable |
| global txn | consistency | Spanner |
| hide PII | governance | column-level security |
Rule of thumb. If you cannot name the constraint family in one word, re-read the stem — you are not ready to look at the options yet.
2. Design & ingest — Dataflow, Pub/Sub, Dataproc
The ingestion domain is 25% of the exam and hinges on one decision: streaming vs batch, and which managed engine fits
The invariant to burn in: on GCP, real-time ingestion is almost always Pub/Sub as the buffer feeding Dataflow as the processor; batch is either Dataflow (if you are writing new pipelines) or Dataproc (if you are lifting existing Spark/Hadoop); and the exam picks between them on "is there existing Spark code," "do you want serverless/no-ops," and "is it streaming." Every ingestion scenario is a variation on that sentence.
The core services and when each wins.
- Pub/Sub. The global, serverless message bus. It is the ingestion buffer — it decouples producers from consumers, absorbs spikes, and guarantees at-least-once (with an exactly-once option). If the scenario says "ingest millions of events per second from globally distributed sources," Pub/Sub is in the answer. It is not a processor and not a store.
- Dataflow. Managed Apache Beam. One programming model for both streaming and batch, fully serverless with autoscaling. This is the default answer for new pipelines and for anything needing windowing, watermarks, and late-data handling. "Least operational overhead + streaming transform" → Dataflow.
- Dataproc. Managed Spark/Hadoop/Presto clusters. The answer when there is existing Spark or Hadoop code to migrate ("lift and shift") or when a team's skillset is Spark. It has clusters to size and (unless you use serverless Dataproc) more ops than Dataflow.
- Data Fusion / Dataflow templates / Data Transfer Service. The "minimal code / no-code" answers — visual ETL (Data Fusion), pre-built jobs (templates), and scheduled SaaS/warehouse loads (DTS).
Windowing and watermarks — the concept the exam loves. Streaming data arrives out of order and late. Beam/Dataflow groups unbounded data into windows (tumbling/fixed, sliding/hopping, session), tracks progress with a watermark (the system's estimate of "we have probably seen all events up to time T"), and decides what to do with late data via triggers and allowed lateness. If a question describes "events arrive up to 10 minutes late and we need correct hourly aggregates," the answer involves Dataflow windowing with allowed lateness — not a cron batch job.
Common trap answers to pre-empt.
- Dataproc for greenfield streaming — wrong; new streaming is Dataflow. Dataproc's streaming story is weaker and higher-ops.
- Cloud Composer as an ingestion tool — wrong; Composer orchestrates jobs, it does not move event data.
- Loading straight to BigQuery with no buffer for a spiky real-time source — risky; Pub/Sub absorbs the spikes and provides replay.
- Dataflow when the requirement is "reuse our existing PySpark" — Dataproc is the lift-and-shift answer.
Dataflow windowing — a worked teaching example
Detailed explanation. Consider an IoT fleet publishing temperature readings to Pub/Sub. You need per-device average temperature every 5 minutes, correct even though some devices buffer offline and flush readings up to 10 minutes late. This is the canonical "fixed window + watermark + allowed lateness" pattern, and the exam expects you to recognise it instantly.
Question. Which processing configuration produces correct 5-minute per-device averages when readings can arrive up to 10 minutes late?
Input.
| Requirement | Value |
|---|---|
| Source | Pub/Sub topic device-readings
|
| Aggregation | avg temperature per device_id, per 5 min |
| Lateness | events up to 10 min late |
| Correctness | late events must update the window result |
Code.
# Apache Beam (Dataflow) — fixed 5-min windows, 10-min allowed lateness
import apache_beam as beam
from apache_beam.transforms import window
from apache_beam.transforms.trigger import AfterWatermark, AfterProcessingTime, AccumulationMode
(
p
| "ReadPubSub" >> beam.io.ReadFromPubSub(topic="projects/x/topics/device-readings")
| "Parse" >> beam.Map(parse_reading) # -> (device_id, (temp, event_ts))
| "Window" >> beam.WindowInto(
window.FixedWindows(5 * 60), # 5-minute tumbling windows
trigger=AfterWatermark(late=AfterProcessingTime(60)), # fire on watermark, re-fire on late data
allowed_lateness=10 * 60, # accept events up to 10 min late
accumulation_mode=AccumulationMode.ACCUMULATING, # late data updates the running result
)
| "KeyByDevice" >> beam.Map(lambda r: (r["device_id"], r["temp"]))
| "AvgPerDevice" >> beam.combiners.Mean.PerKey()
| "ToBigQuery" >> beam.io.WriteToBigQuery("proj.ds.device_avg_5m")
)
Step-by-step trace.
- Pub/Sub delivers readings; Dataflow assigns each to the 5-minute window its
event_tsfalls in — not its arrival time. - The watermark advances as time passes; when it crosses a window's end,
AfterWatermarkfires and emits the average computed so far. - A device that was offline flushes readings 8 minutes late; because
allowed_lateness=10min, those readings still land in their original window. - The late trigger re-fires and, because the mode is
ACCUMULATING, emits an updated average for that window that includes the late readings. - BigQuery receives a corrected row for the affected window.
Output:
| device_id | window_start | avg_temp | note |
|---|---|---|---|
| dev-17 | 10:00 | 21.4 | on-time result |
| dev-17 | 10:00 | 21.9 | corrected after late flush |
Why this works — concept by concept:
-
Event-time windowing — grouping by
event_ts(not arrival time) is what makes the 5-minute averages meaningful; processing-time windows would smear late readings into the wrong bucket. - Watermark — Dataflow's estimate of completeness lets it emit a result promptly while still leaving the window open for a bounded lateness period.
- Allowed lateness + accumulating mode — together they guarantee that a device flushing 8 minutes late updates the correct window instead of being dropped or double-counted.
- Pub/Sub buffer — decouples the fleet from the pipeline so an ingestion spike or a pipeline restart never loses readings (replay from the subscription).
- Cost — Dataflow autoscales workers to the backlog and scales down when idle, so you pay for throughput, not a fixed cluster; the whole path is serverless.
Pub/Sub delivery semantics, ordering & dead-letter — a worked teaching example
Detailed explanation. The exam probes whether you understand what Pub/Sub guarantees and how you make it reliable: default delivery is at-least-once (a message can be redelivered, so consumers must be idempotent), ordering is only preserved when you set an ordering key (and publish to a region), and messages that repeatedly fail processing should be routed to a dead-letter topic after a max delivery attempts threshold instead of being redelivered forever. Exactly-once delivery is available as an opt-in within a region. Recognising which knob a scenario needs — idempotency, ordering key, dead-letter, or exactly-once — is the tell.
- At-least-once (default). Duplicates possible → make the sink idempotent (dedupe on a message id / business key).
- Ordering key. Same-key messages delivered in publish order; unrelated keys stay parallel for throughput.
- Dead-letter topic. After N failed deliveries, move the message aside so a poison message cannot block the subscription.
- Exactly-once (opt-in, regional). Removes duplicates within the region at some throughput cost.
Question. A payments stream must process each event once, preserve per-account order, and never let one malformed event block the subscription. Configure Pub/Sub.
Input.
| Requirement | Pub/Sub feature |
|---|---|
| Per-account ordering | ordering key = account_id
|
| No duplicate side effects | idempotent sink (or exactly-once) |
| Poison message isolation | dead-letter topic + max delivery attempts |
| Replay after incident | subscription retention / seek |
Code.
# Subscription config (conceptual)
ordering_key = "account_id" # per-account FIFO
enable_exactly_once = true # regional exactly-once delivery
dead_letter_topic = "payments-dlq"
max_delivery_attempts = 5 # after 5 fails -> DLQ
retain_acked_messages = true # allow seek/replay
# Consumer still dedupes on event_id as defense-in-depth (at-least-once mindset)
Step-by-step trace.
- Publisher sets
ordering_key = account_id, so all events for one account are delivered in publish order while different accounts stay parallel for throughput. - The subscription enables exactly-once within the region; the consumer additionally dedupes on
event_idso a cross-region or config edge case still cannot double-charge. - A malformed event fails processing; after 5 delivery attempts Pub/Sub moves it to
payments-dlqinstead of redelivering forever — the subscription keeps flowing. - During an incident, retained messages allow a seek/replay to reprocess a time window.
Output:
| Concern | Result |
|---|---|
| Ordering | per-account FIFO preserved |
| Duplicates | removed (exactly-once + idempotent sink) |
| Poison message | isolated in DLQ after 5 tries |
| Recovery | replay via retention/seek |
Rule of thumb. Treat Pub/Sub as at-least-once by default and design idempotent sinks; reach for ordering keys, dead-letter topics, and exactly-once only when the scenario explicitly demands them.
Exam scenario on ingestion
You are asked to design ingestion+processing for a globally distributed clickstream: millions of events/second, need per-session aggregates with late events tolerated, minimal operational overhead, and results in BigQuery for analysts.
Solution Using Pub/Sub + Dataflow session windows into BigQuery
Answer choices (as the exam would present them).
- A. Cloud Scheduler triggers an hourly Dataproc Spark batch job reading from Cloud Storage.
- B. Pub/Sub → Dataflow streaming pipeline with session windows and allowed lateness → BigQuery.
- C. App writes directly to BigQuery via the streaming API with no buffer.
- D. Cloud Composer DAG polls the source every 5 minutes and loads BigQuery.
Code.
Elimination:
A batch + Dataproc -> not real-time, has a cluster to manage [reject: latency + ops]
C direct-to-BQ, no buffer -> can't absorb spikes, no replay/late [reject: reliability]
D Composer polling -> Composer orchestrates, it doesn't stream [reject: wrong tool]
B Pub/Sub + Dataflow session windows -> serverless, late-tolerant [ACCEPT]
Step-by-step trace.
- Constraint keywords: "millions/sec + global" → Pub/Sub; "per-session + late tolerated" → session windows + allowed lateness; "minimal operational overhead" → serverless Dataflow, not Dataproc clusters.
- A is batch (fails real-time) and adds a cluster (fails ops overhead) — eliminate.
- C removes the buffer, so a spike or restart drops events and there is no replay — eliminate on reliability.
- D uses Composer as if it were an ingestion engine — category error; Composer schedules, Dataflow processes — eliminate.
- B satisfies every constraint: Pub/Sub buffers globally, Dataflow session-windows the events with lateness, BigQuery serves analysts, and nothing has servers to manage.
Output:
| Constraint | Winner |
|---|---|
| Real-time, millions/sec, global | Pub/Sub |
| Per-session + late data | Dataflow session windows |
| Minimal ops | Serverless (no clusters) |
| Analyst-queryable | BigQuery |
Why this works — concept by concept:
- Buffer-then-process — Pub/Sub in front of Dataflow is the canonical GCP streaming shape; it decouples producers, absorbs spikes, and enables replay.
-
Session windows — the exam's cue for "per-session" is literally the Beam
Sessionswindow with a gap duration; recognising the keyword maps straight to the API. - Serverless beats clusters on ops-overhead questions — when the constraint is "minimal operational overhead," Dataflow's no-cluster model beats Dataproc every time.
- Cost — pay-per-throughput autoscaling on Dataflow and per-message Pub/Sub pricing, with no idle cluster cost, is also the most cost-effective steady-state design.
Streaming
Topic — streaming
Streaming ingestion and windowing problems
3. Store the data — BigQuery, Bigtable, Spanner, Cloud Storage
20% of the exam is one decision tree: match the store to access pattern, latency, and consistency
The invariant: there is one right store per access pattern, and the exam tests whether you can read the access pattern off the scenario — analytical scans over huge tables go to BigQuery, single-row low-latency lookups at massive write volume go to Bigtable, globally-consistent transactional SQL goes to Spanner, objects and data-lake files go to Cloud Storage, and semi-structured document/mobile data goes to Firestore. Pick the wrong one and the scenario's latency or cost requirement is violated.
The decision tree, memorised.
- BigQuery — serverless, columnar, petabyte-scale analytics warehouse. Best for ad-hoc SQL, aggregations, and BI over large tables. Not for low-latency single-row lookups (it scans; queries are seconds, not milliseconds) and not an OLTP store.
- Bigtable — wide-column NoSQL for high-throughput, low-latency (single-digit ms) key lookups at massive scale (time-series, IoT, ad-tech, personalisation). You design a single row key; there are no joins and no secondary indexes. If the scenario says "1M writes/sec and 10 ms reads," it is Bigtable.
- Spanner — horizontally-scalable, globally-distributed, strongly-consistent relational SQL with 99.999% SLA. The answer for a global transactional system that must not lose consistency (financial ledgers, global inventory). More expensive; only pick it when "global + strong consistency + relational" all appear.
- Cloud SQL — managed MySQL/PostgreSQL/SQL Server for regional OLTP at moderate scale. The answer when the scenario says "existing MySQL app, lift to managed, no global requirement."
- Cloud Storage (GCS) — object storage for the data lake, staging, backups, and archives; storage classes (Standard/Nearline/Coldline/Archive) are the cost lever, and lifecycle rules tier data down automatically.
- Firestore — serverless document database for mobile/web app state with real-time sync.
BigQuery internals the exam probes.
- Partitioning (by ingestion time, a date/timestamp column, or an integer range) prunes whole partitions so queries scan fewer bytes — the primary cost lever.
- Clustering sorts data within partitions by up to four columns so filtered/aggregated queries read less — stacks on top of partitioning.
- Slots are the unit of compute; on-demand pricing bills per bytes scanned, capacity/editions pricing bills for reserved slots. "Predictable heavy workload → reserved slots; spiky/ad-hoc → on-demand."
- Storage vs compute are billed separately — long-term storage auto-discounts untouched data; you pay for query compute independently.
Bigtable schema design the exam probes.
- The row key determines performance. Keys that monotonically increase (timestamps, sequential IDs) create hotspotting — all writes hit one node. Field-promotion and salting distribute load.
- Bigtable is "tall and narrow" for time-series (one event per row) or "wide" for entities with many attributes; know both.
Common trap answers.
- BigQuery for a 10 ms single-row lookup — wrong; that is Bigtable (or a cache). BigQuery is analytical, not low-latency point reads.
- Spanner for analytics scans — wrong and expensive; analytics is BigQuery. Spanner is transactional.
- Bigtable when you need joins or ad-hoc SQL — wrong; Bigtable has no joins.
- Cloud SQL for a global, multi-region, strongly-consistent app — wrong; that is Spanner.
Bigtable row-key design — a worked teaching example
Detailed explanation. A sensor platform writes 1,000,000 readings/second and must serve "latest 24h for device X" in under 10 ms. The naive key timestamp hotspots because all writes land at the tail. Promote the device_id ahead of a reversed/plain timestamp so writes spread across devices and a device's readings stay contiguous for fast range scans.
Question. Design the Bigtable row key for 1M writes/sec with 10 ms "recent readings per device" reads.
Input.
| Requirement | Value |
|---|---|
| Write rate | ~1,000,000 rows/sec |
| Read pattern | recent readings for a given device_id
|
| Latency target | < 10 ms p99 |
| Anti-goal | avoid hotspotting a single node |
Code.
# BAD: monotonically increasing key -> all writes hit the last node (hotspot)
rowkey = f"{event_ts}" # 2026-08-14T10:00:00Z...
# GOOD: field-promotion (device first) + timestamp -> writes spread, reads contiguous
rowkey = f"{device_id}#{event_ts}" # dev-17#2026-08-14T10:00:00Z
# For "latest first" scans, reverse the timestamp so newest sorts to the top:
rowkey = f"{device_id}#{2**63 - epoch_ms}" # dev-17#<reversed-ts>
Step-by-step trace.
- With key =
event_ts, every write in a given millisecond shares a prefix and lands on the same tablet → one node saturates, the rest idle → hotspot. - Prepending
device_idspreads writes across as many tablets as there are active devices → the 1M/sec load balances. - Because a device's rows share the
device_id#prefix, "recent readings for device X" is a single contiguous range scan → fast. - Reversing the timestamp makes the newest reading sort first, so "latest 24h" reads the top of the range without scanning the whole device history.
Output:
| Key design | Write distribution | Read for one device |
|---|---|---|
event_ts |
hotspot (1 node) | scattered |
device_id#event_ts |
balanced | contiguous range |
device_id#reversed_ts |
balanced | newest-first, minimal scan |
Why this works — concept by concept:
-
Field promotion — putting the high-cardinality
device_idfirst spreads writes across tablets, which is the entire trick to avoiding Bigtable hotspots. - Contiguous range reads — a shared key prefix keeps one device's data physically adjacent, turning "recent per device" into a cheap range scan instead of a full-table scan.
- Reversed timestamp — encoding newest-first lets a "latest N" read stop early, hitting the sub-10 ms target.
- No joins by design — Bigtable trades SQL joins for predictable single-digit-ms reads at write volumes BigQuery could never serve as point lookups.
- Cost — Bigtable bills per node-hour and storage; a well-distributed key lets you serve the load on fewer nodes, which is both the performance and the cost win.
BigQuery partitioning & clustering key choice — a worked teaching example
Detailed explanation. Storing analytical data well in BigQuery is mostly one decision: what to partition by and what to cluster by, because those two choices determine how many bytes every future query scans — and bytes scanned is the bill. Partition by the column that queries filter on by time (usually a date/timestamp, giving up to ~4000 daily partitions or ingestion-time partitioning); cluster by the up-to-four columns queries filter or group by next (high-cardinality, frequently-filtered). Get this wrong and either partition pruning never triggers (queries scan everything) or you hit the partition limit.
-
Partition by a date/timestamp column your queries filter on (
event_date), or by an integer range, or by ingestion time. -
Cluster by the next-most-filtered columns (e.g.
country,product_id) — order matters (most-selective first). - Set partition expiration to auto-drop old partitions and cap storage.
- Avoid partitioning on a high-cardinality non-date column (you will exceed the partition limit).
Question. A 5-TB events(event_ts, country, product_id, revenue) table is queried as "last 7 days, filter by country, group by product." Choose partition + cluster keys.
Input.
| Query predicate | Best physical design |
|---|---|
WHERE DATE(event_ts) >= … |
partition by DATE(event_ts)
|
WHERE country = … |
cluster key #1 = country
|
GROUP BY product_id |
cluster key #2 = product_id
|
| retention 400 days | partition expiration = 400d |
Code.
CREATE TABLE analytics.events
PARTITION BY DATE(event_ts)
CLUSTER BY country, product_id
OPTIONS(partition_expiration_days = 400) AS
SELECT * FROM analytics.events_raw;
-- This query now prunes to 7 partitions and reads only matching clusters:
SELECT product_id, SUM(revenue) AS rev
FROM analytics.events
WHERE DATE(event_ts) >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
AND country = 'US'
GROUP BY product_id;
Step-by-step trace.
- Partitioning by
DATE(event_ts)lets the 7-day filter prune ~393 of 400 partitions before any scan. - Clustering by
countryfirst means thecountry = 'US'filter reads only the US-sorted blocks within those 7 partitions. - Clustering by
product_idsecond co-locates rows for theGROUP BY, cutting the aggregation's read. -
partition_expiration_days = 400auto-drops older partitions, capping storage without a cleanup job.
Output:
| Design | Bytes scanned (7-day query) |
|---|---|
| No partition/cluster | ~5 TB |
| Partition only | ~90 GB |
| Partition + cluster (country, product) | ~single-digit GB |
Rule of thumb. Partition by the time column queries filter on; cluster by the next filters/group columns, most-selective first; never partition by a high-cardinality non-date column.
Cloud Storage classes & lifecycle — a worked teaching example
Detailed explanation. Object storage cost is a frequent "most cost-effective" question, and the lever is the storage class plus lifecycle rules. GCS offers Standard (hot, frequent access), Nearline (~monthly access), Coldline (~quarterly), and Archive (rarely, e.g. compliance retention). Each colder tier has cheaper storage but higher retrieval cost and a minimum storage duration. The exam answer for "reduce cost for aging data" is almost never "delete it manually" or "move it by hand" — it is a lifecycle rule that transitions objects to a colder class by age and optionally deletes them after a retention window, automatically.
- Standard — active data lake, staging, frequently read.
- Nearline — backups/data accessed < once a month.
- Coldline — data accessed a few times a year.
- Archive — long-term compliance; cheapest storage, highest retrieval latency/cost.
Question. Raw event files are queried heavily for 30 days, occasionally for a year, then kept 7 years for compliance but almost never read. Minimise total storage cost with no manual ops.
Input.
| Age of object | Access pattern | Target class |
|---|---|---|
| 0–30 days | heavy | Standard |
| 31–365 days | occasional | Nearline |
| 1–7 years | compliance only | Archive |
| > 7 years | none | delete |
Code.
// GCS lifecycle policy — automatic class transitions + expiry
{
"rule": [
{"action": {"type": "SetStorageClass", "storageClass": "NEARLINE"},
"condition": {"age": 30}},
{"action": {"type": "SetStorageClass", "storageClass": "ARCHIVE"},
"condition": {"age": 365}},
{"action": {"type": "Delete"},
"condition": {"age": 2555}}
]
}
Step-by-step trace.
- New objects land in Standard and serve the heavy 30-day read window at low latency.
- At age 30 the lifecycle rule transitions them to Nearline — cheaper storage for the occasional reads that follow.
- At age 365 they move to Archive — cheapest storage for the 7-year compliance hold where reads are rare.
- At age 2555 days (~7 years) they are deleted automatically — no human ever touches them.
Output:
| Window | Class | Relative storage cost |
|---|---|---|
| 0–30 d | Standard | highest |
| 31–365 d | Nearline | lower |
| 1–7 y | Archive | lowest |
| > 7 y | deleted | zero |
Rule of thumb. "Reduce cost for aging data" = a lifecycle rule that tiers down by age; never a manual migration or a cron job.
Exam scenario on storage selection
A fintech needs a globally-distributed ledger: multi-region, strongly consistent, relational schema with transactions, five-nines availability. Analysts separately need to run large ad-hoc SQL aggregations over the ledger history.
Solution Using Spanner for the ledger + BigQuery for analytics
Answer choices.
- A. Put everything in BigQuery (ledger writes + analytics).
- B. Put everything in Bigtable.
- C. Spanner for the transactional ledger; stream/export to BigQuery for analytics.
- D. Cloud SQL (PostgreSQL) with read replicas in each region.
Code.
Elimination:
A BigQuery as an OLTP ledger -> not transactional, seconds not ms writes [reject]
B Bigtable -> no relational schema, no multi-row transactions/SQL [reject]
D Cloud SQL -> regional; replicas are async -> not global strong consistency[reject]
C Spanner (global, strong, relational) + BigQuery (analytics) [ACCEPT]
Step-by-step trace.
- Constraint keywords: "globally-distributed + strongly consistent + relational + transactions + five-nines" → this is the textbook Spanner definition.
- A misuses BigQuery as a transactional store — BigQuery is analytical, not an OLTP ledger — eliminate.
- B loses the relational schema and cross-row transactions — eliminate.
- D is regional; read replicas are asynchronous, so it cannot guarantee global strong consistency — eliminate.
- C uses each service for its purpose: Spanner owns the consistent global ledger; BigQuery owns the heavy analytical scans, fed by Spanner change streams or scheduled export.
Output:
| Need | Service |
|---|---|
| Global, strong, relational, txn | Spanner |
| Ad-hoc PB-scale analytics | BigQuery |
| Five-nines availability | Spanner SLA |
Why this works — concept by concept:
- Spanner is the only "global + strong + relational" answer — the moment those three words co-occur, Spanner is correct and everything else is a distractor.
- Separate transactional and analytical stores — OLTP (Spanner) and OLAP (BigQuery) have opposite access patterns; forcing one store to do both violates a latency or cost constraint.
- Change streams / export bridge the two — you do not query the ledger for analytics; you flow it into BigQuery where scans are cheap and fast.
- Cost — Spanner is premium per node, so you only run the ledger there and offload the heavy scans to BigQuery's pay-per-bytes model, keeping the expensive service small.
SQL
Topic — optimization
Query and storage optimization problems
4. Prepare & analyze — BigQuery SQL, Dataform, Looker, BQML
The analytics domain rewards knowing BigQuery deeply and when a lighter tool beats a heavier one
The invariant: BigQuery is the analytics engine, and the exam tests whether you transform in-warehouse (Dataform/scheduled queries), serve efficiently (materialized views, BI Engine, Looker), model with the lightest sufficient tool (BQML before Vertex AI), and govern access without copying data (authorized views, column-level security). The recurring theme is "use the managed, in-warehouse option, and control cost = bytes scanned."
BigQuery analytics features the exam expects.
-
Nested and repeated fields (
STRUCT/ARRAY) — BigQuery stores semi-structured data natively;UNNESTflattens repeated fields. Denormalising with nested fields is often preferred over joins for performance. - Window functions — ranking, running totals, and lag/lead over partitions; standard SQL, heavily used in analytics scenarios.
- Materialized views — precomputed, incrementally-refreshed aggregates that BigQuery can auto-route queries to; the answer for "speed up a repeated expensive aggregation with minimal maintenance."
- BI Engine — an in-memory acceleration layer for sub-second dashboards on BigQuery, the answer for "make Looker/Data Studio dashboards fast."
- Search indexes / primary+foreign keys (advisory) — newer features that inform the optimiser.
Transformation and modelling.
- Dataform (or dbt) — SQL-based, version-controlled transformation inside BigQuery, with dependency DAGs and tests. The answer for "manage ELT transformations as code." Scheduled queries cover simpler periodic transforms.
-
BigQuery ML (BQML) — train and serve models with SQL (
CREATE MODEL) directly in BigQuery: linear/logistic regression, k-means, time-series (ARIMA_PLUS), boosted trees, and even calls to Vertex AI models. The exam picks BQML when the data is already in BigQuery and the model is standard, and Vertex AI when you need custom training, deep learning, or MLOps at scale. - Looker — the governed semantic/BI layer (LookML) for self-serve analytics; the answer for "consistent, governed metrics across the org."
Governance without copying data.
- Authorized views / authorized datasets — expose a query result to a group without granting access to the underlying tables.
- Column-level security with policy tags (via Data Catalog/Dataplex) and row-level security restrict fields/rows by principal.
- Dataplex — governance and a data mesh/lakehouse catalog across GCS + BigQuery; DLP discovers and de-identifies sensitive data.
Common trap answers.
-
Vertex AI for a standard forecast on data already in BigQuery — usually overkill; BQML
ARIMA_PLUSis the lighter correct answer. - Copying data into a new dataset to restrict access — wrong; authorized views/column-level security expose without copying.
- A nightly full-table aggregation job when a materialized view would refresh incrementally — wrong on cost and freshness.
-
SELECT *on a wide table — scans every column and blows the bytes-scanned bill; select only needed columns.
BigQuery cost control — a worked teaching example
Detailed explanation. An analyst dashboard reruns the same daily revenue aggregation over a 2-TB events table dozens of times a day, scanning the whole table each time. Partition by date, cluster by country, and back the dashboard with a materialized view so repeated queries scan kilobytes instead of terabytes.
Question. How do you cut the bytes scanned (and therefore cost) for a repeated daily-revenue-by-country dashboard over a 2-TB table?
Input.
| Fact | Value |
|---|---|
| Table |
events — 2 TB, 400 days of data |
| Query | daily revenue by country, last 30 days |
| Frequency | dozens of runs/day |
| Current | full-table scan each run (2 TB) |
Code.
-- 1) Partition by day + cluster by country so filters prune + co-locate
CREATE TABLE analytics.events_p
PARTITION BY DATE(event_ts)
CLUSTER BY country AS
SELECT * FROM analytics.events;
-- 2) Materialized view for the exact repeated aggregation (auto-incremental refresh)
CREATE MATERIALIZED VIEW analytics.daily_rev_by_country AS
SELECT DATE(event_ts) AS d, country, SUM(revenue) AS revenue
FROM analytics.events_p
GROUP BY d, country;
-- 3) Dashboard queries the MV, filtered to 30 days -> scans partitions, not 2 TB
SELECT d, country, revenue
FROM analytics.daily_rev_by_country
WHERE d >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY);
Step-by-step trace.
- Partitioning by
DATE(event_ts)means a 30-day filter prunes ~370 of 400 partitions → the engine reads ~1/13th of the data. - Clustering by
countryco-locates rows per country soGROUP BY countryreads less within each partition. - The materialized view precomputes the daily sums and refreshes incrementally as new data lands, so each dashboard run reads a tiny aggregate, not raw events.
- BigQuery can even auto-rewrite a query against the base table to use the MV when it matches.
Output:
| Stage | Bytes scanned per run |
|---|---|
| Full-table scan | ~2 TB |
| Partition prune (30/400 days) | ~150 GB |
| + clustering | ~tens of GB |
| Materialized view | ~KB–MB |
Why this works — concept by concept:
- Partitioning prunes — a date filter skips whole partitions, and because BigQuery bills per byte scanned, skipping data is skipping cost.
- Clustering co-locates — sorting within partitions by the filter/group column reduces the bytes read for those predicates, stacking on partitioning.
- Materialized views precompute — the repeated aggregation is computed once and refreshed incrementally, so dozens of daily runs read a small aggregate instead of re-scanning raw data.
- Cost = bytes scanned — every optimisation here is really the same optimisation: read fewer bytes, pay less, return faster.
Nested & repeated fields (UNNEST) — a worked teaching example
Detailed explanation. BigQuery is columnar and stores semi-structured data natively as STRUCT (nested) and ARRAY (repeated) fields, and the exam expects you to know that denormalising with nested/repeated fields is often preferred over joins because it keeps related data co-located and avoids a shuffle. You read repeated fields back into rows with UNNEST. Recognising "one order with many line items" as an ARRAY<STRUCT> — not two joined tables — is the modelling instinct the analytics domain tests.
-
STRUCTgroups related columns (an address, a geo point) under one field. -
ARRAYholds repeated values (tags, line items) without a child table. -
UNNESTflattens an array into rows for aggregation. - Why prefer it — no join/shuffle for the one-to-many; columnar storage still prunes unread fields.
Question. An orders table stores line items as a repeated STRUCT. Compute total revenue per order and per product.
Input.
| Field | Type |
|---|---|
order_id |
INT64 |
items |
ARRAY<STRUCT<product STRING, qty INT64, price NUMERIC>> |
Code.
-- Revenue per order: sum the array's line items with a correlated UNNEST
SELECT
order_id,
(SELECT SUM(i.qty * i.price) FROM UNNEST(items) AS i) AS order_revenue
FROM analytics.orders;
-- Revenue per product: flatten items to rows, then aggregate
SELECT i.product, SUM(i.qty * i.price) AS product_revenue
FROM analytics.orders, UNNEST(items) AS i
GROUP BY i.product;
Step-by-step trace.
-
itemsis stored inline with each order as a repeatedSTRUCT— no separateorder_itemstable, so no join is needed. - The correlated
UNNEST(items)in the SELECT sums line items within one order, yielding per-order revenue. - The second query cross-joins each order with its flattened
items, producing one row per line item. -
GROUP BY i.productthen aggregates across all orders — all without ever shuffling two tables together.
Output:
| Query | Sample result |
|---|---|
| per order | order 1001 → 248.50 |
| per product | "widget" → 18,420.00 |
Rule of thumb. Model one-to-many as ARRAY<STRUCT> and read it with UNNEST when the child is always queried with its parent; reach for a join only when the child is independently queried at scale.
BigQuery ML forecast — when BQML beats Vertex AI, a worked teaching example
Detailed explanation. A recurring analytics question is "the data is already in BigQuery and we need a standard model — what's the least-effort path?" The exam answer is BigQuery ML: you train and serve with SQL (CREATE MODEL), no data movement, no separate serving infrastructure. ARIMA_PLUS handles time-series forecasting (with automatic seasonality and holiday effects); LOGISTIC_REG/BOOSTED_TREE_CLASSIFIER handle classification; KMEANS handles clustering. You escalate to Vertex AI only when you need custom/deep-learning architectures, large-scale training, or full MLOps — not for a standard forecast on warehouse data.
Question. Forecast the next 30 days of daily sales per product from a BigQuery sales table, with minimal effort and no data export.
Input.
| Fact | Value |
|---|---|
| Data |
sales(product_id, day, units) already in BigQuery |
| Task | 30-day per-product forecast |
| Constraint | minimal effort, no data movement |
| Model | seasonal time-series |
Code.
-- Train a per-product time-series model directly in BigQuery
CREATE OR REPLACE MODEL analytics.sales_arima
OPTIONS(
model_type='ARIMA_PLUS',
time_series_timestamp_col='day',
time_series_data_col='units',
time_series_id_col='product_id' -- one model per product, trained together
) AS
SELECT day, units, product_id FROM analytics.sales;
-- Forecast 30 days ahead with prediction intervals
SELECT * FROM ML.FORECAST(
MODEL analytics.sales_arima,
STRUCT(30 AS horizon, 0.9 AS confidence_level)
);
Step-by-step trace.
-
CREATE MODEL … ARIMA_PLUStrains directly on the BigQuery table — no export, no notebook, no cluster. -
time_series_id_col='product_id'fits a separate seasonal model per product in one statement. -
ML.FORECAST(... horizon => 30)returns the 30-day forecast with confidence bounds as a normal query result. - The output is queryable/joinable immediately and can back a Looker dashboard — still no data left BigQuery.
Output:
| product_id | forecast_day | forecast_units | lower_90 | upper_90 |
|---|---|---|---|---|
| p-1 | 2026-08-15 | 812 | 740 | 884 |
| p-2 | 2026-08-15 | 143 | 121 | 165 |
Rule of thumb. Standard model + data already in BigQuery → BQML. Custom/deep-learning/MLOps → Vertex AI. Do not export data to train something BQML already does in SQL.
Exam scenario on analytics + governance
Analysts need self-serve dashboards over a sensitive customer table, but must not see PII columns, and the org wants consistent metric definitions. Minimise cost and avoid duplicating data.
Solution Using authorized views + column-level security + Looker + BI Engine
Answer choices.
- A. Copy the table into a PII-free dataset for analysts.
- B. Authorized views + column-level policy tags to hide PII, Looker for governed metrics, BI Engine for speed.
- C. Grant analysts direct table access and trust them to avoid PII columns.
- D. Export to spreadsheets nightly with PII removed by a script.
Code.
Elimination:
A copy data -> duplication, drift, extra cost/governance surface [reject]
C direct access -> exposes PII, no governance [reject]
D nightly export -> stale, brittle, ungoverned [reject]
B authorized views + column security + Looker + BI Engine [ACCEPT]
Step-by-step trace.
- Constraints: "must not see PII," "consistent metrics," "minimise cost," "avoid duplicating data."
- A duplicates the table — violates "avoid duplicating data" and creates a second thing to secure and keep in sync — eliminate.
- C exposes PII outright — fails the security requirement — eliminate.
- D is stale and ungoverned — eliminate.
- B hides PII columns via policy tags/authorized views (no copy), defines metrics once in Looker (LookML) for consistency, and uses BI Engine for sub-second dashboards.
Output:
| Requirement | Mechanism |
|---|---|
| Hide PII, no copy | Authorized views + column-level security |
| Consistent metrics | Looker / LookML semantic layer |
| Fast dashboards | BI Engine |
| Low cost | Query in place; no duplication |
Why this works — concept by concept:
- Expose without copying — authorized views and column-level security grant result access while the base table stays locked, eliminating duplication and drift.
- Semantic layer for consistency — defining metrics once in Looker prevents every analyst reinventing "revenue," which is the governance win the scenario asks for.
- BI Engine for latency — the in-memory layer is the specific answer to "make dashboards fast" without pre-materialising everything.
- Cost — querying in place with partition/cluster pruning and MV acceleration beats maintaining a second copy on both storage and compute.
SQL
Topic — window-functions
Analytical SQL and window-function problems
5. Maintain, automate & the exam-day playbook
The last domain is orchestration, monitoring, cost, and security — plus the test-taking technique that wins close questions
The invariant: once data flows, the exam asks how you schedule it (Composer vs Workflows vs Scheduler), how you keep it healthy and cheap (Monitoring, autoscaling, slot reservations, lifecycle rules), and how you secure it (IAM least privilege, CMEK, VPC Service Controls) — and the correct answer is always the least-operational-overhead option that still meets the reliability and compliance bar.
Orchestration — pick by complexity.
- Cloud Composer (managed Apache Airflow) — the answer for complex, dependency-rich DAGs with retries, backfills, and a rich operator ecosystem. It runs a cluster, so it has a baseline cost and some ops; worth it for real pipelines.
- Workflows — serverless orchestration of API/service calls in YAML; the answer for lightweight chaining ("call this, then that") without a Composer cluster.
- Cloud Scheduler — managed cron; the answer for "trigger a job on a schedule," often paired with Pub/Sub or Workflows.
- Dataflow / BigQuery scheduled queries cover in-tool scheduling for their own jobs.
Reliability, monitoring, and cost.
- Cloud Monitoring + Logging + Error Reporting — metrics, dashboards, alerting; the answer for "detect and alert on pipeline failures/latency."
- Autoscaling — Dataflow scales workers to backlog; the answer for "handle variable load without over-provisioning."
-
BigQuery cost controls — reservations/editions for predictable heavy workloads, on-demand for spiky; custom quotas and
maximum_bytes_billedguardrails; partition expiration. - Cloud Storage lifecycle — auto-tier to Nearline/Coldline/Archive and delete by age; the answer for "reduce storage cost for aging data."
- Dead-letter queues + retries + idempotency — the reliability triad for streaming.
Security and compliance.
- IAM least privilege — grant the narrowest predefined role (or a custom role); service accounts per workload. "Which role?" questions want the least privilege that still works.
- CMEK (customer-managed encryption keys) — the answer for "we must control the encryption keys."
- VPC Service Controls — a security perimeter that prevents data exfiltration from services like BigQuery/GCS; the answer for "prevent data leaving the perimeter."
- Data residency — region/multi-region selection to keep data in a jurisdiction.
The exam-day playbook — how to actually take the test.
- Read the last sentence first. The constraint (cost / latency / least ops / global) usually lives in the final clause. Name it before reading the options.
- Eliminate two immediately. There are almost always two answers that are wrong on an obvious axis (batch when it says real-time; a cluster when it says serverless). Cross them out.
- Decide the last two on the constraint keyword. Between two workable options, the "most cost-effective" or "least operational overhead" phrase is the tiebreaker.
- Watch for "choose TWO/THREE." Multiple-select questions penalise partial answers; count the required selections.
- Flag and move on. Do not sink 5 minutes into one question; flag it, bank the easy marks, return with time left.
- Beware absolute distractors ("always," "never") and services used out of role (Composer moving data, BigQuery serving 10 ms lookups).
Making a flaky pipeline reliable — a worked teaching example
Detailed explanation. A nightly Composer DAG loads BigQuery but fails intermittently on transient errors and no one notices until morning. Add retries with backoff, a Monitoring alert on DAG failure, idempotent loads (write to a date-partitioned table with WRITE_TRUNCATE per partition), and a dead-letter path for bad records — all low-ops, managed features.
Question. How do you make a flaky nightly BigQuery load reliable and observable at least operational cost?
Input.
| Symptom | Fix lever |
|---|---|
| Transient task failures | retries + exponential backoff |
| Silent failures | Monitoring alert on DAG state |
| Re-runs double-load data | idempotent partition truncate |
| Bad records abort the load | dead-letter table |
Code.
# Cloud Composer (Airflow) task with retries + alerting + idempotent load
default_args = {
"retries": 3,
"retry_delay": timedelta(minutes=5),
"retry_exponential_backoff": True,
"on_failure_callback": alert_cloud_monitoring, # fire a Monitoring/alerting notification
}
load = BigQueryInsertJobOperator(
task_id="load_events",
configuration={"load": {
"destinationTable": {"projectId": "p", "datasetId": "raw", "tableId": "events$20260814"},
"writeDisposition": "WRITE_TRUNCATE", # idempotent: re-run overwrites the partition
"sourceUris": ["gs://bucket/events/2026-08-14/*.json"],
"sourceFormat": "NEWLINE_DELIMITED_JSON",
"maxBadRecords": 0, # bad records routed to a dead-letter step
}},
default_args=default_args,
)
Step-by-step trace.
- A transient BigQuery/network error now triggers up to 3 retries with exponential backoff instead of failing the run outright.
- If all retries fail,
on_failure_callbackfires a Cloud Monitoring alert — someone is paged instead of discovering it at 9 a.m. - Loading into the date-partition decorator
events$20260814withWRITE_TRUNCATEmakes a re-run overwrite only that day's partition, so replays are idempotent (no doubles). - Bad records are routed to a dead-letter table for inspection rather than aborting the whole load.
Output:
| Before | After |
|---|---|
| Silent nightly failures | Retries + alert on failure |
| Re-runs double-count | Idempotent per-partition truncate |
| One bad row kills the load | Dead-letter isolates bad rows |
Why this works — concept by concept:
- Retries with backoff — most pipeline failures are transient; bounded exponential retries absorb them without human involvement and at no extra service cost.
- Alert on failure — observability turns a silent failure into a paged incident; Cloud Monitoring is the managed, low-ops way to get there.
-
Idempotent partition loads — writing per-partition with
WRITE_TRUNCATEmeans "run it again" is always safe, which is the core reliability property for batch loads. - Dead-letter isolation — quarantining bad records keeps one malformed row from failing the entire batch.
- Cost — every fix uses managed features (Composer retries, Monitoring alerts, BigQuery partition semantics) so reliability improves with near-zero added operational overhead — exactly what "least operational overhead" questions reward.
IAM least-privilege role selection — a worked teaching example
Detailed explanation. Security questions almost always ask for the least privilege that still works, and the trap answer is a broad primitive role (Owner/Editor) or a project-level grant when a resource-level predefined role would do. GCP's model is: attach the narrowest predefined role to a per-workload service account, at the narrowest resource scope. For "a Dataflow job that reads a Pub/Sub subscription and writes one BigQuery dataset," you do not grant BigQuery Admin on the project — you grant roles/bigquery.dataEditor on that dataset plus roles/pubsub.subscriber on that subscription.
- Service account per workload — not a shared human account; scoped, auditable, rotatable.
-
Predefined over primitive —
dataEditornotEditor;dataViewernotViewer. - Resource scope over project scope — grant on the dataset/bucket/subscription, not the whole project.
- Custom role — only when no predefined role fits.
Question. A Dataflow pipeline reads subscription clicks-sub and writes dataset analytics. Grant the least privilege that works.
Input.
| Action needed | Narrowest role | Scope |
|---|---|---|
| Read the subscription | roles/pubsub.subscriber |
subscription clicks-sub
|
| Write the dataset | roles/bigquery.dataEditor |
dataset analytics
|
| Run the job | roles/dataflow.worker |
project (worker SA) |
| Anti-pattern |
roles/editor / roles/owner
|
project ❌ |
Code.
# Per-workload service account, resource-scoped predefined roles:
sa = dataflow-clicks@project.iam
grant roles/pubsub.subscriber on subscription clicks-sub to sa
grant roles/bigquery.dataEditor on dataset analytics to sa
grant roles/dataflow.worker on project (worker identity) to sa
# NOT: roles/editor on the project (far more than the job needs)
Step-by-step trace.
- Create a dedicated service account for this pipeline so its permissions are isolated and auditable.
- Grant
pubsub.subscriberonly onclicks-sub— the job can read that subscription and nothing else in Pub/Sub. - Grant
bigquery.dataEditoronly on theanalyticsdataset — it can write there but cannot touch other datasets. - Grant the
dataflow.workerrole for the job to run; reject any answer that hands outEditor/Owner.
Output:
| Option | Verdict |
|---|---|
Owner/Editor on project |
❌ over-privileged |
| Resource-scoped predefined roles on a per-workload SA | ✅ least privilege |
Rule of thumb. "Which role?" questions want the narrowest predefined role at the narrowest scope on a dedicated service account — never a primitive role and never project-wide when resource-scope works.
Exam scenario on orchestration choice
You need to run three dependent steps once a day — export from Cloud SQL, transform in BigQuery, notify a downstream service — with retries and minimal operational overhead. There is no complex DAG, no backfills, no large operator ecosystem needed.
Solution Using Cloud Scheduler + Workflows (not a Composer cluster)
Answer choices.
- A. Stand up Cloud Composer to run the three steps.
- B. Cloud Scheduler triggers a serverless Workflow that runs the three steps with retries.
- C. A Compute Engine VM running cron and bash scripts.
- D. Chain three separate Cloud Functions with manual timers.
Code.
Elimination:
A Composer -> runs a cluster (baseline cost + ops) for a 3-step chain [reject: overkill]
C VM + cron -> self-managed, patching, no managed retries [reject: high ops]
D hand-chained Functions + manual timers -> brittle, no orchestration [reject]
B Scheduler + Workflows -> serverless, built-in retries, minimal ops [ACCEPT]
Step-by-step trace.
- Constraint keywords: "three dependent steps," "once a day," "retries," "minimal operational overhead," "no complex DAG."
- A (Composer) is the right tool for complex DAGs but runs a managed cluster with baseline cost — overkill for a 3-step chain — eliminate on ops/cost.
- C is a self-managed VM (patching, uptime, no managed retries) — the opposite of minimal overhead — eliminate.
- D hand-rolls orchestration and timing — brittle and unobservable — eliminate.
- B uses Cloud Scheduler (managed cron) to kick a serverless Workflow that sequences the steps with built-in retry/error handling — zero clusters, minimal ops.
Output:
| Requirement | Winner |
|---|---|
| Simple daily chain | Workflows |
| Scheduled trigger | Cloud Scheduler |
| Retries, low ops | Serverless, managed |
Why this works — concept by concept:
- Right-size the orchestrator — Composer is correct for complex DAGs, but for a short serverless chain, Workflows wins the "minimal operational overhead" tiebreaker because it has no cluster.
- Managed cron — Cloud Scheduler is the standard, low-ops way to trigger on a schedule and pairs naturally with Workflows/Pub/Sub.
- Serverless retries — Workflows' built-in error handling gives you reliability without writing or operating retry infrastructure.
- Cost — no idle cluster; you pay per execution, which is both cheapest and lowest-ops for infrequent, simple pipelines.
ETL
Topic — etl
Orchestration and reliability problems
Design
Course — ETL system design
ETL system design for data engineering interviews
Cheat sheet — PDE service-selection recipes
Keyword → service lookup (memorise this table).
| Scenario keyword | Service answer |
|---|---|
| Real-time ingest, millions/sec, global | Pub/Sub (buffer) |
| Streaming transform, windows, late data, no-ops | Dataflow (Beam) |
| Reuse existing Spark/Hadoop code | Dataproc |
| No-code / visual ETL | Data Fusion / Dataflow templates |
| Ad-hoc SQL analytics at PB scale | BigQuery |
| 10 ms single-row reads, 1M writes/sec | Bigtable |
| Global, strongly-consistent relational + txn | Spanner |
| Regional managed MySQL/Postgres OLTP | Cloud SQL |
| Objects, data lake, archive | Cloud Storage (+ lifecycle) |
| Mobile/web document store, real-time sync | Firestore |
| In-warehouse SQL transforms as code | Dataform / dbt |
| Standard model on data already in BQ | BigQuery ML |
| Custom/deep-learning model, MLOps | Vertex AI |
| Governed self-serve metrics | Looker |
| Complex DAG, retries, backfills | Cloud Composer |
| Simple serverless step-chain | Workflows |
| Schedule/cron trigger | Cloud Scheduler |
BigQuery cost-control checklist.
- Partition by date/timestamp; cluster by up to 4 filter/group columns.
- Never
SELECT *on wide tables — select only needed columns. - Materialized views for repeated aggregations; BI Engine for dashboards.
- On-demand (per-byte) for spiky/ad-hoc; reservations/editions for steady heavy load.
- Set
maximum_bytes_billedguardrails and partition expiration.
Dataflow vs Dataproc decision line. New pipeline or streaming with windows/no-ops → Dataflow. Existing Spark/Hadoop to migrate or Spark-skilled team → Dataproc (serverless Dataproc if you want less cluster management).
6–8 week study plan recap. Weeks 1–2 ingest/process → 3–4 storage → 5 analyze+governance → 6 maintain/automate → 7–8 timed practice exams to ≥80%.
Exam-day elimination heuristic. Read the last sentence → name the constraint → eliminate the two obviously-wrong options → decide the last two on cost vs operational overhead → flag hard ones and move on.
Frequently asked questions
Is the GCP Professional Data Engineer certification worth it in 2026?
For data engineers working on (or moving to) Google Cloud, yes — it is one of the more respected cloud data credentials and directly signals the service-selection judgment employers pay for. It carries the most weight when paired with hands-on GCP experience; as a pure paper credential with no practical exposure it helps less. The gcp data engineering certification is especially valuable for consultants, those targeting GCP-heavy shops, and engineers wanting a structured reason to learn BigQuery, Dataflow, and the rest of the stack deeply.
How long does it take to prepare for the PDE exam?
For someone with 1–2 years of general data engineering experience and some GCP exposure, a focused 6–8 weeks at ~8 hours/week is a realistic plan. If you are new to Google Cloud specifically, budget 10–12 weeks and spend more of it hands-on. The single best predictor of readiness is scoring consistently ~80%+ on full-length, good-quality practice exams — not hours logged reading.
What's the passing score for the PDE exam?
Google does not publish a numeric passing score and gives you only a pass/fail result — no percentage. Because you cannot calibrate against an official cut line, use quality practice exams as your gauge and aim to clear ~80% comfortably before booking. Focus your final week on understanding why the right answer beats the runner-up on cost or operational overhead.
Do I need to know Apache Beam / Dataflow code for the exam?
You do not have to write production Beam code, but you must understand Dataflow concepts: the unified batch/streaming model, windowing (fixed, sliding, session), watermarks, triggers, and allowed lateness for late data. Scenarios will describe a late-data or per-session requirement and expect you to map it to the right windowing configuration — recognising the pattern matters far more than syntax recall.
PDE vs AWS DEA-C01 vs Azure DP-700 — which should I take?
Take the one that matches your cloud. If your company runs on GCP, the PDE is the highest-ROI choice; on AWS, take the Data Engineer Associate (DEA-C01); on Azure/Fabric, take DP-700. All three test the same underlying judgment — pick the right managed service under a constraint — just with different service names. If you are cloud-agnostic and choosing for breadth, PDE and DEA-C01 are the two most recognised data-engineering-specific exams.
Is the PDE exam multiple choice or hands-on?
It is multiple-choice and multiple-select scenario questions, delivered at a test centre or via online proctoring — there is no live hands-on lab. That said, the questions are written from operational experience, so hands-on practice (building one pipeline, one Bigtable schema, one partitioned BigQuery table) is the most efficient way to answer them correctly, because you will have seen the failure modes the distractors are built around.
Practice on PipeCode
Turn the PDE blueprint into muscle memory
Study guides explain the services. PipeCode drills build the reflex the exam actually tests — reading the constraint, eliminating the two wrong services, and defending the cost-vs-operational-overhead tiebreaker under a clock. Pipecode.ai is Leetcode for Data Engineering — scenario-first practice on SQL, ETL, and streaming tuned to the trade-offs the Professional Data Engineer exam rewards.





Top comments (0)