Handling Large Scale Data and Time Series Data Questions
Design for efficient storage and querying of massive datasets. Understand time-series data patterns (metrics, logs), specialized solutions like InfluxDB or TimescaleDB, and archiving strategies for historical data.
MediumTechnical
55 practiced
Implement a Python streaming program (pseudo or real code) that consumes a Prometheus remote_write-like stream and computes the top-k most active series by sample count in a memory-constrained environment. Explain memory/time complexity and any streaming algorithms you use (e.g., count-min sketch, lossy counting, heap).
Sample Answer
Approach: use a Count-Min Sketch (CMS) to approximate per-series sample counts with bounded memory, plus a small fixed-size min-heap (or Space-Saving / Misra-Gries) to track the top-k candidate series precisely (or with bounded error). CMS gives O(1) update per sample; heap maintains current top-k by estimated counts.Key points:- CMS parameters: width ~ e/ε, depth ~ ln(1/δ) control error ε and failure prob δ.- Time: O(depth) per update ≈ O(1) with small depth. Heap ops O(log k) occasionally.- Space: O(width*depth) for CMS + O(k) for heap/map.- Errors: CMS overestimates counts (no underestimation). Using heap with CMS uses estimated counts; heavy hitters are likely correct but rare keys may be false positives. For stricter guarantees use Space-Saving algorithm (deterministic, tracks k counters with error ≤ total_count/k).- Edge cases: extremely high cardinality with many low-frequency series — tune CMS size or use sampling; label normalization to limit key explosion; handle clock skew/batch samples by adding deltas.
python
# Python-like streaming implementation
import heapq
import mmh3 # murmurhash3 library, assume installed
from collections import defaultdict
class CountMinSketch:
def __init__(self, width, depth, seed=0):
self.width = width
self.depth = depth
self.tables = [[0]*width for _ in range(depth)]
self.seeds = [seed + i*0x9e3779b1 for i in range(depth)]
def _hashes(self, key):
for s in self.seeds:
yield mmh3.hash(key, s) % self.width
def add(self, key, delta=1):
for i, idx in enumerate(self._hashes(key)):
self.tables[i][idx] += delta
def estimate(self, key):
return min(self.tables[i][idx] for i, idx in enumerate(self._hashes(key)))
class TopKTracker:
def __init__(self, k, cms):
self.k = k
self.cms = cms
self.entry_map = {} # key -> (est_count)
self.heap = [] # min-heap of (est_count, key)
def process_sample(self, series_key):
self.cms.add(series_key, 1)
est = self.cms.estimate(series_key)
if series_key in self.entry_map:
# update: push new tuple; lazy removal when popped
self.entry_map[series_key] = est
heapq.heappush(self.heap, (est, series_key))
return
if len(self.heap) < self.k:
self.entry_map[series_key] = est
heapq.heappush(self.heap, (est, series_key))
else:
min_est, min_key = self.heap[0]
if est > min_est:
# evict until heap top is valid
while self.heap and self.entry_map.get(self.heap[0][1], None) != self.heap[0][0]:
heapq.heappop(self.heap)
if self.heap and est > self.heap[0][0]:
# remove evicted
_, evicted = heapq.heappop(self.heap)
self.entry_map.pop(evicted, None)
self.entry_map[series_key] = est
heapq.heappush(self.heap, (est, series_key))
def topk(self):
# return stable snapshot
items = [(c,k) for c,k in self.heap if self.entry_map.get(k)==c]
items.sort(reverse=True)
return items
# Example usage: streaming loop consuming prometheus remote_write-like samples
cms = CountMinSketch(width=2000, depth=5)
tracker = TopKTracker(k=100, cms=cms)
# for sample in stream:
# series_key = serialize_labels(sample.labels)
# tracker.process_sample(series_key)
# At any time: tracker.topk()MediumTechnical
62 practiced
Prometheus is producing many noisy alerts because of label-level fluctuations and high-cardinality series. As an SRE, propose concrete strategies to reduce alert noise without losing essential observability. Include changes at metric collection, scrape/relabeling, recording rules, and alerting stages.
Sample Answer
Situation: Prometheus is firing many noisy alerts due to label churn and high-cardinality time series. The goal is to reduce noise while preserving actionable observability.Approach — concrete strategies by stage:1) Metric collection- Reduce cardinality at source: avoid emitting per-request IDs, user IDs, or timestamps as labels. Aggregate counters (e.g., emit http_requests_total{code,route} not request_id).- Use histogram buckets sparingly; prefer summaries where appropriate.- Example: change instrumentation to record route templates instead of raw paths.2) Scrape / relabeling- Drop noisy labels early via relabel_configs to prevent series explosion:- Use keep/action to limit to essential labels (service, env).3) Recording rules- Precompute stable aggregates to reduce alerting on raw series. Examples: - rate-aggregates per service: sum by(service) (rate(http_requests_total[5m])) - error_ratio = sum(errors)/sum(requests) by (service)- Use recording rules to downsample and smooth with increase()/rate() over longer windows.4) Alerting stage- Alert on aggregated recording rules, not per-instance series: alert when service-level error_ratio > threshold for N minutes.- Use for: duration to debounce flapping (e.g., for: 10m).- Use severity labels and route noisy/experimental alerts to low-urgency channels.- Use alertmanager grouping and inhibition to suppress related alerts.- Example alert:Trade-offs & reasoning- Dropping labels reduces debug granularity — mitigate by keeping them in high-cardinality logs/traces and using recording rules that preserve drill-down dimensions.- Longer evaluation windows reduce sensitivity to spikes but may delay detection; choose windows aligned with SLOs.Outcome- Fewer noisy alerts, faster signal-to-noise, and preserved ability to root-cause via logs/traces or ad-hoc queries.
yaml
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_request_id]
action: drop
- regex: '.*-instance-\d+'
source_labels: [instance]
replacement: 'instance'
action: replaceyaml
alert: HighServiceErrorRate
expr: job:error_ratio:5m{env="prod"} > 0.05
for: 10m
labels: { severity: critical }HardTechnical
61 practiced
Describe how you would instrument and test disaster recovery (DR) for a global time-series platform. Outline DR test scenarios (regional outage, data corruption, full-cluster rebuild), the test plan, validation queries and metrics to verify success, and how often to run these tests.
Sample Answer
Requirements & constraints:- RTO ≤ 15 min for reads, ≤ 1 hour for full write recovery; RPO ≤ 5 min for critical metrics, ≤ 1 hour for low-priority; global traffic, multi-region clusters, zonal failures possible, compliance for data integrity.High-level approach:- Treat DR as code: automated playbooks, IaC to recreate infra, deterministic data cut/replication tests, and synthetic traffic to validate behavior. Instrument with feature flags to run safe failovers in blue/green manner.DR test scenarios (examples):1) Regional outage (control-plane + data-plane in a region): simulate by disabling region in routing and stopping region control-plane VMs.2) Data corruption (logical): inject a corrupt batch/segment in a replica or simulate bad schema change.3) Full-cluster rebuild: destroy non-production cluster and rebuild from backups + WALs.4) Split-brain / network partition: isolate subset of replicas.5) Gradual resource exhaustion: throttle disk I/O / CPU to mimic degraded performance.Test plan (for each scenario):- Pre-checks: snapshot configs, baseline metrics, quorum health.- Execute: run automated playbook (Terraform/Ansible + orchestration), flip traffic via global load-balancer (canary then ramp).- Validate: run validation queries, compare metrics, confirm write/read continuity.- Rollback: automated rollback path and postmortem.Validation queries & metrics:- Read correctness: SELECT COUNT(*) / SUM(value) over time windows against golden dataset; tail sampling of raw series to ensure no missing points.- Write path: write synthetic series with monotonic sequence IDs and assert ingestion latency < RTO and no gaps.- Consistency: compare checksums/hashes of time-buckets (per-hour parquet segments) between primary and recovered replicas.- WAL recovery: verify last-applied timestamp <= RPO.- Latency & availability: p95/p99 write and read latency, error rate, 5xx rate, successful ingestion rate.- Capacity: disk usage, compaction backlog, replication lag.- Business SLOs: data freshness, query SLA, percent of queries served within thresholds.- Alert validation: ensure paging/ops alerts fire on expected conditions.Instrumentation:- Synthetic clients (k6/locust) generating realistic ingestion/query patterns with labeled markers.- Metrics exporters for replication_lag, wal_position, last_ingested_timestamp, segment_checksums.- Automated validators that pull checksums and run diff, produce pass/fail.Frequency & scope:- Canary smoke tests: daily in pre-prod.- Regional outage drills: quarterly per region (rotate primary region yearly).- Full-cluster rebuild: semi-annually for production-critical clusters; quarterly for non-prod.- Data corruption / WAL restore: monthly in staging; annually in prod with full verification.- After any infra, config, or schema change.Success criteria:- Automated test pass (validation checksums match within RPO), SLAs met (99.9% of queries within p95 latency), no data gaps for critical series, rollback executed cleanly if validation fails. Post-test report with timing, gaps, lessons and remediation tickets.Operationalization:- Run DR as part of SRE playbook with scheduled runbooks, runbooks as code, canary first in single tenant/daylight window, and run postmortems with measurable remediation.
MediumSystem Design
54 practiced
Design a backup and restore strategy for a petabyte-scale time-series deployment that must maintain 99.99% availability. Consider snapshotting, incremental backups, object storage, retention, restore-time objectives (RTO), and restore-point objectives (RPO). Describe trade-offs and how to test restores.
Sample Answer
Requirements & constraints:- Data: petabyte-scale time-series (high write rate, many small objects)- Availability: 99.99% (≈4.38 min downtime/month)- RPO: target minutes to 1 hour depending on SLAs per dataset- RTO: target minutes to <1 hour for critical subsets; bulk restores can be longer- Cost, retention (e.g., 30d hot, 1y cold), and compliance constraintsHigh-level strategy:1. Snapshot + WAL/incremental hybrid- Use storage-level consistent snapshots (filesystem or block) taken frequently (e.g., hourly) for full-consistent point-in-time images of cluster metadata and node-local indices.- Continuously ship write-ahead logs (WAL) or change-logs to durable object storage (S3/GCS) for near-real-time incremental recovery. WAL objects are small, append-friendly, and partitioned by shard/time.2. Object storage as canonical backup tier- Store snapshots and WAL segments in object storage with lifecycle policies: hot (30d), archive (Glacier/Archive) for longer retention.- Use deduplication and compression for chunked time-series blocks to reduce size.3. Catalog & immutable manifests- Maintain a backup catalog (e.g., DynamoDB/Cloud Spanner) with manifests mapping snapshot times, shards, and WAL ranges to object locations and checksums.4. Restore patterns- Fast path (99.99 SLO): For node/network failure, perform local snapshot restore + WAL replay for affected shards to minimize impact—automated orchestration rehydrates only necessary shards.- Full cluster restore: Parallel restore of snapshot shards from object storage with concurrent WAL replay. Prioritize critical tenants/shards for faster RTO.Trade-offs:- Frequency of snapshots vs cost: more frequent snapshots reduce RPO but increase storage and I/O. WAL shipping reduces snapshot frequency needs but adds complexity.- Consistency vs performance: Quiescing writes for crash-consistent snapshots impacts availability—use copy-on-write snapshots or cluster-coordinated checkpoints to avoid pause.- Restore speed vs cost: Keeping recent snapshots in hot storage accelerates restores at higher cost.Testing restores:- Automated restore drills monthly per environment: run partial and full restores to staging, validate checksums, query correctness against golden data, measure RTO/RPO.- Chaos tests: simulate region failure and verify automated failover + restore path.- Sampling validation: randomly sample restored shards and run ingestion/queries; monitor metrics.- Maintain runbooks and automated playbooks (IaC) to run restores and tear down environments.Observability & automation:- End-to-end metrics: snapshot success/failure, WAL lag, restore throughput, object storage latency.- Alerts for missed snapshots, growing WAL lag, catalog inconsistencies.- Use orchestration (Kubernetes operators/Ansible) to automate restore execution and parallelism tuning.This design balances low RPO with reasonable cost and provides fast localized restores to meet 99.99% availability while enabling full recovery through tested, automated procedures.
MediumTechnical
76 practiced
Given the table:Write a SQL query (Postgres/TimescaleDB) to compute the 1-minute moving average of 'cpu' per host over the last 24 hours and return the top 10 hosts by maximum 1-minute avg CPU spike in that window. Use Timescale functions if helpful.
sql
CREATE TABLE metrics (
host text,
metric_name text,
ts timestamptz,
value double precision
);Sample Answer
Approach: bucket the last 24 hours into 1-minute intervals per host (using TimescaleDB time_bucket_gapfill to fill missing minutes), compute the per-minute average of metric 'cpu', then for each host find the maximum 1-minute average (i.e., the largest spike) and return top 10 hosts.Key points:- time_bucket_gapfill ensures missing minutes appear (useful for continuity/alerting).- AVG over the bucket gives the 1-minute average.- Taking MAX(avg_cpu) per host finds the largest 1-minute spike in the 24h window.- Filter metric_name='cpu' in AVG to ignore other metrics.Edge cases:- Hosts with sparse data: gapfill will produce NULL avg_cpu; we exclude NULLs.- If you want only hosts with at least N samples per minute, add a COUNT(*) filter in per_min.- For very high cardinality, consider pre-aggregating in a continuous aggregate for performance.Alternative: compute rolling window avg using window functions if you need overlapping 1-minute windows (e.g., AVG(value) OVER (PARTITION BY host ORDER BY ts RANGE INTERVAL '1 minute')) — but that can be heavier than bucketing.
sql
WITH per_min AS (
SELECT
host,
time_bucket_gapfill('1 minute', ts) AS minute,
AVG(value) FILTER (WHERE metric_name = 'cpu') AS avg_cpu
FROM metrics
WHERE ts >= now() - interval '24 hours'
GROUP BY host, minute
)
SELECT host, max(avg_cpu) AS max_1min_avg_cpu
FROM per_min
WHERE avg_cpu IS NOT NULL
GROUP BY host
ORDER BY max_1min_avg_cpu DESC
LIMIT 10;Unlock Full Question Bank
Get access to hundreds of Handling Large Scale Data and Time Series Data interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.