Storage Systems and Infrastructure Questions
The physical and infrastructure layer beneath databases: disk and volume management, storage redundancy and RAID levels, storage services, and how storage architecture affects durability and performance. Covers matching storage configuration to reliability and throughput requirements. Serves infrastructure and systems roles that own the hardware substrate.
Design a graceful degradation strategy for a web service that depends on several backend databases and caches when one or more downstream systems become slow or unavailable. Prioritize user-facing functionality, detail circuit-breaker behavior, fallback caches, degraded UX, and how to communicate status to users and internal stakeholders.
Sample Answer
Requirements & priorities:
- Preserve core user flows (read-mostly pages, critical writes like payments) with best-effort correctness.
- Minimize user-visible errors and latency; degrade features rather than fail hard.
- Provide clear internal and user-facing status and fast recovery paths.
- Respect SLOs / error budget and alert appropriately.
High-level strategy:
- Defensive timeouts & bulkheads
- Enforce per-downstream timeouts (short, conservative) and thread/connection pool limits (bulkheads) so one slow DB/cache doesn't exhaust resources.
- Circuit breakers
- Implement per-dependency circuit breakers (closed → half-open → open) with:
- Sliding-window error rate + latency threshold.
- Min callers and cooling period before open.
- Exponential backoff for retry attempts on half-open.
- Fast-fail when open, returning fallback or degraded response.
- Emit metrics: state transitions, error counts, latency percentiles. Alert when many circuits open or critical dependency open > threshold.
- Fallback caches & data staleness
- Multi-tier cache strategy:
- Primary cache (redis/memcached) with TTLs tuned.
- Read-through + write-through patterns where feasible.
- Stale-while-revalidate: serve slightly-stale data when origin unavailable and trigger background refresh.
- Pre-warmed emergency cache snapshots for critical endpoints (daily export).
- Define staleness budgets per data type (e.g., user profile: 24h, inventory: 30s).
- Graceful degraded UX
- Feature gating and progressive degradation:
- If personalization DB slow, serve generic or cached recommendations with a banner: “Personalized results temporarily unavailable.”
- If non-critical analytics fail, hide graphs and surface last known values with “last updated at…”.
- For writes: allow local queuing (client or service-level durable queue) with async processing + clear status in UI (“Order received; confirming payment”).
- Provide clear, minimal messaging to users (avoid technical jargon) and show ETA where possible.
- Observability & runbook
- Dashboards: per-dependency health, circuit states, cache hit/miss, queue depth, user error rate, SLA burn rate.
- Alerts: severity tiers—P1 if critical dependency open causing >X% failed critical flows; P2 for persistent latency increases.
- Automated remediation: auto-scale caches/replicas, failover DB replicas, or route reads to read-only replicas. Tie into circuit-breaker to relax timeouts only after failover verification.
- Runbooks: explicit steps for common states (open circuit for DB, cache eviction storms, reconciling queued writes).
- Communication strategy
- Internal: Pager duty + Slack channel with automated status posts; include root-cause hints, affected endpoints, mitigation actions, and ETA.
- External: Status page + in-app status banner. Post short, factual updates: impact, affected features, mitigation in progress, and expected recovery time. Update at regular intervals and after resolution.
Trade-offs & notes:
- Stale-while-revalidate increases correctness risk — limit to read-only or non-financial contexts.
- Aggressive short timeouts protect resources but may increase false positives; tune with traffic testing.
- Pre-warmed caches increase storage/complexity but greatly reduce outage blast radius.
This design balances availability and correctness: keep users served with clear degraded UX, detect+isolate failures quickly via circuit breakers, use fallback caches to absorb backend outages, and ensure operators and users are informed for rapid recovery.
Write a Python program that reads simplified MySQL binlog events from stdin where each line is a JSON object such as {"type": "query", "sql": "ALTER TABLE users ADD COLUMN age int"}. The program should detect and alert (print to stdout) when a DDL statement affects any table in a monitored list and be resilient to duplicate events and temporary restarts.
Sample Answer
Approach:
- Read JSON lines from stdin, parse events, ignore non-query events.
- For each query event, detect if it is a DDL (CREATE/ALTER/DROP/RENAME) and extract affected table(s) with robust regex for common cases.
- Deduplicate using a persisted set of event hashes (SHA256 of the normalized JSON or SQL) stored on disk so restarts don't re-alert.
- When a DDL affects any monitored table, print an alert line (stdout). Keep the program resilient to duplicates and temporary restarts by persisting state.
Code implementation:
#!/usr/bin/env python3
import sys
import json
import re
import hashlib
from pathlib import Path
# Config: monitored tables in form ["db.table", "table"]
MONITORED = {"users", "app.users", "orders"}
STATE_FILE = Path("/tmp/binlog_alert_state.json")
MAX_STATE = 10000 # cap stored hashes to bound size
# load persisted seen set (list of hex digests)
if STATE_FILE.exists():
try:
seen = set(json.loads(STATE_FILE.read_text()))
except Exception:
seen = set()
else:
seen = set()
ddl_re = re.compile(r'^\s*(CREATE|ALTER|DROP|RENAME)\b', re.I)
# crude table extractor for patterns like `schema`.`table`, schema.table, `table`, table
table_re = re.compile(r'(?:from|table|rename\s+to|rename)\s+`?([A-Za-z0-9_]+)`?(?:\.`?([A-Za-z0-9_]+)`?)?', re.I)
def normalize_event(ev):
# create stable representation: type + normalized sql whitespace
return f"{ev.get('type','')}\n{re.sub(r'\\s+',' ', ev.get('sql','') or '').strip()}"
def hash_event(normalized):
return hashlib.sha256(normalized.encode()).hexdigest()
def extract_tables(sql):
sql = sql.strip()
# quick check for DDL
if not ddl_re.match(sql):
return set()
tables = set()
# find patterns like "ALTER TABLE `db`.`table` ..." or "CREATE TABLE table ..."
# We'll look for occurrences of "table" keyword and also standalone backtick/name
# Use multiple regex passes to be resilient
# First, direct "table X" occurrences
for m in re.finditer(r'\bTABLE\s+((?:`?[A-Za-z0-9_]+`?\.)?`?[A-Za-z0-9_]+`?)', sql, re.I):
token = m.group(1)
parts = [p.strip('`') for p in token.split('.', 1)]
if len(parts) == 2:
tables.add(f"{parts[0]}.{parts[1]}")
tables.add(parts[1])
else:
tables.add(parts[0])
# Also handle RENAME TO statements: capture target table(s)
for m in re.finditer(r'\bRENAME\s+(?:TABLE\s+)?`?([A-Za-z0-9_]+)`?(?:\s+TO\s+`?([A-Za-z0-9_]+)`?)', sql, re.I):
a = m.group(1); b = m.group(2)
tables.add(a); tables.add(b)
# DROP/CREATE may include multiple tables like DROP TABLE a,b
for token in re.split(r'[,\s]+', re.sub(r'^[A-Za-z]+\s+TABLE\s+', '', sql, flags=re.I)):
t = token.strip('` ,;')
if t.isidentifier():
tables.add(t)
return {t for t in tables if t}
def persist_state():
try:
# store as list to be json serializable
lst = list(seen)[-MAX_STATE:]
STATE_FILE.write_text(json.dumps(lst))
except Exception:
pass
try:
for line in sys.stdin:
line = line.strip()
if not line:
continue
try:
ev = json.loads(line)
except json.JSONDecodeError:
continue
if ev.get("type") != "query":
continue
sql = ev.get("sql","")
normalized = normalize_event(ev)
h = hash_event(normalized)
if h in seen:
continue # duplicate
# extract tables from DDL
tables = extract_tables(sql)
# check monitored intersection (support both "db.table" and "table")
if tables and any(t in MONITORED for t in tables):
# print alert (could be JSON or plain)
print(json.dumps({"alert":"DDL on monitored table","sql":sql,"tables":list(tables)}))
sys.stdout.flush()
# record as seen and persist occasionally
seen.add(h)
if len(seen) % 100 == 0:
persist_state()
except KeyboardInterrupt:
pass
finally:
persist_state()
Key points:
- Deduplication: SHA256 of stable normalized event ensures idempotency across replays.
- Persistence: state file survives restarts; cap size to bound disk/memory.
- Table extraction: regex-based extraction handles common DDL patterns; not a full SQL parser but pragmatic for monitoring.
- Alerting: prints JSON alerts to stdout so supervisor/collector can ingest.
Time/space complexity:
- Per event: O(L) where L is SQL length. Space: O(N) for N stored hashes (bounded by MAX_STATE).
Edge cases:
- Complex DDL (multiple statements, procedural code) may evade regex; consider integrating a SQL parser (sqlparse) for heavy use.
- Schema-qualified names and quoting handled conservatively; fully-qualified with backticks supported.
- If state file is corrupted, program continues and re-alerts (fail-open) — choose fail-open vs fail-closed per operational policy.
Alternatives:
- Use a proper SQL parser for accuracy.
- Persist state in Redis or a durable stream consumer group (Kafka) for distributed resilience.
Design a backup and restore strategy for a distributed NoSQL store such as Cassandra to meet RPO of 15 minutes and RTO of 1 hour. Discuss snapshots, incremental backups, commit-log (or WAL) archiving, cross-region replication, restore verification, and testing your restore process.
Sample Answer
Requirements:
- RPO = 15 minutes (max data loss)
- RTO = 1 hour (full service or degraded read-only within 1 hour)
- Geo-resilience and tested recoverability
High-level approach:
- Combine periodic snapshots + frequent incremental backups + commit-log (SSTable + commitlog) archiving + cross-region replication. Use automation and verification to meet RPO/RTO.
Components & responsibilities:
- Snapshots
- Daily full snapshots per node (nodetool snapshot). Store in object storage (S3/GS) with lifecycle and encryption.
- Tag snapshots with cluster/epoch metadata and consistent manifest from a Cassandra coordinator.
- Incremental backups
- Enable Cassandra incremental backups (copy new SSTables to object store) every few minutes; or use CDC stream if available.
- Keep retention window >= required point-in-time range plus buffer.
- Commit-log (WAL) archiving / CDC
- Archive commitlogs or use Cassandra CDC to an append-only store (object store or Kafka) every 1-5 minutes.
- Ensure archive includes sequence/timestamp metadata to replay to any point in time up to last 15 minutes.
- Cross-region replication
- Asynchronous multi-region Cassandra clusters using Datacenter-per-region (NetworkTopologyStrategy) for active-active or async replication.
- Additionally replicate backups to a secondary region’s object store (cross-region replication) for DR.
Data flow & restore strategy:
- For full restore: pick latest consistent full snapshot + apply incremental SSTables since snapshot + replay archived commitlogs up to target time.
- For partial/table restore: restore snapshot for affected keyspaces and replay relevant commitlogs/CDC for only that table.
- Automate orchestration (Terraform/Ansible/Kubernetes jobs) to create nodes, attach restored data, and bootstrap ring.
Meeting RPO/RTO:
- Commitlog/CDC + incremental cadence <=15 minutes ensures RPO.
- Pre-baked automation (AMI/container images, IaC) + parallel node restores + rolling bootstrap targeted to limited node count lets full cluster or degraded service recover within 1 hour.
- Maintain warm standby DC for catastrophic failover to reduce RTO.
Verification & testing:
- Continuous restore verification: nightly background restores of random snapshots into isolated test clusters, verify checksums and application-level tests.
- Regular drill schedule: quarterly full DR tests (failover to secondary region), measure RTO and data divergence.
- Canary restores for schema evolution checks.
Operational considerations:
- Monitoring: backup success/failure alerts, lag metrics for commitlog archive, S3 errors, size growth.
- Security: encryption-at-rest/in-transit, IAM, immutable backups.
- Retention & cost: tiering older backups to archive, retention policy aligned with compliance.
- Consistency: use snapshot coordination (pausing compaction briefly or using lightweight cluster-wide marker) to ensure snapshot consistency.
Edge cases & tradeoffs:
- Strict zero-downtime consistent snapshot is complex in active clusters — rely on commitlog replay to achieve point-in-time consistency.
- Active-active reduces RTO but increases complexity; cold-standby cheaper but slower.
This design ensures <=15 min data loss via frequent commitlog/CDC + incremental backups and automates restores with parallelism and warm standby to meet 1-hour RTO, backed by continuous verification and regular DR drills.
Explain LSM compaction strategies (levelled vs tiered). For a write-heavy workload, describe how compaction affects read amplification, write amplification, and space amplification. Propose tuning knobs and operational processes to reduce compaction impact while maintaining acceptable read latency.
Sample Answer
Levelled vs Tiered (brief):
- Levelled compaction: data is organized into levels (L0, L1...) with each level having a size target (e.g., 10×). Each key exists in at most one file per level; compactions merge overlapping files into next level to enforce non-overlap. This yields low read amplification because point/scan sees few files per level.
- Tiered (size-tiered) compaction: incoming SSTs are grouped/merged only when a threshold of similarly-sized files exists, producing larger files in batches. Fewer compaction passes but more overlap across files/levels, so reads may need to check more files.
Effects under write-heavy workload:
- Write amplification (WA): Tiered typically has lower immediate WA (fewer total bytes rewritten) for bursty writes because it delays merges; levelled has higher WA because it repeatedly merges the same keys down levels.
- Read amplification (RA): Levelled has lower RA (fewer SSTs to check), tiered increases RA (more overlapping SSTs).
- Space amplification (SA): Tiered tends to have higher transient SA (duplicate keys across overlapping SSTs) but can use less sustained extra space since fewer rewrites; levelled enforces tighter space bounds per level → lower SA long-term.
Tuning knobs and operational practices for write-heavy systems (SRE focus):
- Compaction style: choose tiered for extreme write throughput if read latency SLOs tolerate higher RA; choose levelled if low tail read latency is primary.
- Target file size / level size multiplier: increase level size multiplier and target file size to reduce frequency of compactions (lower WA) at cost of higher RA/SA.
- Max background compactions / threads: scale compaction threads to available I/O but cap to avoid starving foreground reads.
- Rate limiting: enable compaction throughput throttling (e.g., RocksDB max_compaction_bytes_per_sec) and use dynamic adjustment tied to load/queue depth.
- L0 tuning: reduce L0 files trigger for compaction or increase L0 flush thresholds to avoid compaction storms.
- Write buffer (memtable) sizing & number: larger memtables reduce flush frequency (lower WA) but increase recovery cost.
- Delayed write or batching: coalesce small writes into larger SSTs to reduce compaction churn.
Operational processes:
- Monitoring: instrument metrics—WA, RA (get/miss counts, SST files per read), compaction throughput, compaction backlog, CPU and disk IOPS, and tail read latency. Alert on compaction backlog growth, sustained high WA, or read-latency SLO breaches.
- Auto-tuning: implement a controller that lowers compaction rate during read-latency SLO violations and increases during quiet periods to catch up.
- Capacity planning: ensure headroom in IOPS and disk throughput for compactions; provision faster disks (NVMe) to absorb compaction I/O.
- Safeguards: rolling upgrades of compaction settings, staged rollout and A/B testing; use canaries and runbooks for compaction-induced incidents.
- Maintenance windows: schedule heavy background compaction during low-traffic windows, or run manual full compactions only when needed.
Trade-offs summary:
- To favor writes: use tiered + bigger files + rate-limits + more memtable; accept higher RA/SA and mitigate via caching (block cache/row cache) and SLO-aware throttling.
- To favor reads: use levelled + smaller RA targets + faster storage + more compaction bandwidth; accept higher WA and plan capacity accordingly.
This combination of tuning, dynamic rate control tied to read SLOs, monitoring, and capacity planning lets an SRE balance compaction impact while maintaining acceptable read latency.
Heavy ad-hoc analytics queries from data teams cause cascade failures in production by saturating the primary OLTP database. Design isolation mechanisms to prevent this class of failure: options include separate analytic cluster with CDC, resource groups/QoS, query governors, admission control, and incentives/policy. Choose an approach and detail rollout steps.
Sample Answer
Approach summary:
Use a hybrid solution that prioritizes a separate analytics cluster fed by real-time CDC, combined with admission-control + resource groups on the OLTP side as a safety net, and governance/policy to steer behavior. This isolates heavy queries from production while protecting against replication lag or misconfiguration.
Architecture & components:
- CDC pipeline: Debezium (or native DB CDC) → Kafka → transform layer → analytic cluster ingest (ClickHouse/BigQuery/Redshift/Snowflake).
- Analytic cluster: columnar store for ad-hoc queries; query gateway for auth/routing and cost-estimates.
- OLTP protections: resource groups / workload manager (Postgres RSG, MySQL thread pools, or cloud DB QoS) + query governor (max runtime, row-scan limits) + admission control (concurrent query caps).
- Observability: metrics (QPS, latency, replication lag, scanned rows), dashboards, alerts, and SLOs for OLTP latency and throughput.
- Governance: data access policy, training, and incentives (chargeback, quota per team).
Rollout plan (phased, with verification & rollback at each step):
-
Discovery & requirements (2–4 weeks)
- Inventory heavy queries, peak usage patterns, skills, and critical tables.
- Define OLTP SLOs and analytics SLA targets; identify must-have columns/streams.
-
PoC CDC + analytic cluster (2–6 weeks)
- Stream 1–2 high-volume tables through Debezium→Kafka→ClickHouse.
- Validate schema evolution handling, ordering, low-latency ingest, and simple queries.
- Measure replication lag and query result parity.
-
Implement OLTP safety nets (2–4 weeks, parallel)
- Enable resource groups on DB for unknown/untagged sessions with conservative caps.
- Deploy query governor rules: timeouts (e.g., 2s for interactive APIs), max scanned rows.
- Add admission-control layer to reject or queue long-running ad-hoc sessions.
-
Build query gateway & routing (2–4 weeks)
- Provide a single endpoint for data teams with auto-routing to analytic cluster.
- Implement cost-estimation and explain plans; warn users if query will run on OLTP.
- Instrument gateway to tag queries for billing/quota.
-
Migration & pilot with data teams (4–8 weeks)
- Onboard 1–2 analytics teams to use new cluster; migrate dashboards and reports.
- Encourage read-only replica usage where acceptable.
- Monitor replication lag, query behavior, and OLTP metrics closely.
-
Harden & expand (4–8 weeks)
- Extend CDC coverage across critical tables, ensure schema change procedures.
- Tighten OLTP resource groups to disallow ad-hoc analytics except through approved paths.
- Add automated breakers: if OLTP latency > SLO, throttle/deny ad-hoc queries automatically.
-
Governance, incentives, and enforcement (ongoing)
- Enforce policy: require use of analytic cluster for heavy queries; quota and chargeback for excess.
- Provide templates, training, and a “fast lane” process to expose new data in analytic cluster.
- Reward teams that migrate (e.g., higher quota, credits).
-
Runbooks, observability, and rollback
- Create runbooks: how to rollback CDC, how to failover analytic cluster, how to re-open OLTP limits in emergencies.
- Set alerts on replication lag, OLTP p95 latency, and query rejection rates.
- Define rollback: disable routing, revert resource-group caps, and stop CDC ingestion if instability detected.
Why this approach:
- Primary isolation via analytics cluster removes load from OLTP while preserving low-latency transactional performance.
- CDC gives near-real-time data for analytics without direct OLTP scanning.
- Resource groups and governors provide defense-in-depth for misconfigurations or if teams bypass the pipeline.
- Governance closes the human/process loop so behavior changes persist.
Key risks & mitigations:
- Replication lag → monitor and surface staleness; fall back to small read replicas for very fresh needs.
- Schema drift → automated schema migration tooling and pre-production validation.
- Cost/complexity → start with high-impact tables; automate pipelines and standardize templates.
Success metrics:
- Reduction in OLTP tail latency and incidents caused by ad-hoc queries.
- % of heavy queries served by analytic cluster.
- Replication lag under threshold (e.g., <5s) for critical tables.
This plan balances technical isolation, operational safety, and organizational change to prevent cascade failures from heavy ad-hoc analytics.
Unlock Full Question Bank
Get access to all Storage Systems and Infrastructure interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.