PCA Practice Questions
This page is the PCA question bank — twenty-four single-best-answer questions, split across the five official domains in roughly the same proportion the CNCF's blueprint weights them, each with every option worked through in the answer key rather than a bare correct letter. It sits between two other pages on this ladder rung: read PCA — the exam first if you haven't, since the questions below assume you already know what an instant vector is, what rate() actually does to a counter, and why a histogram and a summary answer completely different questions. Once this bank stops surprising you, move on to the timed, full-length PCA Mock Exam · Set 1 and Set 2. Every question here is original content written against the published PCA competencies — none of it is drawn from, or claims to reproduce, the real proctored exam.
Remember Ellie's notepad-nurse from the exam page — the one who writes everyone's temperature down every fifteen seconds? This page is twenty-four riddles about that notepad. Each riddle gives you a scenario and four possible answers, and only one of them is truly right — the other three were written on purpose because they're exactly the kind of mistake someone half-paying-attention actually makes: mixing up the wrong tool, forgetting one small step, or answering a slightly different question than the one that was asked. Cover the answers with your hand, guess first, then peek — and more importantly, read why the other three were wrong. Getting one wrong here costs nothing and teaches you something real. Getting the same one wrong twice means it's time to stop skimming and actually go reread that part of the exam page.
How this bank works
☺ Like you're 10: Cover the four answers with your hand, guess first, then look — and if a question fools you twice, that's the one to actually go study.
Every question below has a stem (the scenario or the exact thing being asked), four options, and exactly one key. The other three are not padding — each is built from a real misconception someone genuinely holds about Prometheus: a function applied to the wrong metric type, a step that sounds plausible but breaks a specific rule, a true fact that answers a different question than the one asked, or an "always" stated one qualifier too strongly. That is how CNCF-style multiple-choice items are actually constructed, and it's why "I recognised something true" is not the same skill as "I found the one thing that answers this exact stem" — see the PCA blueprint for how the real exam is built the same way.
The five piles, sized like the blueprint
Twenty-four questions split five ways can't hit 28/20/18/18/16 exactly, but they land close, and in the same rank order — PromQL alone is nearly a third of the bank, exactly as it's nearly a third of the real exam:
Reframe every question from "which of these is a true statement?" to "which of these answers this exact stem?" More than one option is often defensible on its own — the whole skill PCA is testing is picking the one that answers this question, not just any true thing you happen to know about Prometheus.
Every question below was written for this course, mapped against the CNCF's published PCA competencies — the same 26 competencies tabulated on the PCA blueprint page. None of it is drawn from, or claims to reproduce, the real proctored exam, which the Linux Foundation does not release publicly. Getting every one of these right tells you that you know the domain — it is not a guarantee of the real paper's exact difficulty, phrasing or coverage. Always verify price, timing and pass mark on the official Linux Foundation page before you book.
PromQL — questions 1–7 (28% of the blueprint)
☺ Like you're 10: This pile is about the exact words you use to ask the notepad a question — get one word wrong and it hears a completely different question.
-
Which PromQL expression selects only the
http_requests_totalseries forjob="checkout"whosecodelabel starts with a5— matching every 500-series status without listing each value individually?http_requests_total{job="checkout", code!="5.."}http_requests_total{job="checkout", code=~"5.."}http_requests_total{job="checkout", code=="5.."}http_requests_total{job="checkout", code>500}
Show answer & explanation
Answer: B.
=~is the regex-match operator, and5..as a regex means "5, then any two characters" — exactly every 500-series code. A's!=is negated equality, so it would exclude only the literal string"5..", matching almost everything else instead. C uses==, which isn't a PromQL label-matching operator at all. D tries a numeric comparison on a label, but label matchers work on the label's string value, not as an inequality against a number. -
Which of these four expressions correctly computes the total per-second request rate across every instance of the
checkoutjob, summed together?sum(rate(http_requests_total{job="checkout"}[5m]))rate(sum(http_requests_total{job="checkout"})[5m])avg(http_requests_total{job="checkout"})increase(http_requests_total{job="checkout"}[5m]) / 5
Show answer & explanation
Answer: A.
rate()has to run on a counter range vector, per series, so it can correctly handle each series' own resets — sum second. B tries to wrap an already-aggregated instant vector in a range selector, which both breaks the reset handling and isn't valid syntax to begin with. C'savg()never turns a raw, ever-growing counter into a rate at all. D reinvents rate badly: it divides a 5-minute increase by the literal number5instead of by 300 seconds, so the units come out wrong even though the shape looks plausible. -
Which function takes a range vector and returns the lowest value seen for each series across that window, collapsing it back down to one instant-vector value per series?
min_over_time()bottomk()delta()min()
Show answer & explanation
Answer: A.
min_over_time()is exactly the "aggregating over time" family — it walks the samples inside one series' window and returns the minimum.bottomk()(B) andmin()(D) are "aggregating over dimensions" operators instead: they compare across different series at a single instant, and both need an instant vector input, not a range vector.delta()(C) does take a range vector, but it returns the difference between the first and last value, not the minimum across the window. -
topk(3, sum by (pod) (rate(container_cpu_usage_seconds_total[5m])))is meant to show the three busiest pods by CPU usage. A teammate accidentally rewrites the inner aggregation assum without (pod) (...). What actually happens?- The query behaves identically —
by (pod)andwithout (pod)mean the same thing here - The query fails to parse, since
withoutcannot be used insidesum topksilently ignores the change and still ranks the result by pod correctly- The result now has one series per everything except
pod, sincewithoutdrops the named label instead of keeping it — likely collapsing every pod into a single combined series
Show answer & explanation
Answer: D.
by (label)keeps only the labels you name;without (label)keeps every label except the ones you name. Swappingby (pod)forwithout (pod)throws away exactly the label the whole query needs to rank on, so instead of one series per pod, everything else stays and pod identity is gone — the opposite of whattopkneeds to do its job. Nothing here is a parse error (B), and the behaviour is very much not identical (A) or silently self-correcting (C). - The query behaves identically —
-
This expression is meant to compute the fraction of
checkoutrequests that were errors, split out by response code, but it returns completely empty:sum by (job, code) (rate(http_requests_total{job="checkout"}[5m])) / sum by (job) (rate(http_requests_total{job="checkout"}[5m]))Why does it return nothing, even though both halves individually return data?
rate()cannot be divided by anotherrate()-derived expressionsum bycannot be used inside a binary operator- The left side's series carry a
codelabel that the right side's series don't, and PromQL's default vector matching requires an identical label set on both sides, so no pair ever matches job="checkout"needs to be repeated a second time on the right-hand side of the division
Show answer & explanation
Answer: C. By default, PromQL binary operators try to match each left-side series to exactly one right-side series with the same label set. The left side kept
code; the right side dropped it. No right-side series has that extracodelabel, so every left-side series fails to find a match, and the whole result is empty. A and B both describe restrictions that don't exist in PromQL. D is a specific, plausible-looking guess, butjob="checkout"is already applied inside bothrate()calls — the filter was never the problem. -
Which expression correctly computes the 95th-percentile
checkoutrequest duration in seconds, aggregated across every instance of the job?histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket{job="checkout"}[5m])))quantile(0.95, http_request_duration_seconds{job="checkout"})avg(http_request_duration_seconds_sum{job="checkout"} / http_request_duration_seconds_count{job="checkout"})histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job="checkout"}[5m])))
Show answer & explanation
Answer: A.
histogram_quantile()reads the cumulative_bucketseries, and it needs thelelabel kept through the aggregation to know which bucket boundary each row belongs to. D is the single most common way this breaks in real dashboards: it's identical to the correct answer exceptsum(...)has noby (le), so the bucket boundaries get collapsed away and the function has nothing left to work with. B callsquantile(), an aggregation operator for plain instant vectors of raw sample values — it doesn't know what a histogram bucket is. C computes something real, the mean duration from_sumdivided by_count— a legitimate metric, just not a p95. -
A batch job writes its own success time as a gauge,
platform_backup_last_success_timestamp_seconds, holding a Unix timestamp. Which expression correctly returns the number of hours since the last successful backup?platform_backup_last_success_timestamp_seconds / 3600(time() - platform_backup_last_success_timestamp_seconds) / 3600rate(platform_backup_last_success_timestamp_seconds[1h])changes(platform_backup_last_success_timestamp_seconds[1h])
Show answer & explanation
Answer: B. This is the "timestamp metrics" pattern: subtract the recorded timestamp from
time(), the current evaluation time, to get an elapsed duration in seconds, then convert to hours. A just divides the raw Unix timestamp itself — a huge, meaningless number. C misappliesrate(), which is for ever-increasing counters, to a gauge that holds a point-in-time value; the result is not an "hours since" figure. D'schanges()counts how many times the value changed within the window, not how long ago the most recent change was.
Prometheus Fundamentals — questions 8–12 (20% of the blueprint)
☺ Like you're 10: This pile is about the notepad-nurse herself — how she walks her rounds, what she writes down, and what she flatly refuses to do.
-
Which statement correctly describes how Prometheus and Alertmanager relate to each other?
- Alertmanager is a module running inside the Prometheus server process itself
- Prometheus and Alertmanager both independently scrape the same targets and compare their results
- Alertmanager scrapes targets directly and pages Prometheus whenever a target looks unhealthy
- Prometheus evaluates alerting rules and forwards any that are firing to Alertmanager, a separate process responsible for grouping, silencing, inhibition and routing to receivers
Show answer & explanation
Answer: D. Prometheus owns rule evaluation; Alertmanager is a separate binary that only receives firing alerts and decides what to do with them — group them, silence them, suppress downstream noise, and route the survivors to a receiver. A misplaces Alertmanager's process boundary entirely. B and C both invert or duplicate the pull relationship — Alertmanager never scrapes anything.
-
In the scrape config below, what does the
action: keeprelabel rule actually do?relabel_configs: - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] action: keep regex: "true"- It runs after the scrape and deletes any resulting time series whose value doesn't match "true"
- It has no effect unless
metric_relabel_configsis also defined - It renames the annotation label to
keepon every series that gets stored - It runs before the scrape and drops any discovered target whose matching annotation isn't exactly "true", so opted-out pods are never scraped at all
Show answer & explanation
Answer: D.
relabel_configsruns before the scrape, against the target list service discovery produced —keep/drophere decide which discovered targets get scraped at all. A describes whatmetric_relabel_configsdoes instead, which runs after the scrape against the resulting series — a different config block entirely, and a classic mixed-up-stage distractor. C invents a renaming behaviourkeepdoesn't have. B is false; this block is fully functional on its own. -
A finance team wants to use Prometheus as the system of record for customer billing, arguing "it's already collecting all our usage metrics." Which limitation makes this the wrong tool for that job?
- Prometheus cannot store counters, only gauges
- Prometheus samples on a scrape interval rather than recording every individual event, so it can't guarantee the exact, complete count billing requires — and it isn't built for long-term, high-durability retention on its own either
- PromQL cannot perform division, so a per-unit price could never be calculated
- Prometheus requires a paid license for any production use beyond internal monitoring
Show answer & explanation
Answer: B. This is exactly what "Understanding Prometheus Limitations" is testing: Prometheus samples at an interval, it isn't designed as a durable, long-term source of truth on its own, and it does not give billing-grade accuracy. A is backwards — counters are one of its core metric types. C is false; PromQL supports arithmetic freely. D is a fabrication; Prometheus is open source with no such licensing gate.
-
A team adds a
request_idlabel to a counter so they can trace individual requests in Prometheus. What is the most likely operational consequence?- A cardinality explosion — every distinct
request_idvalue creates a brand-new time series, and with enough unique requests this can exhaust memory and degrade or crash the server - Nothing meaningful changes; Prometheus stores every label combination equally cheaply
- Prometheus automatically drops any label with more than a fixed number of distinct values, so no real harm is done
- The counter silently converts into a histogram to accommodate the extra label
Show answer & explanation
Answer: A. A time series is a metric name plus its full label set, and every unique combination is its own series. An unbounded, per-request identifier used as a label multiplies the series count by every request that's ever happened — the textbook cardinality explosion. B ignores that cost entirely. C and D both describe protective mechanisms Prometheus does not have; nothing catches this automatically.
- A cardinality explosion — every distinct
-
Given this exposition-format snippet:
# HELP http_requests_total Total HTTP requests served. # TYPE http_requests_total counter http_requests_total{code="200",method="get"} 42981What does the
# TYPEcomment line communicate, and to whom?- It's purely documentation for a human reading the raw output; Prometheus itself ignores it entirely
- It determines which network port the exporter listens on
- It sets the metric's initial value before any samples are recorded
- It tells Prometheus how to interpret and query the metric — for example, that this one is a counter, so
rate()andincrease()are the appropriate functions rather than reading the raw value directly
Show answer & explanation
Answer: D. The
TYPEline is machine-readable metadata that Prometheus itself parses and stores — it's how the UI, client tooling and query guidance know a given metric is a counter, gauge, histogram or summary. A wrongly assumes it's cosmetic. C and B each invent a job the comment doesn't do; initial values and network ports are configuration concerns, not exposition-format metadata.
Observability Concepts — questions 13–16 (18% of the blueprint)
☺ Like you're 10: This pile steps back from Prometheus specifically — it's about the bigger idea of watching a system at all, in more ways than one.
-
A batch ETL job runs for 90 seconds every night and then exits completely. Prometheus's normal pull-based scraping would never catch it while it's running. What's the correct way to get its final metric values into Prometheus?
- Configure the job's container to push its metrics directly to the Prometheus server's ingestion API
- Lower the Prometheus scrape interval to a few seconds so it's guaranteed to catch the job mid-run
- Have the job push its final values to the Pushgateway before it exits, and let Prometheus scrape the Pushgateway on its normal schedule
- Nothing can be done; Prometheus fundamentally cannot monitor short-lived jobs
Show answer & explanation
Answer: C. This is precisely the Pushgateway's narrow, deliberate purpose: a short-lived job pushes once, and the Pushgateway holds those values as a normal, pull-friendly target until Prometheus scrapes it on schedule. A invents an ingestion API Prometheus doesn't have — it is a pull system, full stop. B is unreliable and wasteful even if it happened to work occasionally. D overstates the real limitation; there's a purpose-built answer, it's just narrow.
-
In a Kubernetes cluster where pods are constantly created and destroyed by deployments and autoscaling, why does Prometheus use service discovery instead of a static list of scrape targets?
- Static target lists are forbidden by the Prometheus configuration schema
- Pods don't have stable, predictable IP addresses, so Prometheus asks the Kubernetes API what currently exists and continuously re-derives its target list, rather than relying on a list that would go stale within minutes
- Service discovery is only needed for exporters, never for directly instrumented applications
- Kubernetes requires every scraped target to be registered through its own built-in Prometheus operator
Show answer & explanation
Answer: B. This is the whole reason the pull model scales in a dynamic environment: rather than someone hand-maintaining IPs, Prometheus queries the API server and keeps its target list current on its own. A is false — static configs remain entirely valid, just impractical here. C draws a distinction that doesn't exist; any target's lifecycle can churn, instrumented or not. D names a real, common tool (the Prometheus Operator) but overstates it as a hard requirement, which it isn't.
-
A checkout request is slow, and an engineer needs to see exactly which downstream service call inside that one request added the delay. Metrics show the aggregate latency went up; logs show individual error lines. Which observability signal is actually built to show the causal chain of calls within a single request?
- Metrics, because
histogram_quantilecan isolate a single request's path - Logs, because every service already writes a line per request
- A distributed trace, made of spans — each span representing one unit of work in the request's path, connected together into a trace that shows exactly where the time went
- Alerts, because Alertmanager already groups related events together
Show answer & explanation
Answer: C. Metrics tell you that something got slower in aggregate; logs tell you what happened at discrete points; only a trace stitches one request's journey across every service it touched into a single causal timeline of spans, which is exactly what's needed to find where the time actually went. A misapplies an aggregate quantile function to a single-request question it was never built to answer. B and D each name a real, useful signal that simply isn't built for this specific job.
- Metrics, because
-
A team measures that checkout's success rate over the last 30 days was 99.95%. Their internal target is 99.9%, and their contractual promise to customers is 99.5%, with service credits owed below that. Which term describes the internal 99.9% target itself?
- SLI (Service Level Indicator) — the measurement itself
- Error budget — the allowed gap between the SLO and 100%
- SLA (Service Level Agreement) — the contractual promise, with consequences attached
- SLO (Service Level Objective) — the internal target
Show answer & explanation
Answer: D. The 99.95% actually measured is the SLI. The 99.9% internal goal the team is holding itself to is the SLO — option D. The 99.5% figure with consequences attached is the SLA (C), a different, looser number on purpose so the team has room to miss its own internal target without breaching the customer promise. The error budget (B) would be the gap between the SLO and perfect, 100% success — a related but distinct concept from the target itself.
Alerting & Dashboarding — questions 17–20 (18% of the blueprint)
☺ Like you're 10: This pile is about the part that actually wakes somebody up — and about not waking them up for nothing.
-
In the alerting rule below, what does the
for: 10mfield actually control?- alert: CheckoutHighErrorRate expr: job:http_request_errors:ratio_rate5m{job="checkout"} > 0.05 for: 10m- Alertmanager waits 10 minutes after the alert fires before actually paging anyone
- The expression must keep evaluating true continuously for 10 minutes before the alert moves from pending to firing — which stops a brief blip from paging anyone
- The alert automatically resolves itself after 10 minutes, whether or not the underlying condition is still true
- Prometheus only re-evaluates this specific rule once every 10 minutes
Show answer & explanation
Answer: B.
for:gates the transition from pending to firing: the condition has to hold continuously across that whole duration first. That's a deliberate defence against noisy, self-correcting blips — not a delay tacked onto Alertmanager (A), not a self-resolving timer (C), and unrelated to the rule's own evaluation interval, which is set separately (D). -
A single node outage causes 40 different alerting rules to fire simultaneously across every service running on it. Which Alertmanager feature exists specifically to stop this from becoming 40 separate pages, and instead deliver it as one coherent notification?
- Silences
- Inhibition
- Grouping
- Recording rules
Show answer & explanation
Answer: C. Grouping is exactly this: alerts that share labels get bundled into one notification instead of paging separately. Inhibition (B) is a related but distinct mechanism — it fully suppresses a downstream alert's notification once a parent alert is already firing, rather than bundling them together. Silences (A) mute known, expected alerts on a schedule a human sets, not a live storm. Recording rules (D) precompute PromQL expressions and have nothing to do with notification behaviour.
-
A new alerting rule fires whenever any node's CPU usage exceeds 80%, regardless of whether anything else is wrong. The on-call engineer gets paged nightly, and CPU is usually back to normal by the time they check. What alerting-philosophy mistake does this rule make?
- It alerts on a cause — raw CPU percentage — rather than a symptom users actually feel, like error rate or latency; 80% CPU is often completely healthy and not inherently actionable on its own
- The
for:duration is set too long - It should be converted into a recording rule instead of an alerting rule
- It's missing a
runbook_urlannotation, which is the only real problem with it
Show answer & explanation
Answer: A. This is the "when, what and why" competency in one scenario: alert on symptoms users actually feel, not on a resource number that's frequently fine. D names a genuine best practice — every page should carry a runbook — but it isn't the core mistake here; even with a perfect runbook, paging on cause rather than symptom is still the wrong call. B and C don't address the actual problem, which is what the rule alerts on, not its timing or its resource type.
-
A team builds one Grafana dashboard for checkout latency and wants to reuse the exact same dashboard for the payments and shipping services without duplicating panels. What Grafana mechanism is built for this?
- A template variable — for example
$job— referenced inside each panel's PromQL query, so switching the variable at the top of the dashboard re-points every panel at a different service - Duplicating the dashboard JSON once per service and manually editing each PromQL query
- Recording rules, which automatically generate one dashboard per label value
- Alertmanager routing, which can display metrics for multiple services on one page
Show answer & explanation
Answer: A. Template variables are exactly the "one dashboard, many services" mechanism: define
$jobonce, reference it in every panel's query, and switching the dropdown re-points the whole dashboard. B is the manual, duplication-heavy approach the feature exists to avoid. C invents a dashboard-generation capability recording rules don't have — they only precompute PromQL expressions. D confuses an alerting-routing concern with a dashboarding one. - A template variable — for example
Instrumentation and Exporters — questions 21–24 (16% of the blueprint)
☺ Like you're 10: This pile is about how numbers get onto the notepad in the first place — writing your own, or translating someone else's.
-
A developer instrumenting an HTTP handler wants to track request duration in a way that lets a fleet-wide p99 be computed later with
histogram_quantile(). Using a Prometheus client library, which metric type should they choose?- Gauge, set to the duration of the most recent request
- Counter, incremented once per request
- Histogram, with buckets chosen up front to match expected latencies
- Summary, since it calculates the p99 automatically
Show answer & explanation
Answer: C. A histogram's cumulative buckets are exactly what
histogram_quantile()aggregates across instances into a fleet-wide percentile. A gauge (A) overwrites its value on every request, discarding everything between scrapes. A counter (B) can only count occurrences, not durations. A summary (D) does compute a quantile, but it does so client-side, per instance — those quantiles cannot be aggregated across a fleet, which defeats the exact goal in the stem. -
Reviewing a pull request, you see a new counter instrumented like this:
REQUESTS = Counter( "http_requests_total", "Total HTTP requests served.", ["method", "code", "user_id"], )What's the problem with this label set, and what should change?
- Nothing — more labels always make a metric more useful
user_idshould be removed; it's unbounded and high-cardinality, and Prometheus labels should stay low-cardinality dimensions likemethodandcode, not per-entity identifiersmethodandcodeshould be removed, sinceuser_idalone is enough to identify the series- The metric needs a fourth label,
timestamp, since Prometheus doesn't otherwise know when each request happened
Show answer & explanation
Answer: B. This is the cardinality principle from the Data Model domain applied at instrumentation time: an unbounded identifier like
user_idas a label creates one new series per distinct user, which is exactly the failure mode to avoid. A ignores the real cost. C would remove the two labels that actually make the metric useful for dashboards and alerts. D is a fabrication — Prometheus already timestamps every sample it stores; that's not something a label needs to carry. -
A team needs Prometheus to monitor CPU, memory and disk usage on a fleet of plain Linux VMs that run no Prometheus-aware application code at all. What's the standard way to get that data into Prometheus?
- Write a custom Pushgateway client for each VM
- It isn't possible; Prometheus can only monitor applications with a client library built in
- Enable a hidden
--os-metricsflag on the Prometheus server itself - Run
node_exporteron each VM, which reads OS-level metrics and re-exposes them in the exposition format for Prometheus to scrape directly
Show answer & explanation
Answer: D. This is precisely what an exporter is for: translating a system that doesn't natively speak Prometheus — here, the Linux kernel's own counters — into the exposition format, so it can be scraped like any other target. A misapplies the Pushgateway, which exists for short-lived jobs, not always-on machine metrics. C invents a flag that doesn't exist. B is false; exporters are the standard answer to exactly this gap, no client library required.
-
Which metric name follows Prometheus naming convention correctly for a counter measuring the total number of bytes sent over the network?
network_bytesNetworkBytesSentTotalnetwork_bytes_sent_totalbytes.sent.total
Show answer & explanation
Answer: C. Convention: snake_case, a base-unit suffix (
_bytes), and counters end in_total—network_bytes_sent_totalsatisfies all three and describes what's measured rather than a dashboard panel. A drops both the_sentcontext and the_totalcounter suffix. B uses PascalCase, which isn't the Prometheus convention. D uses periods as separators — Prometheus metric names are restricted to letters, digits, underscores and colons, so this isn't even a valid metric name.
Turning a wrong answer into a fact
☺ Like you're 10: The score isn't the point. What matters is why you got one wrong — because "I never knew that" and "I knew it but misread the question" need completely different fixes.
Finishing the bank and noting a percentage teaches you almost nothing on its own. When you miss one, decide honestly which of two things happened. If you genuinely didn't know the fact, that's a content gap — go reread the matching section of PCA — the exam, and don't move on until you can restate it in your own words, out loud. If you knew the material but picked wrong anyway, that's almost always a misread stem or a distractor that got you on speed rather than knowledge — reread the exact wording of the question you missed before you touch the next one. Either way, questions you miss twice on a later pass are the ones actually worth writing down.
"Question 6 — the histogram_quantile one — I got wrong twice before it actually stuck. I kept reading sum(rate(x_bucket[5m])) and thinking 'looks right, ship it,' without ever noticing the missing by (le). It ran fine, it just quietly answered a different question than the one I meant to ask. Now I read every histogram_quantile query looking for that one clause first, before anything else."
Pick your two lowest-confidence questions from the bank above and actually build the scenario. Run Prometheus and node_exporter locally, or reuse the setup from the PCA blueprint's memory drill. If it was question 6: write the correct histogram_quantile query, confirm it returns a sane number, then delete by (le) on purpose and watch the result stop making sense. If it was question 18: fire two unrelated alerting rules for the same simulated outage and watch Alertmanager fold them into one grouped notification instead of two separate pages. A concept you've watched break once, on purpose, is very hard to get wrong on paper again.
If you want a rough, self-graded pass/fail signal: the Linux Foundation's published cut score for its multiple-choice exams, PCA included, is 75%. Scoring at or above that here, cold, across all 24, is a reasonable — though entirely unofficial — readiness signal before you book the timed mock exam.
Remy: Done! All twenty-four, five minutes flat.
Ellie: And?
Remy: …I don't actually know why C was right on the vector-matching one. I just recognised the shape of the query.
Ellie: Then you've answered zero of them so far, honestly. Speed without the reasoning underneath it is just a coin flip with extra confidence.
Gizmo: Easier trick — just alert on every metric you've got. Full coverage. Nothing gets past Gizmo! 🔔🔔🔔
Timmy: Question 19 says otherwise, Gizmo. Alert on symptoms users actually feel — everything else belongs on a dashboard, not in someone's pocket at 3am.
Remy: Fine. Back to the vector-matching question. Explaining it out loud until it actually makes sense this time.
1. How many questions does this bank hold in total, and roughly how are they split across the five PCA domains? 2. Which single domain gets the most questions here, and why does that match the real blueprint? 3. What single clause, if dropped from a sum wrapped around a histogram's rate, breaks histogram_quantile — and why? 4. Why can a division between two PromQL vectors return completely empty even when both halves have data on their own? 5. What is the Pushgateway actually for, and what is it not for? 6. What's the difference between Alertmanager's grouping and its inhibition? 7. When you miss a question, what's the very next thing you should do before moving to the next one?
Check your answers
- 24 questions — 7 PromQL, 5 Prometheus Fundamentals, 4 Observability Concepts, 4 Alerting & Dashboarding, 4 Instrumentation and Exporters, tracking the blueprint's 28/20/18/18/16 split in rank order if not in exact percentage.
- PromQL — because it's the single largest domain on the real blueprint at 28%, ahead of every other domain individually.
by (le). Without it, the aggregation collapses the bucket-boundary label away, andhistogram_quantilehas no way to tell which cumulative bucket any remaining row belongs to.- Because PromQL's default vector matching requires both sides of a binary operator to carry the same label set — if one side has an extra label the other side dropped, no pair of series matches, and the whole result is empty.
- Short-lived batch jobs that finish and exit before Prometheus can ever scrape them. It is not a general push endpoint — Prometheus itself remains a pull system, and Pushgateway is a narrow, deliberate exception.
- Grouping bundles multiple firing alerts that share labels into a single notification. Inhibition fully suppresses a downstream alert's notification once a related parent alert is already firing — different mechanisms solving a similar noise problem in different ways.
- Reread the exact wording of the question you missed before moving on — most misses on material you actually know come from a misread stem or a distractor that won on speed, not from a real content gap, and you can only tell the difference by rereading immediately.