PCA Mock Exam · Set 2
The second of two PCA papers on this course, sitting on the exact same forty-five-question, five-domain split as Set 1 — thirteen PromQL, nine Prometheus Fundamentals, eight each on Observability Concepts and Alerting & Dashboarding, seven on Instrumentation and Exporters — so your two scores are directly comparable. The difference is shape, not size. Set 1 tested whether you could name a definition or read one function cold; this set drops you into a scenario — an engineer's query, a dashboard behaving oddly, two proposed configs — and asks you to reason through two or three clues layered together before picking an answer, the way the real associate exam tends to phrase its harder items. Sit this one after Set 1 is solid, not before: it assumes the vocabulary and reaches straight for how it gets misapplied in practice.
Set 1 asked "what's this called?" This set asks "here's a small story about something going wrong — now figure out why." Both are still just picking the best of four answers and reading the explanation right after, but this time the question usually gives you two or three clues stitched together, and you have to notice which clue actually matters. It's harder in the way a word problem is harder than a times-table question, even though it's testing the exact same math underneath.
How this paper is weighted
☺ Like you're 10: Same five topics, same number of questions per topic as last time — only the questions themselves got trickier.
Set 2 keeps Set 1's exact domain split — 13 · 9 · 8 · 8 · 7 across PromQL, Prometheus Fundamentals, Observability Concepts, Alerting & Dashboarding, and Instrumentation and Exporters — because the whole point of a second paper is a clean, apples-to-apples comparison against your first score. What changes is how each question is built. Instead of "what does this one function do," expect "an engineer wrote this query/config/alert for this reason — which of these explanations of what actually happens is correct." Several questions present two superficially similar approaches side by side and ask you to spot the one genuine difference between them, which is exactly the skill a knowledge-only, no-terminal exam has to lean on to test whether you can really use PromQL rather than recognize it.
| Domain | Official weight | Questions here | Where to revise |
|---|---|---|---|
| 🐘 PromQL | 28% | 13 | PCA — the exam · Prometheus |
| 🦉 Prometheus Fundamentals | 20% | 9 | Prometheus · PCA — the exam |
| 🐿️ Observability Concepts | 18% | 8 | The OpenTelemetry data model |
| 🐢 Alerting & Dashboarding | 18% | 8 | Grafana · PCA — the exam |
| 🦫 Instrumentation and Exporters | 16% | 7 | OpenTelemetry Collector |
On a scenario-heavy paper, wrong answers usually aren't false in isolation — they're true statements that answer a slightly different question than the one asked. Before picking, ask yourself what specific claim each option is actually making, not just whether it sounds like something you've read before. That habit is worth more on this set than on Set 1.
Sit it like the real thing
☺ Like you're 10: Same rules as last time — no notes, one sitting, answer every question even the ones you're unsure of.
Nothing about exam conditions changes for a second paper. The PCA is delivered online and remote-proctored, closed-book, with no cluster and no documentation allowance — sit this the same way you sat Set 1: one uninterrupted block, every other tab closed, an answer committed to every item before you check it. If you're taking both papers back to back, take a real break between them; a scenario-heavy set read while tired is where careless misreads happen, and those misreads teach you the wrong lesson about what you actually know.
What's safe to state structurally: the PCA is a CNCF/Linux Foundation associate-level, knowledge-based multiple-choice exam — not hands-on like the five core Kubernetes exams in the Kubernetes course or a performance-based exam like the CNPE — delivered online under remote proctoring, with no formal prerequisite. Two numbers the Linux Foundation does publish and this paper is calibrated against: a 90-minute duration, and 75% or above to pass (the Linux Foundation's Multiple Choice Exam FAQ states this pass mark applies to every LF multiple-choice exam). The real question count is not published, and this paper's size of forty-five exists only to match Set 1 for comparability, not to claim a real number. Price, retake terms, eligibility window and certification validity are all revised over time too. Confirm everything on the official Linux Foundation PCA page and the CNCF certification page before you register or pay — this page is a study aid, not a substitute for reading them.
The paper — 45 questions across three blocks
☺ Like you're 10: Same three blocks as before — read the whole little story in each question before you pick, because the important detail is rarely the first thing mentioned.
Each question names its domain in parentheses; a couple are marked (Select TWO) and need both correct letters for full credit. Where a question compares two approaches, read both all the way through before deciding — several of the wrong options here are correct-sounding statements about the wrong one of the two.
Block 1 — Q1–15
Q1 (PromQL). A platform team wants to attach the owning team's name to every raw per-pod request-rate series, without first collapsing pods down to one series per namespace. They write:rate(http_requests_total{namespace!=""}[5m]) * on(namespace) group_left(team) kube_namespace_labels
Every namespace has anywhere from one to dozens of pods, but exactly one kube_namespace_labels series. Why does this query need group_left, rather than just on(namespace) alone?
- A.
on(namespace)alone matches the many per-pod series on the left against the singlekube_namespace_labelsseries on the right — a many-to-one relationship PromQL refuses to resolve implicitly, since it can't tell that duplication is intentional rather than a labelling mistake;group_leftboth permits it and copies theteamlabel onto the result - B.
on()can only be used when both sides have exactly one series total, andgroup_leftis what allows more than one series per side to exist at all - C.
group_lefthas no effect on matching cardinality — it only controls the sort order of the output - D.
kube_namespace_labelsis itself many-to-many on its own, andgroup_leftresolves ties between duplicate rows by keeping whichever is encountered first
Check the answer
A. Without group_left, PromQL raises "many-to-one matching must be explicit" precisely because many left-hand series sharing a namespace would otherwise silently collapse onto one right-hand row. group_left(team) both grants that permission and pulls the extra team label from the "one" side onto every matched result.
Q2 (Prometheus Fundamentals). A team sets scrape_interval: 10s and scrape_timeout: 15s for a slow legacy endpoint in one scrape config, and Prometheus refuses to start with a configuration error. What's wrong?
- A.
scrape_timeoutmust always equalscrape_intervalexactly - B.
scrape_timeoutmust be less than or equal toscrape_interval— a scrape allowed to run longer than the gap before the next one is due doesn't make sense, so Prometheus rejects the config outright - C.
scrape_timeoutcan only be set globally, never per scrape config, and this job-level value is what's invalid - D. There's no real maximum for
scrape_timeout; the actual problem is that10sis too short ascrape_intervalto be legal
Check the answer
B. Prometheus validates that scrape_timeout cannot exceed scrape_interval, because a scrape permitted to run longer than the interval between scrapes would routinely overlap the next one. The fix here is either raising scrape_interval or lowering scrape_timeout, not removing either setting.
Q3 (Observability Concepts). A checkout-service dashboard shows four panels: requests per second, an error-rate percentage, p50/p95/p99 latency, and CPU/memory/queue-depth against capacity. Which mapping to the four "golden signals" is correct?
- A. Traffic = requests/sec; Latency = the percentile panels; Errors = the error-rate percentage; Saturation = the CPU/memory/queue-depth panels
- B. Latency = requests/sec; every other panel is a form of "errors"
- C. Golden signals only apply to network-device dashboards, not application-level ones like this
- D. Saturation and traffic are simply the same signal measured two different ways
Check the answer
A. The four golden signals map cleanly here: traffic is volume (requests/sec), latency is how long requests take (the percentiles), errors is the failure rate, and saturation is how full the system's resources are (CPU/memory/queue depth against their limits) — four genuinely distinct questions, not restatements of each other.
Q4 (Alerting & Dashboarding). A team's Alertmanager route sends everything to a default "platform-oncall" receiver, with a child route sending severity="page" alerts to PagerDuty. They also want every page-severity alert to land in a shared "#incidents" Slack channel in addition to PagerDuty, not instead of it. What config detail makes that happen?
- A. Nothing extra — Alertmanager notifies every matching route by default, since routing isn't first-match-wins
- B. Setting
continue: trueon the PagerDuty child route, so Alertmanager keeps evaluating sibling and parent routes after this one matches instead of stopping there - C. Listing the same receiver name under two separate
matchblocks - D.
group_by, which is what actually controls whether multiple receivers are notified
Check the answer
B. Routing normally stops at the first matching route. continue: true is the specific setting that tells Alertmanager to keep walking the tree after a match, which is how one alert ends up notifying more than one receiver — here, both PagerDuty and Slack.
Q5 (Instrumentation and Exporters). A team needs metrics from a 15-year-old commercial database with no source access and no way to embed a Prometheus client library inside it. What's the correct category of tool, and how does it conceptually differ from instrumenting a service you own?
- A. A purpose-built exporter — a separate process that queries the database through whatever interface it already exposes and translates the result into the exposition format; a client library, by contrast, is linked directly into code you control and instruments it from the inside
- B. The Pushgateway, since it's designed for exactly this kind of external system
- C. Client libraries can be attached to any running process without source access, so this is actually a client-library use case
- D. There is no standard tool for this; it requires modifying the Prometheus server itself
Check the answer
A. This is precisely what an exporter is for — a small, separate translator process, not code linked into the target system. The Pushgateway is a narrow exception for short-lived batch jobs pushing their own results in, not a general adapter for long-running external systems like a database.
Q6 (PromQL). An engineer wants to compare this Friday's error rate to the same time last Friday, for anomaly detection:sum(rate(http_requests_total{code=~"5.."}[5m])) / sum(rate(http_requests_total{code=~"5.."}[5m] offset 7d))
What does offset 7d achieve here, and is it placed correctly?
- A. It shifts the whole expression's evaluation time back 7 days and must wrap the entire query:
(...)[offset 7d] - B. It shifts the time at which the vector selector it's attached to is evaluated, back by the given duration — attached directly after the
[5m]range here, it correctly evaluates that same 5-minute window as it stood 7 days ago, giving a genuine week-over-week comparison - C.
offsetonly works on instant vectors, never inside a range-vector function likerate(), making this invalid syntax - D.
offsetchanges the query's step interval, not its evaluation time
Check the answer
B. offset attaches directly to a vector selector — instant or range — and shifts when that selector is evaluated. Placed right after [5m], it correctly pulls the same 5-minute window from 7 days earlier, which is exactly the shape a week-over-week comparison needs.
Q7 (Prometheus Fundamentals). A target exposes its own instance label in its metrics payload, meaningful to the application and distinct from the automatic instance label Prometheus derives from the scrape target's address. The team is confused why their custom value keeps getting overwritten. Which setting controls this, and what's the default?
- A.
honor_labels: truelets the label as exposed by the target win over Prometheus's own automatically-added target labels on conflict; the default,honor_labels: false, does the opposite — Prometheus's label wins and the conflicting scraped one is renamed with anexported_prefix - B.
honor_timestampsis what controls this, nothonor_labels - C. There's no way to preserve a target's own conflicting label; it's always overwritten unconditionally
- D.
honor_labelsonly affects the reserved__name__label, never ordinary labels
Check the answer
A. With the default honor_labels: false, Prometheus's own target labels take precedence and any conflicting label from the target is preserved under an exported_ prefix instead of being dropped. Setting honor_labels: true flips that precedence so the scraped value wins outright.
Q8 (Observability Concepts). A team writes their SLI as sum(rate(http_requests_total{code!~"5.."}[5m])) / sum(rate(http_requests_total[5m])), sets an internal SLO of 99.5% over a rolling 30-day window, and separately signs a customer contract at 99.0% with financial penalties for any shortfall. Which of the three is the SLI, precisely?
- A. The 99.0% figure
- B. The 99.5% figure
- C. The PromQL ratio expression itself — the actual measurement being computed — not a percentage target on its own, but the thing compared against a target
- D. The 30-day rolling window
Check the answer
C. The SLI is the indicator — the measurement mechanism itself, here the success-ratio query. The 99.5% is the SLO (the internal target set against that indicator) and the 99.0% is the SLA (the external, contractual promise). Neither percentage is the SLI; they're targets applied to it.
Q9 (Alerting & Dashboarding). A route has group_wait: 30s, group_interval: 5m, repeat_interval: 4h. Three related alerts fire in the same new group: at T+0, T+10s, and T+35s. Roughly what happens?
- A. Alertmanager waits
group_wait(30s) from the first alert before the initial notification, batching in anything that arrives within that window — so the T+10s alert makes the first notification, while the T+35s alert (arriving after that first send) waits for the nextgroup_intervalbatch 5 minutes later; after that, the still-firing group is only re-notified everyrepeat_interval(4h) unless something changes sooner - B. All three alerts are sent as three separate notifications immediately, regardless of these settings
- C.
group_wait,group_intervalandrepeat_intervalare three names for the identical timer - D.
repeat_intervalcontrols how long Alertmanager waits before the very first notification of a new group
Check the answer
A. group_wait governs the initial batching delay for a brand-new group; group_interval governs how often a still-active group's later-arriving members get flushed; repeat_interval governs how often an unchanged, still-firing group gets re-notified. All three do genuinely different jobs.
Q10 (Instrumentation and Exporters). Two label designs for an HTTP metric are proposed: raw numeric status codes (200, 201, 301, 404, 500, 503…) versus a collapsed class (2xx, 3xx, 4xx, 5xx). Both are technically bounded — HTTP codes are a small, finite set either way. Which is the more common safe default, and why?
- A. Raw codes are strictly better because they never lose information
- B. Both are equally recommended in every case, with no real trade-off
- C. The collapsed class is the more common default because most dashboards and alerts only need the class-level distinction, and it keeps series count small and stable regardless of how many distinct codes a service happens to emit; raw codes remain legitimate and still bounded when the exact code genuinely matters for debugging, but they aren't needed for most alerting
- D. Raw status codes aren't actually bounded, because new HTTP status codes are added too frequently to track
Check the answer
C. Both designs are cardinality-safe in principle, so this isn't really a cardinality question — it's a "what does the consumer actually need" question, and the class-level label wins by default because success/client-error/server-error covers most decisions without the churn of tracking every individual code.
Q11 (PromQL). A nightly backup job pushes a success counter through the Pushgateway only when it completes; if it crashes early, nothing is pushed at all — the series is entirely absent, not zero. The team wants to page if the expected metric hasn't appeared in the last 25 hours. Which expression correctly captures "this metric produced no series at all in the last 25 hours"?
- A.
platform_backup_success_total == 0 - B.
absent_over_time(platform_backup_success_total[25h]) - C.
increase(platform_backup_success_total[25h]) == 0 - D.
rate(platform_backup_success_total[25h]) < 1
Check the answer
B. absent_over_time() is purpose-built for exactly this: it returns a 1 when its range vector had no samples at all in the window, and no result otherwise. Options C and D fail the same way — increase() and rate() on a range with zero samples return no data, not 0, so neither ever crosses the stated threshold.
Q12 (Prometheus Fundamentals). A Prometheus pod is OOM-killed mid-write. On restart, the operator worries the last few minutes of samples — held in memory but not yet flushed to a persisted block — were lost. What protects against this, and what exactly does it protect?
- A. The WAL (write-ahead log) — every sample is appended there before being held in memory, so on restart Prometheus replays the WAL to reconstruct the in-memory chunks that hadn't yet reached a persisted block
- B. Prometheus automatically snapshots the entire TSDB to a remote object store, with no configuration needed
- C. There's no crash protection; anything not yet in a block since the last 2-hour compaction is always lost
- D. The WAL only protects recording-rule results, not raw scraped samples
Check the answer
A. The WAL is exactly this safety net: samples are durably appended to it before they're only-in-memory, so a crash before the next block persists doesn't lose them — Prometheus replays the WAL on startup to rebuild what was lost from memory.
Q13 (Observability Concepts). A request enters through a gateway, then calls three downstream services in turn. Each service logs its own span, but engineers can't stitch one request's spans together in their tracing UI. What's the most likely missing mechanism?
- A. Every service needs its own separate Prometheus instance
- B. Trace context propagation — each service must forward a shared identifier (e.g., a W3C
traceparentheader) to the next hop so every span along the chain carries the same trace ID; without it, each service unknowingly starts its own disconnected trace - C. All services must write to one single combined log file
- D. Increasing histogram bucket granularity would fix this
Check the answer
B. Without a propagated trace ID, there's nothing linking the three services' spans together — each one is a valid, complete trace of exactly one hop, with no shared identifier for a tracing backend to stitch them into a single end-to-end view.
Q14 (Alerting & Dashboarding). When a cluster's ClusterDown alert fires, the team wants every ServiceUnreachable alert sharing the same cluster label suppressed entirely — not merely grouped, but not sent at all — for as long as ClusterDown is firing. Which config implements this, and what does it match on?
- A. A
routeblock withcontinue: false - B. An
inhibit_rulesentry withsource_matchersforalertname="ClusterDown",target_matchersforalertname="ServiceUnreachable", and anequallist of["cluster"]requiring both alerts to share that label's value before the source suppresses the target - C. A silence auto-created whenever any
severity="critical"alert fires - D. Grouping alone — no separate config is needed
Check the answer
B. This is specifically what inhibit_rules is for: a source alert (the parent, ClusterDown) suppresses a target alert (the downstream symptom, ServiceUnreachable) once equal confirms they're the same incident. Grouping only bundles notifications together; it doesn't stop a group member from being sent.
Q15 (Instrumentation and Exporters). A histogram is named api_response_time_ms with bucket boundaries [10, 50, 100, 500, 1000], and observations are recorded in milliseconds. What convention does this violate, and what's the concrete downside beyond style?
- A. It violates the convention of naming metrics in base SI units (seconds, not milliseconds); the concrete risk is that any dashboard, alert or shared recording rule written generically against the far more common "
_seconds"-style histograms will misread this metric's raw numbers as seconds, silently treating "500" as 500 seconds instead of 500 milliseconds - B. Nothing meaningful — milliseconds are just as valid a unit, and the naming convention is purely cosmetic
- C. Histograms can't hold millisecond-scale values at all, so Prometheus rejects the scrape outright
- D. Prometheus requires an
_mssuffix on every latency histogram
Check the answer
A. The scrape itself works fine — this is a consumption problem, not a validity problem. Naming conventions exist so a query or dashboard written once, generically, behaves correctly against any metric that follows them; a deviation like this quietly breaks that assumption for whoever borrows a "seconds" query against it later.
Block 2 — Q16–30
Q16 (PromQL). An engineer wants the peak 5-minute request rate observed at any point over the last hour, sampled internally every 30 seconds:max_over_time(rate(http_requests_total[5m])[1h:30s])
What is this subquery doing, and what does 1h:30s specifically mean?
- A. It's invalid — a range vector selector can't be nested inside another range vector selector
- B. The inner
rate(...[5m])is re-evaluated at each point spaced 30 seconds apart across a trailing 1-hour window (the subquery), producing a series of instant-rate values thatmax_over_timethen reduces to a single peak;1his the subquery's total range and30sis its resolution - C.
1h:30smeans the whole query only runs once per hour, at the 30-second mark - D. Subqueries silently ignore the resolution argument and always fall back to the global
scrape_interval
Check the answer
B. The subquery syntax <instant_query>[<range>:<resolution>] re-runs the inner instant query repeatedly at the given resolution across the given range, turning it into a range vector an outer function like max_over_time can then reduce — exactly how "the peak of a smoothed rate" gets computed.
Q17 (Prometheus Fundamentals). An engineer notices Prometheus's data directory contains many small subdirectories with roughly 2-hour time ranges, which later merge into fewer, larger directories. What's happening?
- A. Each 2-hour block is an immutable, self-contained chunk of samples once its window closes; a background compaction process periodically merges adjacent smaller blocks into larger ones, reducing file count and query overhead, and prunes blocks that have aged past the retention period
- B. Prometheus rewrites the same block in place and never creates new ones
- C. The 2-hour figure is fully configurable and controls how often alerting rules are evaluated
- D. Compaction is purely cosmetic and has no effect on query performance
Check the answer
A. Fresh data lands in small, immutable 2-hour blocks; compaction later merges neighboring blocks into larger ones on a schedule, which both shrinks the number of files a query has to touch and is the mechanism that eventually drops data older than the retention window.
Q18 (Observability Concepts). A latency histogram's p99 spikes on a dashboard, and the on-call engineer wants to jump directly from that specific spiky bucket to one slow trace that landed in it, instead of guessing which trace among thousands was the culprit. What's the purpose-built mechanism for this?
- A. Recording rules
- B. Exemplars — small samples attached to a histogram or counter observation, typically carrying a trace ID, that a client library records alongside the bucket increment; Grafana can render them as clickable points on a histogram panel that jump straight into the matching trace
- C. The
lelabel - D. Alertmanager inhibition
Check the answer
B. Exemplars are exactly this bridge between a metric and a trace: a sampled data point riding alongside an ordinary observation, carrying enough context (usually a trace ID) that a UI can link straight from a spike on a graph to the individual trace behind it.
Q19 (Alerting & Dashboarding). Alongside a fast, short-window burn-rate alert that pages immediately on a severe spike, a team also configures a second rule: a lower burn-rate threshold evaluated over a much longer window, routed to a ticket instead of a page. Why pair the two instead of relying on the fast one alone?
- A. A fast, short-window alert reacts quickly to a severe spike but can flap on noise and would miss a slow, steady leak that never spikes hard enough to trip it; a slow, longer-window alert smooths out noise and catches that steady leak, but reacts too slowly to justify paging on for a genuinely fast-burning incident — using both covers both failure shapes without over-paging on either
- B. The two windows exist purely for configuration convenience; using both is redundant
- C. Slow-window alerts are strictly worse and should be removed
- D. Multi-window burn-rate alerting only works when the SLO is exactly 99.9%
Check the answer
A. This is the multi-window, multi-burn-rate pattern: a fast window catches the incident that would blow the whole budget quickly and deserves a page now, while a slow window catches the quiet leak a fast window can't see, routed more gently since it isn't yet an emergency.
Q20 (Instrumentation and Exporters). A fixed fleet of exactly 3 payment-gateway instances has a contractual, per-instance latency SLA requiring an exact client-computed quantile, with no need to ever combine or compare quantiles across the 3 instances. Is a Summary metric type actually appropriate here, or always the wrong choice compared to a Histogram?
- A. Summary is never appropriate under any circumstances; Histogram should always be used instead
- B. A Summary can be the right choice specifically when a per-instance, client-computed quantile is genuinely what's needed and a fleet-wide aggregatable quantile isn't required — the trade-off (no cross-instance aggregation) simply doesn't bite in a scenario that was never going to aggregate across instances anyway
- C. Summary and Histogram produce byte-identical output; the choice is purely stylistic
- D. Summary is only valid for counters, never for latency measurements
Check the answer
B. The exam's usual guidance to prefer Histogram is really a warning about the aggregation trade-off, not a blanket ban on Summary. When the scenario was never going to aggregate across instances — a small, fixed, individually-tracked fleet with a per-instance contractual number — that specific downside doesn't apply.
Q21 (PromQL). A disk-usage gauge is trending upward. The team wants to know if it will breach 90% within the next 4 hours, based on the last 6 hours of trend, so they can page before it's full rather than after:predict_linear(node_filesystem_avail_bytes{mountpoint="/data"}[6h], 4*3600) < 0
What does predict_linear(v, t) compute, and why does this expression correctly predict a disk-full event?
- A. It returns the value the gauge is expected to have
tseconds from now, via simple linear regression over the range vectorv— this expression predicts available bytes falling below zero (i.e., full) within the next 4 hours if the current trend continues - B. It returns the average rate of change over the range, expressed as a percentage
- C.
predict_linearonly works on counters, never on gauges, so this is invalid on a filesystem-availability gauge - D. The second argument
tis a count of samples to look ahead, not a duration in seconds
Check the answer
A. predict_linear fits a linear regression to the range vector and projects it forward by t seconds. It's the standard tool for gauges specifically — disk space, queue depth, anything trending — and predicting a negative available-bytes value is exactly how you phrase "this will run out."
Q22 (Prometheus Fundamentals). Two independent Prometheus servers, one per data center, both scrape a service named "checkout" and both remote_write into the same long-term storage backend. Why does setting external_labels: { region: "us-east" } and { region: "eu-west" } respectively matter here?
- A.
external_labelsare attached to every series a server produces once that data leaves it — via federation orremote_write— so the two data centers' otherwise-identical series (same job, even the same instance names) stay distinguishable downstream instead of colliding - B.
external_labelschange how PromQL evaluates queries locally on each server, filtering out non-matching series - C.
external_labelsare required forscrape_configsto function at all and have nothing to do with multi-server setups - D.
external_labelsare deprecated in favor ofrelabel_configsand serve no remaining purpose
Check the answer
A. Locally, each server's own data doesn't need external_labels to make sense of itself. The moment that data leaves the server — federated out or remote-written into a shared backend alongside another server's identically-named series — external_labels is what keeps the two data centers from being indistinguishable downstream.
Q23 (Observability Concepts). Two proposed label designs for the same metric are debated: Design 1 adds region and env (a handful of fixed values each); Design 2 adds session_id (effectively unique per user session). Which correctly distinguishes dimensionality from cardinality and applies it to both designs?
- A. Dimensionality is the number of distinct label names (keys) a metric carries; cardinality is the number of distinct series those labels' actual values produce in combination. Design 1 adds dimensionality with low resulting cardinality (few regions × few envs); Design 2 adds a label whose value space is nearly unbounded, exploding cardinality even though it's still "just one more dimension" by count
- B. Dimensionality and cardinality are two names for the same concept, and both designs carry equal risk
- C. Cardinality refers only to the number of Prometheus servers in a federation setup
- D. Adding any label always increases cardinality by exactly the same fixed amount, regardless of its value space
Check the answer
A. This is exactly the distinction the cardinality-explosion warnings are getting at: counting label names tells you nothing about the danger, because the danger lives in how many distinct values a label can actually take — bounded and small for Design 1, effectively unbounded for Design 2.
Q24 (Alerting & Dashboarding). A dashboard has a $namespace template variable driving a $pod variable's own query, label_values(kube_pod_info{namespace="$namespace"}, pod), so picking a namespace narrows the pod dropdown to just that namespace's pods. What's this pattern called, and what does it enable?
- A. Chained (or cascading) template variables — one variable's query result depends on another variable's currently-selected value, letting a dashboard progressively narrow its scope instead of showing every pod across the whole cluster in one unfiltered dropdown
- B. This requires a separate dashboard per namespace and can't be done in one dashboard definition
- C. Template variables can only be populated from a hardcoded list, never from a live query
- D. This is only possible with recording rules, not plain
label_valuesqueries
Check the answer
A. Chaining is the whole point of template variables at scale: rather than one flat, unfiltered dropdown, each variable's query can reference an already-selected variable, progressively narrowing the dashboard's scope exactly the way $namespace narrows $pod here.
Q25 (Instrumentation and Exporters). A service registers a Counter with labels ["method", "code"] that vary on every .inc() call, and also wants every metric it exposes to carry a fixed version="2.4.1" label representing the deployed build, set once at process startup and never changing for that process's lifetime. How should the fixed version label be implemented, as distinct from method/code?
- A. As a const label — set once when the metric (or registry) is created and applied identically to every sample the process emits, never passed again at observation time, unlike
method/code, which must be supplied on every.inc()call because they vary per observation - B. Added to the same
labels()call asmethodandcodeon every single request, hardcoded to the same literal string each time - C. Fixed, build-time metadata like a version string can't be expressed as a Prometheus label under any circumstances and must go in a log line
- D. Const labels and per-observation labels are the same mechanism in every client library
Check the answer
A. A const label is set once at registration and never varies again, exactly matching a build-time version string. Repeating it on every .inc() call like method/code would technically work but is redundant and a maintenance risk if the string ever needs to be updated in more than one place.
Q26 (PromQL). An engineer tries kube_pod_info * on(namespace) kube_namespace_labels without group_left, and Prometheus errors with something like "multiple matches for labels: many-to-one matching must be explicit." What's actually going wrong?
- A.
kube_pod_infohas many series sharing the same namespace value, each of which would need to match the singlekube_namespace_labelsrow for that namespace — a many-to-one relationship PromQL refuses to resolve implicitly withoutgroup_left/group_right - B.
kube_namespace_labelsitself has duplicate rows for the same namespace, which is illegal regardless ofgroup_leftand must be fixed at the source - C. The multiplication operator can never be used between two "info"-style metrics
- D.
on(namespace)needs at least two labels named to avoid this error
Check the answer
A. This is the identical mechanism as Q1, from the other side: many per-pod series legitimately sharing one namespace value trying to match a single namespace-level row is many-to-one, and PromQL insists that be spelled out explicitly rather than guessed at.
Q27 (Prometheus Fundamentals). Within one rule group, a recording rule computing job:errors:ratio_rate5m is defined immediately before an alerting rule whose expr references that exact recording rule's output name. Is this safe, and why?
- A. It's safe — rules within a single group are evaluated sequentially, in the order written, and each rule's result becomes available to subsequent rules in that same evaluation cycle, so the alerting rule sees the freshly-computed value rather than a stale one from last cycle
- B. It's unsafe — rules across an entire group are always evaluated concurrently with no ordering guarantee
- C. Recording rules and alerting rules can never coexist in the same rule group
- D. It's only safe if both rules explicitly set an identical per-rule
evaluation_interval
Check the answer
A. Sequential-within-a-group is exactly the guarantee that makes "recording rule feeds an alerting rule" a safe, common pattern — as long as they're in the same group and written in that order. It's across different groups that ordering isn't guaranteed, which is the real footgun to watch for.
Q28 (Observability Concepts). Team A logs "user 4471 checkout failed: card declined" as free text; Team B logs {"user_id": 4471, "event": "checkout_failed", "reason": "card_declined"} as JSON. What's the practical, observability-relevant advantage of Team B's approach?
- A. Structured logs compress better on disk, and that's their only real advantage
- B. Structured logs expose fields as queryable, machine-parseable key-value pairs, letting a log aggregation system filter, aggregate and correlate on
reasonoruser_iddirectly, rather than requiring fragile regex or string parsing to pull the same information out of free text - C. Unstructured logs can't be indexed by any logging system at all
- D. Structured logging is only relevant to Prometheus, not to log-focused tools
Check the answer
B. The real payoff is queryability: a structured field like reason="card_declined" can be filtered, grouped and counted directly, where a free-text line needs a regex that breaks the moment the wording changes even slightly.
Q29 (Alerting & Dashboarding). An alerting rule with for: 1m against a noisy, spiky metric fires and resolves repeatedly every few minutes, and the on-call engineer starts ignoring the notifications. Which change most directly addresses the flapping itself, as opposed to just muting the noise?
- A. Add a silence that never expires
- B. Lengthen the
for:duration so the condition must hold continuously for longer before firing, and/or smooth the underlying expression (e.g., aggregate or average over a longer window) so brief spikes stop crossing the threshold in the first place - C. Increase
repeat_interval - D. Route the alert to a different receiver
Check the answer
B. A never-expiring silence and a different receiver both hide the symptom without touching the cause; repeat_interval only affects how often an already firing alert re-notifies. Only lengthening for: or smoothing the expression changes whether the alert flaps in the first place.
Q30 (Instrumentation and Exporters). A Python web app runs under gunicorn with 8 worker processes, each importing the same Prometheus client library and registering the same Counter. Scraping /metrics returns numbers far lower than expected, as if only one worker's data is ever reported. What's the most likely cause and the standard fix?
- A. Prometheus itself is misconfigured with too short a
scrape_interval - B. Each gunicorn worker has its own separate in-memory metric registry by default, so whichever single worker happens to handle a given
/metricsrequest only reports its own slice of traffic; the standard fix is multiprocess mode — settingPROMETHEUS_MULTIPROC_DIRand using the client library's multiprocess collector so every worker writes to shared files a single aggregating collector reads across all of them - C. This is expected and correct; Counters are never meant to be shared across multiple processes
- D. The only fix is running exactly 1 gunicorn worker, since client libraries can't support multi-process apps at all
Check the answer
B. This is a well-known multiprocess gotcha specifically because each worker process gets its own registry by default. Multiprocess mode exists precisely to solve it, aggregating every worker's numbers rather than reporting whichever one happened to answer the scrape.
Block 3 — Q31–45
Q31 (PromQL). An engineer is debugging a feature-flag gauge that toggles between 0 and 1 whenever a flag is flipped (no reset semantics at all), and separately a counter that occasionally resets to 0 on pod restarts. They want to count how many times the flag flipped in the last day, and separately how many times the counter restarted in the last day. Which functions correctly pair with which metric?
- A.
resets()for the flag flips,changes()for the counter restarts - B.
changes()counts how many times a series' value changed between consecutive samples (works on any series, gauge or counter) — correct for the flag;resets()counts counter resets specifically, a value drop — correct for the restarting counter - C. Both
changes()andresets()only work on counters; gauges must usedelta()instead - D.
changes()andresets()are aliases for the same function, kept for backward compatibility
Check the answer
B. changes() is the general-purpose one — any value change counts, on any series. resets() is narrower and specific to counters, counting only drops in value, which is exactly the restart signature the counter scenario needs.
Q32 (Prometheus Fundamentals). A platform wants a small, central "global" Prometheus that aggregates a handful of pre-computed, low-cardinality recording-rule results — one number per region — from a dozen regional servers, without ingesting all their raw, high-cardinality data. Which mechanism is purpose-built for this, and what's its key limitation compared to full remote-write/long-term storage?
- A. The
/federateHTTP endpoint, scraped by the global server like any other target, pulling a chosen subset of already-aggregated series from each regional server — a pull-based, hierarchical pattern good for a small set of summary series, not a substitute for genuine long-term, full-fidelity storage across the whole fleet - B.
remote_writeis the only mechanism Prometheus offers for this;/federatedoesn't exist - C. Federation replicates the entire TSDB byte-for-byte between servers
- D. Federation requires Alertmanager to be federated separately and has no relationship to the Prometheus server itself
Check the answer
A. Federation is a narrow, deliberate tool: pull a small, chosen slice of already-summarized data upward into a global view. It's the wrong tool for full-fidelity, whole-fleet long-term storage — that's what remote-write into Thanos, Mimir or Cortex is for.
Q33 (Observability Concepts). A service has a 99.9% monthly SLO — an error budget of roughly 43 minutes of allowed downtime for the month. At 2am, an alert fires because the current error rate, if sustained, would exhaust the entire monthly budget within the next hour. What alerting concept is this, and why is it usually paired with a second, slower-burning threshold?
- A. This is a fast burn-rate alert — a burn rate consuming budget many times faster than sustainable justifies waking someone immediately, even off a short observation window, because a delay could blow the whole month's budget. A slower, longer-window burn-rate alert is paired alongside it to catch a steady, less dramatic leak the fast-window alert would miss or flap on
- B. This is simply the SLO threshold itself firing, with no separate "burn rate" concept involved
- C. Burn-rate alerting only applies to SLAs, never to internal SLOs
- D. A fast burn-rate alert should always replace slower thresholds entirely, since it reacts faster in every case
Check the answer
A. The urgency of a fast burn-rate alert comes from the math, not the raw error rate: at this pace, the entire month's allowance is gone within the hour, which is exactly the kind of thing worth an immediate page — while the slow-window companion alert exists to catch a leak too gradual to ever trip this one.
Q34 (Alerting & Dashboarding). A planned 2-hour database maintenance window will predictably trip several latency and error-rate alerts on the affected service. The team wants those specific alerts muted for exactly that window, without disabling the alerting rules themselves, so the rules resume firing normally the moment the window ends if the underlying issue is still present. What's the correct mechanism, and how does it differ from commenting out the rule for two hours?
- A. A silence, created in Alertmanager, matching the relevant labels with a start and end time covering the window — it stops notifications from being sent while the Prometheus alerting rule keeps evaluating and the alert's pending/firing state keeps updating normally underneath, so nothing needs redeploying before or after
- B. Commenting out the rule achieves an identical outcome with no downside
- C. Silences can only be created for alerts already firing, never proactively ahead of a planned window
- D. Inhibition is the correct mechanism for a planned, time-boxed maintenance window, not silencing
Check the answer
A. A silence mutes notifications without touching rule evaluation at all — the rule keeps running underneath, so state is preserved and nothing needs a config reload on either end of the window. Commenting the rule out requires exactly that reload twice and loses the rule's live state for the whole window.
Q35 (Instrumentation and Exporters). A nightly cron job runs a certificate-expiry check and needs its result (days until expiry, as a gauge) available to Prometheus, but it's a short one-shot script, not a long-running process that could expose its own /metrics endpoint. What's the standard node_exporter-based pattern for this, distinct from both a client-library integration and the Pushgateway?
- A. node_exporter's textfile collector — the cron job writes plain exposition-format text to a
.promfile in a designated directory, and node_exporter serves it alongside its normal OS metrics on the next scrape, with no long-running process or push endpoint required from the script itself - B. The cron job must be rewritten as a long-running daemon exposing its own
/metricsendpoint - C. The Pushgateway is the only correct tool for any short-lived script's output
- D. node_exporter can never expose anything beyond its own built-in OS metrics
Check the answer
A. The textfile collector is the standard node_exporter answer for host-level, cron-style facts specifically: drop a file, let node_exporter pick it up on its next scrape, no daemon or push endpoint needed. The Pushgateway is a plausible-sounding alternative but is aimed more at job-completion metrics from applications, not host facts like this.
Q36 (PromQL). A recording rule needs a clean 0-or-1 value for every instance's latency-over-threshold state — not a filtered vector that silently drops the healthy instances — so a dashboard heatmap can color every instance, including the healthy ones. Which comparison correctly produces a 0-or-1 value for every input series, rather than filtering some out?
- A.
instance_latency_seconds > 0.2 - B.
instance_latency_seconds > bool 0.2 - C.
clamp_max(instance_latency_seconds, 0.2) - D.
bool(instance_latency_seconds > 0.2)
Check the answer
B. Without bool, a comparison operator filters — keeping only series where the condition is true, with their original value, and dropping the rest. The bool modifier changes that behavior so every input series survives, its value replaced with a plain 1 or 0. clamp_max caps a value; it doesn't produce a boolean at all.
Q37 (Prometheus Fundamentals). A team using file-based service discovery notices every target in their SD file only carries custom meta-labels like __meta_filepath and a top-level targets: ["10.0.4.12:9100"] entry, with no explicit "address" field set anywhere in relabel_configs — yet Prometheus correctly scrapes 10.0.4.12:9100. What's actually determining the scrape address?
- A.
__address__is itself a label, populated by each service-discovery mechanism (here, directly from the file'stargetslist), and relabeling can rewrite it like any other label via atarget_label: __address__step — if nothing rewrites it, Prometheus scrapes whatever__address__already holds - B. Prometheus always scrapes port 9100 on every discovered target's IP unless told otherwise
- C.
__address__is a reserved, read-only value that relabel_configs can never modify - D. The scrape address always comes from the
job_namefield, and__address__is unused
Check the answer
A. Every target's scrape address really is just the current value of the __address__ label, seeded by the discovery mechanism and freely rewritable through relabeling — a very common pattern is rewriting it to change the scraped port, which directly contradicts option C.
Q38 (Observability Concepts). A tracing pipeline is expensive at full volume, so a team samples 1% of traces. One engineer proposes deciding whether to keep a trace the moment its first span is created, before the outcome is known; another proposes buffering the whole trace and deciding after it completes, so error and slow traces can always be kept regardless of the sampling rate. Which is head-based and which is tail-based, and what's the trade-off?
- A. The first proposal is head-based sampling — cheap and simple, decided upfront, but blind to outcome, so errors and slow traces get sampled at the same low rate as everything else; the second is tail-based sampling — more expensive, since the full trace must be buffered before deciding, but can deliberately keep 100% of errors and slow traces while still discarding most routine ones
- B. The first is tail-based and the second is head-based
- C. Both proposals describe the identical mechanism under different names
- D. Sampling decisions can only ever be made head-based; tail-based sampling doesn't exist
Check the answer
A. The names describe exactly when the decision happens — at the "head" (the first span, no outcome known yet) or the "tail" (after the whole trace completes, outcome fully known). Tail-based costs more to buffer but is the only way to guarantee errors and slow traces aren't just as likely to be dropped as everything else.
Q39 (Alerting & Dashboarding). A Grafana stat panel for up{job="checkout"} shows "No data" during an incident. One engineer insists that must mean the value is 0 (down); another points out those aren't the same thing. Who's right, and why does the distinction matter operationally?
- A. They mean the same thing; "No data" is just Grafana's cosmetic label for a literal zero value
- B. "No data" means the query returned no series at all for the current time range — e.g., the target vanished from service discovery entirely, or Prometheus itself is unreachable from Grafana — a different failure mode than
up==0, where Prometheus IS reaching the target and getting an explicit scrape failure. Confusing the two can send an on-call engineer chasing the wrong layer of the stack - C. "No data" only ever appears due to a Grafana rendering bug and never reflects the underlying query result
- D.
upcan never return "No data," only 0 or 1
Check the answer
B. up==0 means Prometheus reached the target and the scrape itself failed — a target-side problem. "No data" means the series never existed for that query at all, which points somewhere entirely different: service discovery, Prometheus's own reachability, or the query's label matchers. Treating them as the same fact sends the responder to the wrong layer.
Q40 (PromQL). A team wants the top 5 CPU-heaviest pods, but only among pods already above a 0.5-core threshold — so if fewer than 5 pods clear the threshold, fewer than 5 should appear, with no padding from idle pods. Which expression is correct?
- A.
topk(5, sum by (pod) (rate(container_cpu_usage_seconds_total[5m]))) and (sum by (pod) (rate(container_cpu_usage_seconds_total[5m])) > 0.5)— usingand(vector set-intersection) to keep only members of the true fleet-wide top-5 set that also clear the threshold, so the result can have fewer than 5 series but never one below 0.5 - B.
topk(5, sum by (pod) (rate(container_cpu_usage_seconds_total[5m])) > 0.5) - C.
bottomk(5, sum by (pod) (rate(container_cpu_usage_seconds_total[5m]))) > 0.5 - D.
topk(5, sum by (pod) (rate(container_cpu_usage_seconds_total[5m]))) * on(pod) (sum by (pod) (rate(container_cpu_usage_seconds_total[5m])) > bool 0.5)
Check the answer
A. and keeps left-hand series that also have a matching series on the right, correctly filtering the true top-5 down to only the ones clearing 0.5 cores. Option B filters below-threshold pods out first, then ranks the survivors — which can silently promote a pod that was never in the real fleet-wide top 5. Option D multiplies every top-5 entry by 1 or 0 instead of removing it, so a sub-threshold pod stays in the result at a value of 0 rather than being dropped, which isn't what was asked for.
Q41 (Prometheus Fundamentals). A team wants their on-prem Prometheus servers to ship data into a centralized, long-term, horizontally-scalable backend without every downstream consumer needing to scrape each Prometheus individually. How does this fit with Prometheus's core pull model?
- A.
remote_writeis Prometheus actively pushing every locally-ingested sample onward to a configured remote endpoint as it's written — a deliberate, additional push path layered on top of the core pull model, distinct from (and not a replacement for) how Prometheus itself gathers data from its own targets - B.
remote_writesilently converts the destination backend into another Prometheus scrape target that must poll the source server instead - C.
remote_writeand the Pushgateway are the exact same mechanism under different names - D.
remote_writerequires disabling local scraping entirely
Check the answer
A. Prometheus's own scraping stays pull-based and unaffected; remote_write is a separate, additional pipe that pushes already-ingested samples onward. It answers a genuinely different question than the Pushgateway, which exists so short-lived jobs can get data into a pull-based Prometheus in the first place.
Q42 (PromQL, Select TWO). A target's process crashes at 10:00:00; its last successful scrape was at 09:59:45. A dashboard panel graphing that series keeps showing the last known value, unchanged, until roughly 10:04:45 — five minutes after the crash — then the line vanishes entirely rather than dropping to zero. Which TWO statements correctly explain this?
- A. Prometheus's default staleness lookback is 5 minutes: an instant-vector query still returns the last sample if it falls within 5 minutes of the query time, which is why the value keeps rendering for that stretch
- B. When a scrape fails and stays failed, Prometheus writes an explicit stale marker into the series shortly after the failure is detected, and any query evaluated after that marker (and once the 5-minute lookback has elapsed) no longer returns the series at all
- C. Grafana always interpolates missing points as a flat line for exactly five minutes, regardless of what Prometheus returns
- D. Missing metrics are always shown as zero by Prometheus, and the panel is configured to hide zero values
- E. Prometheus deletes the underlying samples from the TSDB the moment a scrape fails
Check the answer
A and B. Both are genuine Prometheus mechanisms: the 5-minute staleness lookback explains why the last value keeps answering queries for a while, and the explicit stale marker explains why the series then disappears outright rather than reading as zero. C misattributes the behavior to Grafana; D and E both misdescribe how Prometheus actually treats a missing series — it's never silently zero, and nothing is deleted from the TSDB.
Q43 (PromQL). An exporter exposes container_name values like web-7f9c8d-4kxvz (a pod-suffixed name), and the team wants a clean app label by stripping the trailing random suffix so they can join against a deployment-level metric:label_replace(container_cpu_usage_seconds_total, "app", "$1", "container_name", "(.+)-[a-z0-9]+-[a-z0-9]+")
What does label_replace(v, dst, replacement, src, regex) do, and is this call shaped correctly?
- A. It permanently renames
container_nametoappeverywhere, discarding the original label - B. It creates a new label named
app(or overwrites it if already present) on every series, setting its value to the regex capture groups extracted fromsrc— the originalcontainer_namelabel is left untouched; this call is shaped correctly as long as the regex actually matches the labeled value - C.
label_replacecan only add new labels, never overwrite an existing one with the same name - D. The regex argument is optional, defaulting to a literal substring match if omitted
Check the answer
B. label_replace sets a destination label from a regex match against a source label — leaving the source untouched — and if dst and src happen to be the same name, it overwrites in place. It doesn't discard anything on the source side, and the regex is required, not optional.
Q44 (PromQL). An engineer wants to spot suspiciously many nodes reporting the exact same, possibly-stuck value for node_load1 across a large fleet:count_values("load_value", node_load1)
What does this actually compute?
- A. For each distinct numeric value seen across all
node_load1series at the current instant, it emits one result series with a new labelload_valueset to that value, whose own value is a count of how many input series shared it — useful for spotting an unusual pile-up of nodes reporting an identical, possibly frozen, value - B. It counts, per node, how many distinct values that node reported over the query range
- C.
count_valuesrequires a range vector, not an instant vector, so this expression is invalid as written - D. It's functionally identical to
count(node_load1), and the first argument is ignored
Check the answer
A. count_values aggregates by the sample value, not by a label — grouping series that currently report the same number together and counting how many there are. That's a genuinely different question from count(), which just counts series regardless of what value each one holds.
Q45 (PromQL). A junior engineer, trying to get p95 latency, writes quantile(0.95, http_request_duration_seconds) directly against a metric name — not histogram bucket series — and is confused why it doesn't behave like histogram_quantile(). What's the actual difference between the two?
- A. They're two names for the same operation and always return identical results
- B.
quantile()is an aggregation operator that computes a quantile across a set of already-collected series' current values at one instant — similar to howsum()oravg()aggregate across series — with nothing to do with histogram buckets.histogram_quantile()is a function that estimates a quantile within one logical histogram by interpolating across its cumulativelebuckets. Usingquantile()against raw per-instance values answers "what's the Nth-percentile value across instances right now," a fundamentally different question from a histogram's fleet-wide request-latency percentile - C.
quantile()only works on counters andhistogram_quantile()only works on gauges - D. Both require an
lelabel to function correctly
Check the answer
B. This is the classic mix-up: quantile() aggregates across series at one moment, the same family as sum()/avg()/max(). histogram_quantile() is a completely different tool that interpolates a percentile within one histogram's _bucket series. Neither is a drop-in replacement for the other, and only histogram_quantile() cares about le at all.
Score yourself
☺ Like you're 10: Same scoring method as Set 1 — count correct answers out of forty-five, compare to 75%, then see which domain cost you the most.
The Linux Foundation's Multiple Choice Exam FAQ states that a score of 75% or above is required to pass any LF multiple-choice exam, and the PCA is one. Because this paper shares Set 1's exact domain split, put the two domain-by-domain scores side by side — a domain that dropped noticeably between the two papers is a real signal: you know the definitions, but the same idea slips under a layered scenario.
| Domain | Questions in this paper | Your score | If you're under two-thirds, go here |
|---|---|---|---|
| 🐘 PromQL | Q1, 6, 11, 16, 21, 26, 31, 36, 40, 42, 43, 44, 45 | /13 | PCA — the exam |
| 🦉 Prometheus Fundamentals | Q2, 7, 12, 17, 22, 27, 32, 37, 41 | /9 | Prometheus |
| 🐿️ Observability Concepts | Q3, 8, 13, 18, 23, 28, 33, 38 | /8 | The OpenTelemetry data model |
| 🐢 Alerting & Dashboarding | Q4, 9, 14, 19, 24, 29, 34, 39 | /8 | Grafana |
| 🦫 Instrumentation and Exporters | Q5, 10, 15, 20, 25, 30, 35 | /7 | OpenTelemetry Collector |
| Total | 45 questions | /45 | 75% (34/45) to clear comfortably |
If Set 2 scored lower than Set 1 in the same domain, don't treat that as a worse result — treat it as more accurate information. Set 1 tells you what you can recall; Set 2 tells you what you can actually apply when a question doesn't hand you the concept by name. A domain that held steady between the two papers is genuinely solid. A domain that dropped is where a second read of the linked page, done slowly this time, pays off more than another timed rep.
Where each domain is taught
☺ Like you're 10: Same handful of pages as before — every scenario above still traces back to one of them.
Nothing in this paper reaches beyond what Set 1 already pointed you toward. The scenarios are new; the underlying material they're testing isn't.
PromQL
Vector matching with on/group_left, offset, subqueries, absent_over_time, predict_linear, changes/resets, the bool modifier, label_replace, count_values, and quantile() vs histogram_quantile().
🦉 · 20%Prometheus Fundamentals
scrape_timeout limits, honor_labels, the WAL, TSDB block compaction, external_labels, rule-group evaluation order, federation, and the __address__ meta-label.
🐿️ · 18%Observability Concepts
Golden signals, the SLI/SLO/SLA distinction under pressure, trace context propagation, exemplars, cardinality vs dimensionality, structured logging, burn rate, and sampling strategies.
🐢 · 18%Alerting & Dashboarding
Routing trees with continue, the group_wait/group_interval/repeat_interval timers, inhibit_rules, multi-window burn-rate alerting, chained template variables, flapping, silences, and "No data" vs zero.
🦫 · 16%Instrumentation and Exporters
Exporters vs client libraries, bounded label design, base-unit naming, when Summary is legitimate, const labels, multiprocess mode, and the node_exporter textfile collector.
The wider revision kit still applies: flashcards for the vocabulary, the self-check quiz for mixed recall, the glossary for anything a question assumed you already knew, and the PCA study plan for sequencing. For shorter, module-specific reps, the PCA practice bank splits the same ground into focused drills.
Remy: Finished! Forty-one out of forty-five, and Set 1 was thirty-nine — I'm getting better!
Ellie: Better on the total, sure. Which domain still cost you, Remy — same one as last time, or a different one this time?
Remy: ...PromQL, actually. I picked the topk-then-filter version instead of the and version on the top-5-above-threshold question.
Foxy: Wait, aren't those the same five pods either way?
Timmy: Not always. Filter-then-rank can promote a pod that was never really in the fleet-wide top five, just because the real top five got trimmed down first. Order matters.
Gizmo: Easy fix — skip group_left entirely and let Prometheus figure out the join on its own. Less typing!
Ellie: Prometheus won't figure it out, Gizmo — it'll throw a many-to-one matching error and refuse to run at all. That's not a shortcut, that's a query that never executes.
Remy: So Set 1 taught me the vocabulary, and Set 2 just taught me I don't actually know it as well as I thought I did.
Ellie: That's the whole reason there are two papers, Remy. Now go read the ordering explanation once more, slowly, and it'll stick.
1. Why does kube_pod_info * on(namespace) kube_namespace_labels need group_left, structurally? 2. What's the difference between changes() and resets(), and which metric type is each meant for? 3. Why does an and-based top-5-above-threshold query behave differently from filtering inside topk()'s own argument? 4. What's the practical difference between a "No data" panel state and a literal up==0? 5. Why is a fast, short-window burn-rate alert usually paired with a slower, longer-window one rather than used alone? 6. What does honor_labels: true change, and what's the default behavior instead? 7. Structurally, why can a Summary metric type still be the right choice even though the exam generally favors Histogram for latency?
Check your answers
- The left side has many series sharing one namespace value, all trying to match the single right-side row for that namespace — a many-to-one match PromQL refuses to resolve implicitly.
group_leftboth permits it and copies extra labels from the "one" side onto the result. changes()counts any value change on any series, gauge or counter — right for the flag.resets()counts counter-specific drops in value — right for the restarting counter.- Filtering first (inside
topk()) ranks only the already-filtered survivors, which can promote a pod that was never in the true fleet-wide top 5. Usingandranks the whole fleet first, then intersects with the threshold — a genuinely different result set. - "No data" means the query returned no series at all — the target may have vanished from service discovery, or Prometheus can't be reached.
up==0means Prometheus DID reach the target and got an explicit scrape failure. They point to different layers of the stack. - A fast window reacts quickly to a severe spike but can flap on noise and misses a slow, steady leak; a slow window smooths noise and catches the leak but reacts too slowly to justify paging alone. Together they cover both failure shapes.
honor_labels: truelets a target's own scraped label win over Prometheus's automatically-added target label on conflict. The default,false, does the opposite — Prometheus's label wins and the conflicting scraped one is renamed with anexported_prefix.- Summary's real downside is that its client-side quantiles can't be aggregated across instances. In a scenario that was never going to aggregate across instances — a small, fixed fleet needing an exact per-instance number — that downside simply doesn't apply.
That's the full sitting. If a domain dropped compared to Set 1, that's the one worth a slow, deliberate reread rather than another timed run — this paper was built to surface exactly that gap.