Observability and Monitoring Architecture Questions
Building visibility into infrastructure and services: metrics, logs, and traces, dashboards and alerting, SLIs/SLOs, and the design of an observability stack. Covers instrumenting systems for actionable signal, reducing alert noise, and diagnosing production issues from telemetry. Infrastructure-wide observability, distinct from network-specific monitoring.
You're told storage costs $X per terabyte per month, and asked to propose a tiered storage policy for logs and metrics under that budget: hot, warm, and cold tiers, retention windows, a downsampling strategy for older data, and archival to cheaper storage. How would you make sure compliance requirements and alerting still work once raw data has been moved or downsampled?
Sample Answer
Direct answer
Split retention into hot (raw, fast storage, days), warm (compressed, still full resolution, weeks), cold (downsampled and heavily sampled, up to a year), and archive (compliance-only raw extracts, years, cheapest storage). Alerting stays correct because every alert either reads from the hot/warm tiers where it needs full resolution, or is redesigned to work on the statistical properties (percentiles, error counts) that downsampling preserves. Compliance stays correct because the raw bytes a regulator might ask for are pulled into a separate, signed archive path that bypasses the operational downsampling entirely.
Tiering policy
| Tier | Window | What's kept | Why |
|---|---|---|---|
| Hot | 0-7 days | Full-resolution raw metrics and logs | Real-time debugging, alert evaluation needs exact values |
| Warm | 8-30 days | Full resolution, compressed ~3x | Recent-incident lookback, still-fresh alerting context |
| Cold | 31-365 days | Metrics downsampled 10:1, logs sampled to 1% (errors kept at 100%) | Trend/capacity analysis, cheap enough to keep a year |
| Archive | 365+ days | Compressed 4:1 extract of the compliance-flagged subset only | Regulatory/legal retention, rarely queried |
Downsampling rule for metrics: never discard raw points, only aggregate. Roll up 10s scrapes into 1-minute buckets storing count, sum, min, max, and pre-computed percentile buckets (histogram, not just an average), so alerting on p95/p99 still works post-downsample. Averaging away the histogram is the single most common mistake here: an average of averages silently breaks p99-based alerts.
Downsampling rule for logs: keep 100% of error/warn-level events forever within the cold window; sample info/debug logs at a low, deterministic rate (hash on trace ID) so a given request's logs are either fully kept or fully dropped, never split. That preserves the ability to reconstruct a whole request's log trail if it was selected.
Keeping alerting correct across tiers
- Alert rules that fire on absolute thresholds (error count, latency p99) are pinned to run against hot/warm data only, since that is where fidelity is guaranteed.
- Longer-window alerts (weekly seasonality, slow burn SLO budget) run against cold-tier rollups, but only because the rollup step preserved histograms rather than plain averages, so the alert's percentile math still holds.
- Backtest every downsampling change: replay the last 90 days of raw data through the proposed rollup and confirm the alert would have fired at the same times it fired against raw data, before shipping the rollup config.
Keeping compliance correct across tiers
- Compliance retention is a separate write path, not a side effect of the operational tiers. At ingest, a policy tags records matching compliance scope (PII, regulated data classes, specific tenants); those records get a signed, immutable copy written to the archive tier at full fidelity, independent of what the operational hot/warm/cold pipeline does to the rest of the stream.
- The operational tiers are free to downsample or expire on their own schedule because the compliance obligation is already satisfied by the separate archive copy.
- Archive retrieval is on-demand (async restore), so day-to-day cost stays low, but an auditor's request for raw data from 3 years ago is answerable.
Worked example
Assume a service ingests G=500 GB/day of combined logs and metrics, split 30% metrics / 70% logs, and the storage budget is a hot-tier unit price of X=$23/TB-month (a concrete stand-in for the given $X). Illustrative relative unit prices for the other tiers, typical of SSD-backed vs. compressed-object vs. archival storage:
Warm priceCold priceArchive price=0.40X=$9.20/TB-month=0.15X=$3.45/TB-month=0.02X=$0.46/TB-monthTier volumes (hot: 7 days raw; warm: next 23 days at 3:1 compression; cold: next 335 days, metrics down 10:1 and logs sampled to 1%; archive: a 2%-of-ingest compliance subset, retained 7 years = 2555 days, compressed 4:1):
VhotVwarmVcoldVarchive=500×7=3,500 GB=3.50 TB=3500×23=3,833.33 GB=3.83 TB=10150×335+350×335×0.01=5,025+1,172.5=6,197.5 GB=6.20 TB=4500×0.02×2555=425,550=6,387.5 GB=6.39 TBMonthly cost:
C=3.50(23)+3.83(9.20)+6.20(3.45)+6.39(0.46)=80.50+35.27+21.38+2.94=$140.09/monthCompare that to the naive policy of keeping everything raw, at hot-tier price, for the full 7-year compliance window:
Cnaive=500×2555×100023=$29,382.50/month Reduction=1−29,382.50140.09=99.5%Tiering plus targeted downsampling cuts the steady-state storage bill by roughly 99.5% relative to keeping everything at hot-tier fidelity for the retention window, while still satisfying a 7-year compliance obligation and keeping alerting on full-fidelity data for the first month.
flowchart LR
Ingest[Raw Ingest] --> Hot[Hot 0-7d full res]
Hot --> Warm[Warm 8-30d compressed 3x]
Warm --> Cold[Cold 31-365d downsampled 10x, logs 1%]
Cold --> Archive[Archive 365d+ compliance subset, 4x compressed]
Hot --> AlertEval[Alert Evaluation]
Warm --> AlertEval
Cold --> AlertEval
Archive --> Restore[On-demand Restore]
Restore --> AlertEval
Ingest --> Vault[Signed Compliance Vault]
Vault --> Archive
Trade-offs and pitfalls
- Downsampling metrics to plain averages instead of histograms is the classic mistake: it looks fine until a p99-based SLO alert goes silent because the percentile can no longer be reconstructed from the rollup.
- Deterministic (hash-based) log sampling beats random sampling because it keeps a request's full log trail together instead of showing half a trace.
- Treating compliance retention as "whatever the operational tiers happen to still have" instead of a separate write path is a common design flaw: an operational cost-cutting change (shortening the cold window) can silently create a compliance gap unless the archive copy is decoupled from it.
- Archive restore latency (minutes to hours, depending on backend) is a real trade-off against cost; if legal holds require faster turnaround, that changes the archive tier choice and its price multiplier.
- Budget monitoring has to be proactive: alert on forecasted spend crossing the budget before the bill arrives, not after, since tier transitions are usually async and lag actual ingest growth.
Design access control for an observability platform used by 50 engineering teams: an RBAC model, namespace or tenant isolation, per-team dashboards and saved queries, audit trails, and SSO/SAML integration. What's different about the admin role for platform operators, and how does your answer change between a SaaS deployment and an on-prem one?
Sample Answer
Build access control as three layers that compose: identity and provisioning (who is this, what teams are they in), a policy engine that maps identity plus team to permitted actions on specific resources, and an immutable audit trail of every access decision. The 50-team scale is what makes a policy-engine approach (rather than hand-rolled per-resource checks) worth the setup cost.
Design
flowchart LR
A[SSO / SAML IdP] --> B[SCIM Provisioning]
B --> C[Team / Group Mapping]
C --> D[Policy Engine: OPA]
D --> E[Dashboard and Query ACL]
D --> F[Platform Admin Scope]
E --> G[Audit Log: append-only]
F --> G
- Identity and provisioning: SSO via SAML/OIDC for authentication, SCIM (or LDAP sync) for provisioning, so team membership changes (new hire, team transfer, offboarding) propagate automatically instead of relying on manual grants.
- RBAC model: a small set of coarse roles (Viewer, Editor, TeamLead, PlatformAdmin) combined with resource-scoped permissions (
dashboard:read,dashboard:write,query:run,query:save,data:ingest). Evaluate as(subject, action, resource, context)through a policy engine (e.g., OPA/Rego) rather than scatteringif user.role == 'admin'checks through application code, since 50 teams' worth of dashboard-sharing and cross-team-visibility rules will otherwise become unmaintainable ad hoc logic. - Namespace/tenant isolation: each team's dashboards, saved queries, and (if applicable) raw telemetry are tagged with a team/namespace ID; the policy engine denies cross-team access by default, and sharing is an explicit grant, not an implicit consequence of both teams querying the same underlying data.
- Audit trail: append-only log of every access decision (actor, action, resource, timestamp, allow/deny), shipped to a separate system (SIEM or dedicated log store) so a compromised or misconfigured application instance can't retroactively edit its own audit history.
What's different about the platform-operator admin role
PlatformAdmin needs privileges no team role should have (upgrading the platform, changing global retention policy, accessing any team's data for support purposes) but that scope is exactly what makes it the highest-risk role. Treat it differently from every other role: require a separate approval workflow for admin grants, use time-boxed or break-glass access (temporary elevation with mandatory justification and MFA, auto-expiring) rather than standing admin permissions, and audit admin actions with the same rigor as the audit trail above, ideally with additional real-time alerting since a compromised admin credential is a platform-wide risk, not a single-team risk.
SaaS vs. on-prem
| Aspect | SaaS | On-prem |
|---|---|---|
| Multi-tenancy model | Logical: namespace/tenant ID enforced in the data plane across shared infrastructure | Often single-tenant per deployment already, or offer namespace isolation within one customer's own cluster |
| Identity integration | Centralized IdP the platform operator controls (own SSO tenant, own SCIM sync) | Must integrate with the customer's existing SSO/SAML/LDAP, which varies per customer and requires a flexible integration layer |
| Encryption/key management | Centralized KMS, platform operator manages keys (with option for customer-managed keys for higher-security tiers) | Customer typically owns key management; platform needs to support bring-your-own-KMS |
| Audit log destination | Centralized SIEM the platform operator runs | Must export to the customer's own SIEM/log destination; platform can't assume it owns the audit pipeline |
| Isolation guarantee | Logical isolation is normally acceptable, since the platform operator's own infrastructure enforces it | Some customers deploying on-prem specifically want single-tenant network isolation (K8s namespace + network policy, or fully separate deployment) as a hard requirement, not a preference |
A quick sizing check on audit volume
A frequent unstated assumption is that audit logging is "too expensive to keep forever." For 50 teams with roughly 30 engineers each performing about 200 dashboard/query actions/day at 300 bytes/audit record:
teams, engineers_per_team, actions_per_day, record_bytes = 50, 30, 200, 300
total_engineers = teams * engineers_per_team # 1,500
events_per_day = total_engineers * actions_per_day # 300,000
bytes_per_day = events_per_day * record_bytes # 90 MB/day
total_bytes_1yr = bytes_per_day * 365 # ~32.85 GB/year
At roughly 32.85 GB for a full year of audit history across the whole platform, audit-log retention is cheap relative to telemetry storage; there's rarely a cost reason to truncate it, which supports keeping compliance-grade audit history far longer than raw telemetry.
Trade-offs and pitfalls
- Embedding authorization checks directly in each service (instead of a shared policy engine) is the design that looks fine at 5 teams and becomes unmaintainable at 50, because every new sharing rule requires a code change in every service that touches dashboards or queries.
- Standing admin access (a permanent PlatformAdmin role assignment with no expiry) is the most common real-world gap between a documented RBAC design and actual practice; break-glass, time-boxed elevation closes that gap but requires operational discipline to actually use.
- On-prem deployments that assume the SaaS identity/audit architecture transfers unchanged will hit friction the first time a customer's SSO provider or compliance requirement doesn't match the platform operator's assumptions; the abstraction boundary (pluggable IdP, pluggable audit sink) needs to be designed in from the start, not retrofitted.
- Least-privilege between on-call engineers and management is a common refinement: an on-call engineer needs broad read access across teams during an incident but not write/admin access, while a manager might need visibility without either; collapsing both into a single "Editor" role loses that distinction.
Telemetry pipelines have to make a consistency trade-off: eventual consistency, at-least-once, at-most-once, or exactly-once delivery. For each model, explain what it means for the correctness of a metric aggregation, and describe concrete techniques (idempotent writes, deduplication IDs, write-ahead logs) you'd use to keep a high-throughput pipeline correct under one of the weaker guarantees.
Sample Answer
Direct Answer
Pick the weakest delivery guarantee that still lets you reconstruct correctness at the aggregation layer, rather than paying the latency and complexity cost of strong guarantees everywhere. For most telemetry pipelines that means at-least-once delivery plus idempotent, deduplicated writes: true end-to-end exactly-once delivery across independent systems does not really exist, what you can build is a system that behaves as if it were exactly-once because duplicate deliveries have no effect on the result.
Structured Elaboration
At-most-once
A point may be silently lost, never duplicated. Cheapest and lowest-latency, but every dropped point is gone. Acceptable only for high-volume, low-value telemetry where a small, unbiased loss rate does not meaningfully change an aggregate.
At-least-once
No steady-state data loss, but retries can produce duplicates. This is the practical default for most producers (send, wait for ack, retry on timeout, occasionally re-deliver something that actually did arrive). Correctness at the aggregation layer requires deduplication: an idempotent write keyed by a unique identifier, so a duplicate delivery does not change the result.
Exactly-once
The goal is a metric aggregation that reflects each event's effect precisely once. In practice this is built, not given by any single component: it requires end-to-end idempotency (deterministic dedup keys), transactional or checkpointed writes (a write-ahead log tying the aggregate update and the consumer offset commit together atomically), and durable state. The cost is coordination overhead and added write latency.
Eventual consistency
Aggregates may be temporarily stale or incomplete, converging to correct only after all in-flight and late-arriving events settle. Handled with event-time windowing, watermarks that define how late an event can arrive before it's excluded, and a correction mechanism (recompute or emit a delta) for events that show up after their window closed.
Making a weaker guarantee correct in practice
- Deduplication IDs: a producer-assigned, monotonically increasing sequence number per stream. The consumer tracks a high-water mark per stream and discards anything at or below it.
- Idempotent writes: aggregation writes as upserts keyed by (metric, labels, timestamp window), so replaying the same input overwrites rather than accumulates.
- Write-ahead logs: the consumer writes its aggregate update and its new checkpoint offset in a single atomic step, then acks the upstream offset. On crash and restart, it replays only the un-checkpointed range, which is safe because that replay is itself idempotent.
Checkpoint and dedup flow
flowchart LR
EVT["Event with sequence id"] --> DEDUP{"seq > high-water mark?"}
DEDUP -->|"yes, new"| APPLY["Apply to Aggregate"]
DEDUP -->|"no, duplicate"| DISCARD["Discard"]
APPLY --> CKPT[("Atomic: Aggregate + Checkpoint")]
CKPT --> ACK["Ack Offset Upstream"]
Worked Example
Assume 3 producers each sending 100 events into a 1-minute rollup window, for a correct total of 300 events. One producer's network connection times out on 2 of its events, so it retries both, and (because at-least-once delivery does not deduplicate at the transport layer) both the original and the retried copy arrive.
Without deduplication, the consumer counts:
300+2=302 events overcount=300302−300≈0.67%A small number for one window, but a systematic bias that compounds across every window and every retried event at scale, not a one-time rounding error.
With deduplication (each producer assigns a monotonically increasing sequence number per stream, and the consumer tracks a high-water mark per producer, discarding anything at or below the mark it has already seen):
counted events=300(both duplicate deliveries discarded, since their sequence numbers are≤the high-water mark)The dedup mechanism restores the exact correct count using only transport-layer at-least-once delivery, no exactly-once broker feature required.
Trade-offs and Pitfalls
Sequence-number-based dedup (a high-water mark per stream) only works correctly for pure retries, redelivery of the same sequence number. It breaks if the transport can reorder events beyond a small window, since a legitimately new event with a lower sequence number than one already seen would be incorrectly discarded as a duplicate. If your transport allows significant reordering, dedup by explicit unique event ID with a time-bounded lookup window instead of a strict high-water mark.
Exactly-once semantics inside a single system (say, a Kafka Streams application with transactional writes) do not extend automatically to an external sink. The moment you write to an external time-series database or object store, you are back to needing idempotent writes at that boundary, transactional guarantees inside the stream processor do not make the external write exactly-once by themselves.
Choosing at-least-once plus dedup as the default is right for the vast majority of telemetry, but not for every case: a billing-critical count (metered usage that directly drives a customer invoice) may warrant paying the extra coordination cost for stronger guarantees specifically at that boundary, while everything else stays on the cheaper default.
Design a cross-signal correlation index that lets an engineer jump quickly from a metric anomaly to the relevant logs and traces for root-cause analysis. What identifiers would you require every signal to carry, how would you build and maintain that mapping, and how would you keep queries across systems fast at scale, including when an identifier is missing?
Sample Answer
Direct answer
Require every signal (metric, log line, trace span) to carry a small set of common identifiers, most importantly trace_id (or a request/correlation ID where full tracing isn't wired up yet), plus service_name and a timestamp. Build a lightweight mapping index, keyed by those identifiers, that points to where each related log or span physically lives (shard, offset, time range) rather than storing the events themselves twice. When an identifier is missing, fall back to narrowing by time window and service first, then use a scored, probabilistic match on top of that narrowed candidate set instead of searching the whole corpus.
Structured elaboration
Required identifiers per event:
trace_id/span_id: the strong identifier, present when full distributed tracing is instrumented.service_name,host_or_pod_id: contextual keys usable even when tracing isn't present.event_timestamp: needed for the time-window fallback and for ordering.metric_dimensions/anomaly_id: on the metrics side, the anomaly detector emits an anomaly document carrying whatever identifiers were present on the underlying series.
Index structure, not a duplicate copy: the mapping store is a pointer index (topic/partition/offset for logs, trace/span ID for traces), not a second copy of log or trace bodies. Logs and traces stay in their existing systems (search index, trace store); the correlation index only tells the query which shard and offset to fetch.
Query flow:
- Direct ID lookup, if the anomaly carries a
trace_id: O(1) pointer lookup into the mapping store. - Indexed search, if not: query the log/trace index filtered by
service_nameand a time window around the anomaly. - Probabilistic fallback, if identifiers are sparse: score candidates from step 2 by temporal proximity, shared host, and payload fingerprint, and return ranked results with a visible confidence, not a silent best guess.
flowchart LR
A[Metric anomaly] --> ID{trace_id present?}
ID -- yes --> M[(Mapping index: id -> pointer)]
M --> L1[Fetch log/trace by pointer]
ID -- no --> TW[Filter: service + time window]
TW --> SC[Score candidates: proximity, host, fingerprint]
SC --> L2[Ranked candidates + confidence]
Maintaining the mapping: on ingest, if a strong identifier is present, upsert a bounded (capped) pointer list into the mapping store for that ID and set a TTL matching the signal's own retention. A background job prunes expired entries so the index doesn't grow unbounded past what the underlying logs and traces themselves retain.
Worked example
Index sizing. Assume the cluster produces 50,000 events/sec across logs and trace spans combined:
eventsPerDay=50,000×86,400=4.32×109Each mapping entry (topic, partition, offset, timestamp, service ID) is about 40 bytes:
indexBytesPerDay=4.32×109×40 bytes=172.8 GB/dayAt a 7-day retention window matching typical hot log retention:
indexBytesRetention=172.8×7=1,209.6 GB≈1.21 TBThis is the number that justifies capped pointer lists and TTL-based pruning in the design above: an unbounded, uncapped index at this event rate would grow past a terabyte inside a week even though it stores no event bodies, only pointers.
Fallback candidate-set size when the identifier is missing. With 200 services sharing the 4.32×109 daily events roughly evenly:
eventsPerServicePerDay=2004.32×109=21,600,000Narrowing to a ±2.5s window (5s total) around the anomaly timestamp before service-level scoring:
timeReductionFactor=586,400=17,280 candidateSetSize=17,28021,600,000=1,250 eventsFiltering by service and a 5-second window before running any fuzzy scoring shrinks the search space from 21.6 million events/day for that service down to about 1,250 candidates, small enough for a scoring pass to run against on every anomaly without scanning the full day's log volume.
Trade-offs & pitfalls
| Fallback stage | Precision | Cost | When it's needed |
|---|---|---|---|
| Direct ID lookup | Exact | O(1) | trace_id present (the common case in fully-instrumented services) |
| Service + time window | Approximate, but bounded | O(candidates), here ~1,250 | trace_id missing, service known |
| Fuzzy scoring | Ranked, with confidence | O(candidates) scoring passes | trace_id missing and multiple plausible matches remain |
Common wrong turns: treating the mapping index as a full copy of the underlying signals instead of a pointer index (this doubles storage for no correlation benefit and creates a second source of truth to keep consistent); running the fuzzy-scoring pass over the entire day's events instead of narrowing by service and time window first, which turns an O(1,250) scoring problem into an O(21,600,000) one for no accuracy gain; and silently returning the single top-scored fuzzy match without surfacing its confidence, which looks like a correlation but is actually a guess an on-call engineer has no way to sanity-check.
You need accurate 95th and 99th percentile queries over weeks or months of data without scanning raw points every time. Design an approach using pre-aggregation, materialized rollups, and approximate sketch algorithms (t-digest or HDR histograms), including how you'd merge partial sketches from many collectors and what accuracy you give up for that speed.
Sample Answer
Direct answer
Don't compute percentiles from raw points at query time. Have every collector maintain a local mergeable sketch (t-digest or an HDR histogram) over each rollup window, ship the sketch instead of the raw values, and merge sketches hierarchically (minute into hour, hour into day) so a query over weeks or months merges a small number of pre-combined objects instead of scanning raw samples. The accuracy you give up is bounded and tunable: it comes from the sketch's compression parameter, not from randomly dropping data, so you can trade a known amount of tail-percentile error for a known reduction in merge work.
Structured elaboration
Sketch choice:
- t-digest: variable bucket width, denser (more accurate) near the tails where percentile queries usually care most, mergeable, size controlled by a compression parameter δ (roughly, number of centroids).
- HDR histogram: fixed relative-error buckets across a configured value range, deterministic worst-case error bound, very fast to merge (array addition), best when the metric's range is known and bounded (for example request latency in milliseconds).
- Use t-digest for open-ended or skewed distributions; use HDR when the value range is known and the deterministic bound matters more than tail density.
Hierarchical rollups: each collector emits a 1-minute sketch. A rollup job merges 60 one-minute sketches into an hourly sketch, and 24 hourly sketches into a daily sketch. Retention is tiered: keep 1-minute sketches for 7 days, hourly for 30 days, daily beyond that. This is what makes "merge partial sketches from many collectors" tractable at high fan-in: each collector contributes one small sketch per window rather than a stream of raw points, and the merge tree does the aggregation once instead of at every query.
Merging at query time: the query picks the coarsest rollup tier that covers the requested range, merges those objects (t-digest merge is just concatenating and re-clustering centroids; HDR merge is bucket-wise addition), and computes the percentile from the merged sketch.
Handling very high fan-in (many collectors, very high ingest volume): the same hierarchy scales horizontally: instead of one job merging all collectors' sketches directly, shard collectors into groups, merge within a group first, then merge group-level sketches at the next tier. This bounds the fan-in of any single merge step regardless of how many collectors exist upstream.
flowchart LR
C1[Collector] -- 1-min sketch --> M1[Minute merge]
C2[Collector] -- 1-min sketch --> M1
M1 -- 60 merged --> H[Hourly sketch]
H -- 24 merged --> D[Daily sketch]
Q[Percentile query] -- picks coarsest tier covering range --> H
Q --> D
D -- retained 30d+ --> COLD[(Cold rollup store)]
Worked example
Merge count reduction. For a 30-day 99th-percentile query, merging at 1-hour granularity versus 1-minute granularity:
hourlyMerges=24×30=720 minuteMerges=1440×30=43,200 hourlyMergesminuteMerges=72043,200=60×Querying against hourly rollups means merging 60x fewer objects than querying against minute rollups, which is the primary win of hierarchical sketching: it shrinks the object count the query has to combine, not just the byte count of any one object.
t-digest size at a given compression. A t-digest with compression δ holds roughly δ centroids, each storing a mean and a count (two 8-byte doubles = 16 bytes):
digestBytes(δ)=16δAt δ=100: digestBytes=1,600 bytes per digest.
Storage over a year, at scale. For services=5,000 tracked series, keeping hourly digests for 365 days:
digestStorageYear=5,000×(24×365)×1,600 bytes=70.08 GBCompare to keeping raw 15s points for those same 5,000 series for a year, at 16 bytes/point:
rawStorageYear=5,000×1586,400×365×16 bytes=168.192 GB digestStorageYearrawStorageYear=70.08168.192=2.4×Worth being honest about what this number says: at δ=100, sketch storage is only 2.4x smaller than keeping the equivalent raw points, because each digest is representing many raw points (5,760 raw points/day per series versus 24 digests/day per series, so each digest stands in for 240 points). The real win from sketching at this compression level is the 60x merge-count reduction above, not storage. If storage is the binding constraint, dropping δ to 50 halves digest size (800 bytes) and roughly doubles the storage reduction factor to about 4.8x, at the cost of coarser centroid resolution and larger tail error.
Trade-offs & pitfalls
| Compression δ | Digest size | Tail accuracy | Storage reduction vs. raw (1yr, hourly) |
|---|---|---|---|
| 200 | 3,200 bytes | Best | ~1.2x |
| 100 | 1,600 bytes | Good | ~2.4x |
| 50 | 800 bytes | Coarser | ~4.8x |
Common wrong turns: merging sketches from different tiers as if they were interchangeable (a 1-minute sketch and an hourly sketch built with different compression settings can merge, but mixing merge granularities inconsistently across a query makes the resulting error bound harder to reason about, so keep the compression parameter fixed across a tier); treating a merged sketch's percentile as exact (it is an estimate with error concentrated where the sketch is coarsest, which for t-digest is the middle of the distribution, not the tails); and picking HDR for an unbounded-range metric (queue depth that can spike arbitrarily) where the fixed value range either clips outliers or forces an oversized histogram, when t-digest's variable bucketing is the better fit there.
Unlock Full Question Bank
Get access to all 27 Observability and Monitoring Architecture interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.