How would you watch statement counts in production without logging every statement the service executes?
answer
- keep the number, drop the text
- one field per request record
- low-cardinality keys only
- compare a route with itself
- sample grouped text above a threshold
basics
~20 sRecord the per-request count as a number - on the request record and as a distribution keyed by route - alert on per-route drift rather than an absolute threshold, and sample grouped statement text only above a threshold.
solid answer
~50 sKeep the number, drop the text. The same per-request counter used in tests writes one extra field on the record the service already emits per request, and feeds a distribution keyed by route or operation - both cheap, because the expensive part of statement logging is the formatting and volume, not the counting. Key it only on low-cardinality names; identifiers in the key create unbounded series. Alert on shape rather than an absolute number: a route's own percentile against last week, a step change at a release, or statements per returned row trending up. Then buy the text back by sampling - when a request's count crosses its route's threshold, emit one record with the statements grouped by text, parameters stripped, rate-limited per route. Cheap in the healthy case, full evidence in the pathological one.
go deeper
Know that statement logging is a development tool: one line per statement is far too much data in production, and the count is the part worth keeping there.
Explain the two destinations for the number - a field on the per-request record and a distribution keyed by operation - and why the key must stay low-cardinality.
Design the two tiers: a cheap always-on count with drift alerting per route, and rate-limited sampling of grouped statement text triggered by the count itself.
Own the tradeoff between observability cost, data handling and diagnostic power, and state plainly what the arrangement still cannot answer before anyone relies on it.
Statement logging is a development instrument. Turned on for every request in production it writes one line per statement, at the exact moment the service is issuing far too many of them, on the machine that is already struggling — and the lines carry parameter values, which is a data-handling problem on top of a volume problem. The production version of the same idea keeps the **number** and throws away the text, then buys the text back only for the requests that deserve it. ## Record the count as a measurement, not as text The per-request counter described for tests works unchanged in production; what changes is where the number goes. Two destinations, and they answer different questions: - **On the request's own record.** Whatever line or span the service already emits per request gets one more field: the statement count, next to the route or operation name and the duration. Cost is one integer on a record that was being written anyway, and it makes "why was this request slow" answerable after the fact. - **As an aggregate.** The same number fed into a distribution keyed by operation gives percentiles per route over time — the series you can actually alert on. Both are cheap because the expensive part of statement logging was never the counting, it was the formatting and the volume of the text. ## Keep the key space small A distribution keyed by operation is useful; a distribution keyed by anything that varies per request is a liability. Route templates, job names and message types are safe keys. Identifiers, parameter values, user names, raw paths with values interpolated into them are not: each distinct value creates its own series, the series count grows without bound, and the cost lands in the service's memory, in the export step and in the store at the other end. The rule is the same one that applies to any dimensional measurement, and it bites harder here because a per-row read is precisely the situation where identifiers are abundant. ## Alert on the shape, not on an absolute number An absolute threshold across a service is either so high it never fires or so low it fires constantly, because a legitimate count varies by an order of magnitude between a health check and a reporting screen. What is meaningful: | Signal | What it catches | Why it is stable | |---|---|---| | Per-route percentile compared with the same route last week | a change in the access pattern | the route is its own baseline | | A step change aligned with a release | a regression that shipped | correlates cause and effect immediately | | Count divided by rows returned, trending up | multiplication rather than more traffic | normalises away load and page size | | Count percentile far above the route's median | one input class taking a bad path | catches the tail an average hides | Total statements per second is a load metric and belongs on the capacity dashboard, not here: it rises with traffic and says nothing about whether a code path multiplied. ## Buy the text back by sampling The count tells you a route regressed; it never tells you which read multiplied. So arrange for the evidence to be captured only when it is worth capturing: 1. **Trigger on the count.** When a request's count crosses a threshold for its route, emit one record for that request containing the statements grouped by text with parameters stripped, and their repetition counts. 2. **Cap it.** A rate limit per route per interval, so a route that regresses for every request produces a handful of samples rather than a flood, and so the diagnostic path cannot become the outage. 3. **Redact.** Group by statement text with values removed; the shape and the repetition count are what diagnose the problem, and the values are what create the data-handling risk. 4. **Keep a slower unconditional trickle.** A small random sample, independent of the trigger, is what lets you see the baseline shape of a route that has never crossed its threshold. This gives the property that matters operationally: constant, negligible cost in the healthy case, full evidence in the pathological one. ## Know what this arrangement still cannot do - It shows a route regressed, not which line multiplied — the sampled statement shapes narrow that, a captured call path narrows it further, and neither is guaranteed to be present for the request you care about. - Counts alone will not distinguish "more statements" from "the same statements, now slower", so the count belongs beside the duration, never instead of it. - A route whose statements are grouped into fewer sends can regress in latency while its execution count sits still, and vice versa. - Sampling by definition misses the request the user complained about, unless the trigger happened to fire. ## Why interviewers like this one It separates people who have only ever debugged this locally from people who have had to find it in a running system. The local answer — turn on statement logging — is the wrong answer in production for reasons of cost, volume and data handling, and the good answer is a two-tier design: a cheap number always, expensive text rarely and deliberately.
- Why is a service-wide threshold on statement count a poor alert?Because a legitimate count varies by an order of magnitude between routes, so one number is either so high it never fires or so low it fires constantly. Each route is its own baseline: compare it with itself over time, or normalise by rows returned. A step change at a release is a far stronger signal than crossing an arbitrary line.
- What makes total statements per second the wrong metric for this?It is a load figure. It rises with traffic and falls at night, so a per-row read that arrived in yesterday's release is invisible inside it, and a marketing spike looks identical to a regression. The count that diagnoses is per request, keyed by operation; the aggregate rate belongs on a capacity dashboard.
- What must never end up in the key of a statement-count distribution?Anything that varies per request: identifiers, parameter values, user names, raw paths with values in them. Each distinct value becomes its own series, so the series count grows without bound and costs memory in the process, work at export and storage at the other end. Route templates, job names and message types are the safe keys.
- The count is flat but the route got slower. What does that tell you?That the access pattern did not change and something about the statements or the data did - a plan change, growth in a scanned table, contention, or a slower dependency. Counts and durations answer different questions, which is why the count is recorded beside the duration rather than instead of it.
saying these in an interview costs you the question
- Proposes leaving full statement logging on in production to catch it.
- Alerts on one absolute statement-count threshold for the whole service.
- Keys the metric by identifier or parameter value, exploding series count.
- Logs statements with parameter values still in them.
- Watches total statements per second and calls it a per-row read detector.
- Assumes a count metric identifies which code path multiplied.