← Back to all questions
StaffObservabilityInfrastructure

Design a Distributed Logging & Monitoring System

A full Staff-level walkthrough of a multi-tenant observability platform that ingests 10TB of logs and 100B metric points per day from 100K+ servers — trading per-GB cost against query speed through hot/warm/cold tiering, while a decoupled alerting path still pages in under a minute. It carries multi-tenant isolation, quota-based chargeback, and an exactly-once stream pipeline through every step. Follow it top to bottom, or jump to any step.

Level
Staff
Category
Observability · Infrastructure
Interview time
60 min
100% free · No login required
WHAT THIS QUESTION TESTS
·Decoupling alerting from search so a store outage never delays a page
·Exactly-once ingestion with a durable stream buffer and checkpointed processing
·Hot/warm/cold tiering with per-namespace retention and partition-pruned cold queries
·Multi-tenant isolation: per-namespace quotas, indices, and access scoping
★ STAFF-LEVEL SIGNALS
Hybrid tenancy — shared cluster for the long tail, dedicated clusters for top-volume teams
Quota-based chargeback as a cost lever that drives teams to self-reduce log volume
Migrates many bespoke stacks via dual-write, per-team flags, and dashboard import
Statistical anomaly detection first (EWMA + z-score), ML ensemble as a later augmentation
0

Scope & ambiguity

Framing statement:

“Before I dive in, let me frame how I’d approach this. A multi-tenant observability platform at AWS scale — 100K+ servers, 10TB/day of logs, 100B metric data points/day — spans multiple organizations: an agent/collection team owning the per-host agent and data shipping, an ingestion pipeline team owning Kinesis and Flink processing, a storage team owning OpenSearch and S3 lifecycle, a query/analytics team owning the search and dashboard experience, an alerting/anomaly team owning real-time detection, and a platform/tenancy team owning namespace isolation and chargeback. The central architectural challenge is sub-minute alerting while keeping per-GB costs below commodity thresholds — the alerting path must be decoupled from the query path so that a slow OpenSearch cluster never delays a critical page. I’ll focus primarily on the ingestion pipeline and tiered storage architecture, then deep-dive into multi-tenant isolation and anomaly detection.”

Phased delivery:

Phase
Timeline
Scope
Phase 1
0-3 months
Single-region log collection + metrics ingestion, basic dashboards, single storage tier (OpenSearch), 10K servers
Phase 2
3-9 months
Multi-region, hot/warm/cold tiering, multi-tenant isolation with chargeback, anomaly detection, 100K servers
Phase 3
9-18 months
Distributed tracing integration, ML-powered root cause analysis, natural language query, unified observability platform

Team ownership:

Team
Ownership
Agent/Collection Team
Per-host CloudWatch Agent, log tailing, local buffering, structured parsing, sampling control
Ingestion Pipeline Team
Kinesis Data Streams, Apache Flink enrichment/routing/pre-aggregation, exactly-once delivery
Storage Team
OpenSearch (hot/warm), S3 Parquet lifecycle, Timestream for metrics, retention policy enforcement
Query/Analytics Team
Search API, Athena integration for cold queries, dashboard service, query optimization
Alerting/Anomaly Team
Flink CEP for threshold alerts, anomaly detection models, SNS notification routing
Platform/Tenancy Team
Namespace CRUD, quota management, chargeback metering, access control, onboarding portal

Key ambiguity I’d challenge:

“When we say ’monitoring’ — are we including distributed tracing, or only logs and metrics? Tracing adds a fundamentally different data model (span trees vs time-series vs full-text). I’ll design for logs + metrics core in Phase 1-2 and add tracing integration in Phase 3, where we ingest X-Ray spans and correlate them with logs and metrics via trace_id.”
1

Requirements

Functional Requirements (Phase 1-2)

  • FR1: Collect logs from 100K+ servers with configurable sampling/filtering at agent level
  • FR2: Ingest structured metrics (counters, gauges, histograms) at 100B data points/day
  • FR3: Full-text search on logs with sub-5s latency for recent data (last 24h)
  • FR4: Real-time alerting on metrics (threshold + anomaly) with sub-60s detection
  • FR5: Multi-tenant isolation — each team sees only their own data, enforced at query and storage level
  • FR6: Configurable retention policies per team/service (7 days to 7 years)
  • FR7: Dashboards with pre-aggregated metrics for common operational views (p50/p99 latency, error rate, throughput)

Non-Functional Requirements

Requirement
Target
Log ingestion latency
p99 < 30s from emission to searchable
Metric ingestion latency
p99 < 15s from emission to alertable
Alert detection latency
< 60s from threshold breach to notification
Query latency (hot)
p50 < 2s, p99 < 5s
Query latency (warm)
p50 < 10s, p99 < 30s
Query latency (cold/Athena)
< 5 minutes
Availability
99.95% ingestion, 99.9% query
Durability
99.999999999% (11 nines, S3-backed)

Organizational Context

  • Existing system: Evolution from CloudWatch + per-team bespoke solutions (ELK stacks, custom Kinesis pipelines, Grafana+InfluxDB). No single platform covers all teams — duplication of effort and inconsistent alerting quality.
  • Regulatory: SOC2/PCI logs must be immutable (WORM — Write Once Read Many). Audit logs require 7-year cold retention with tamper-proof verification (S3 Object Lock).
  • LP Connection: Dive Deep — engineers need deep observability into every service to diagnose issues. Frugality — tiered storage and chargeback ensure cost efficiency at scale.
  • Timeline: Phase 1 in 3 months with Agent + Ingestion Pipeline teams (12-15 engineers).

Out of Scope (Explicitly)

  • APM code-level profiling (Phase 3)
  • Distributed tracing storage and span indexing (Phase 3)
  • External customer-facing CloudWatch (separate product team)
  • Log content — we store and index; we do not interpret application-specific semantics
2

Back-of-envelope estimation

Scale Numbers

Metric
Value
Servers
100K+
Log volume
10TB/day raw, ~3TB/day compressed (3.3x avg compression)
Metrics
100B data points/day = ~1.15M/sec sustained, ~3.5M/sec peak (3x burst)
Unique metric time series
~50M
Log events/sec
~2M sustained, ~6M peak
Teams/namespaces
~2,000
Concurrent dashboard viewers
~5,000 peak
Ad-hoc queries/day
~50,000

Storage Estimation

Data
Calculation
Result
Hot logs (24h, OpenSearch SSD)
10TB/day × 1.5x index overhead
~15TB
Warm logs (30 days, UltraWarm)
10TB/day × 30 × 1.3x overhead
~390TB
Cold logs (1 year, S3 compressed)
3TB/day × 365
~1.1PB
Metrics hot (7 days, Timestream)
100B/day × 8B/point × 7
~5.6TB
Metrics warm (90 days, downsampled 10x)
5.6TB × 90/7 / 10
~7.2TB

Cost Estimation

Resource
Calculation
Monthly Cost
Kinesis Data Streams
~200 shards × $0.015/hr × 730h
~$15K
Flink processing
50 KPU × $0.11/hr × 730h + managed overhead
~$25K
OpenSearch (hot + warm)
r6g.2xlarge × 20 hot + ultrawarm 390TB
~$50K
S3 storage
1.1PB × $0.023/GB
~$25K
Timestream
Write + storage for 100B/day
~$20K
Athena queries
~50K queries/mo × ~5GB scanned avg × $5/TB
~$5K
Alerting compute (Flink)
10 KPU dedicated
~$10K
Total
~$150K/month

Architectural implication: Storage is the dominant cost — Frugality LP demands tiered architecture. Keeping all logs in hot OpenSearch for 30 days would cost ~$650K/month. Hot/warm/cold tiering reduces this by ~75%. Chargeback makes teams accountable for their log volume, creating natural incentive to reduce log spam and set appropriate retention.

3

API design

External API (REST)

# Log Ingestion
POST /api/v1/logs/ingest -- Batch log events (up to 1MB or 1000 events)
Body: { "namespace": "payments", "events": [
{ "timestamp": "...", "level": "ERROR", "service": "checkout",
"message": "...", "structured_fields": {...}, "trace_id": "..." }
]}
 
# Metric Ingestion
POST /api/v1/metrics/ingest -- Batch metric data points
Body: { "namespace": "payments", "datapoints": [
{ "metric_name": "latency_p99", "timestamp": "...", "value": 42.5,
"dimensions": {"service": "checkout", "region": "us-east-1"},
"aggregation_type": "GAUGE" }
]}
 
# Log Search
POST /api/v1/logs/search -- Full-text search with filters
Body: { "namespace": "payments", "query": "error AND timeout",
"time_range": {"start": "...", "end": "..."}, "filters": {"level": "ERROR"},
"limit": 100, "cursor": "..." }
 
# Metric Query
POST /api/v1/metrics/query -- Metric aggregation query
Body: { "namespace": "payments", "metric_name": "latency_p99",
"aggregation": "AVG", "group_by": ["service"], "period_sec": 60,
"time_range": {"start": "...", "end": "..."} }
 
# Alerting
POST /api/v1/alerts/rules -- Create alert rule
GET /api/v1/alerts/active -- Currently firing alerts
 
# Dashboards
POST /api/v1/dashboards -- Create dashboard
GET /api/v1/dashboards/{id} -- Get dashboard definition + data
 
# Namespace Management
GET /api/v1/namespaces/{ns}/usage -- Ingestion/storage/query usage + cost
PUT /api/v1/namespaces/{ns}/config -- Retention, quotas, sampling config

Inter-Service Contracts (gRPC)

SERVICE: IngestionRouter
OWNER: Ingestion Pipeline Team
PROTOCOL: gRPC (internal)
 
RPC IngestLogBatch(LogBatchRequest) returns (LogBatchResponse)
RPC IngestMetricBatch(MetricBatchRequest) returns (MetricBatchResponse)
- Latency SLA: p99 < 10ms (acknowledgement, before async processing)
- Availability SLA: 99.99%
- Auth: mTLS between agent and ingestion endpoint
- Rate limiting: per-namespace token bucket (configurable by TenancyService)
SERVICE: LogQueryService
OWNER: Query/Analytics Team
PROTOCOL: gRPC
 
RPC SearchLogs(SearchRequest) returns (SearchResponse)
- Latency SLA: p99 < 5s (hot), p99 < 30s (warm)
- Automatic tier routing: query router inspects time range, routes to
OpenSearch (hot/warm) or Athena (cold) transparently
- Auth: namespace-scoped IAM token
SERVICE: AlertEvaluator
OWNER: Alerting/Anomaly Team
PROTOCOL: gRPC + Async
 
RPC EvaluateRule(EvaluateRuleRequest) returns (EvaluateRuleResponse)
- Latency SLA: p99 < 5s for rule evaluation
- Events published: alerts.state_change → SNS topic
(PagerDuty, Slack, email subscribers per alert rule)
- Decoupled from query path — reads from Flink aggregation, not OpenSearch

API Governance

  • Versioning: URI versioning (/api/v1/), Protobuf for all internal gRPC services
  • Rate limiting: Per-namespace ingestion rate limit (token bucket, configurable), per-namespace query concurrency limit (default 10 concurrent queries)
  • Schema registry: Protobuf schemas in internal registry, backward-compatible evolution enforced by CI
  • Deprecation: External API versions supported minimum 12 months; internal gRPC services require 3-month migration window
4

Data model

Core Entities

Entity: LogEvent
- event_id: UUID (PK)
- namespace: VARCHAR(128) (partition key in OpenSearch)
- timestamp: TIMESTAMP (sort key)
- source_host: VARCHAR(256)
- log_level: ENUM (TRACE, DEBUG, INFO, WARN, ERROR, FATAL)
- service_name: VARCHAR(128)
- message: TEXT (full-text indexed)
- structured_fields: JSON (key-value pairs, indexed selectively)
- trace_id: VARCHAR(64) (nullable, for Phase 3 correlation)
- ingestion_timestamp: TIMESTAMP
 
Entity: MetricDataPoint
- metric_name: VARCHAR(256)
- namespace: VARCHAR(128)
- timestamp: TIMESTAMP
- value: DOUBLE
- dimensions: MAP<VARCHAR, VARCHAR> (up to 10 dimensions)
- aggregation_type: ENUM (COUNTER, GAUGE, HISTOGRAM)
 
Entity: AlertRule
- rule_id: UUID (PK)
- namespace: VARCHAR(128)
- metric_query: JSON (metric name, dimensions, aggregation, period)
- condition: JSON (operator, threshold, consecutive_periods)
- severity: ENUM (INFO, WARNING, CRITICAL, FATAL)
- notification_targets: JSON[] (SNS ARN, Slack webhook, PagerDuty key)
- enabled: BOOLEAN
- created_by: VARCHAR(128)
- created_at: TIMESTAMP
 
Entity: Namespace
- namespace_id: VARCHAR(128) (PK)
- owner_team: VARCHAR(256)
- ingestion_quota_bytes_per_day: BIGINT
- retention_policy_days: JSON (hot_days, warm_days, cold_days)
- sampling_config: JSON (per-level sampling rates)
- created_at: TIMESTAMP
- updated_at: TIMESTAMP

Data Ownership Boundaries

Data
Owner Service
Access Pattern
Hot logs (0-24h)
LogStorageService
Direct OpenSearch query via Query Gateway
Warm logs (1-30d)
LogStorageService
OpenSearch UltraWarm query via Query Gateway
Cold logs (30d-7yr)
ArchiveService
Athena SQL over S3 Parquet via Query Gateway
Metrics (hot + warm)
MetricStorageService
Timestream query via Query Gateway
Alert rules + state
AlertService
gRPC sync, cached in Flink evaluator
Namespace config
TenancyService
gRPC with local cache (TTL 60s)
Dashboards
DashboardService
DynamoDB, served via Query Gateway
Chargeback records
TenancyService
DynamoDB, aggregated hourly

Storage Technology Choices

Data
Technology
Rationale
Alternative Considered
Hot logs
OpenSearch (SSD, r6g.2xlarge)
Full-text search, sub-5s queries, managed service
Elasticsearch self-managed (rejected: operational overhead at this scale)
Warm logs
OpenSearch UltraWarm
S3-backed, 80% cheaper than hot, managed lifecycle
OpenSearch on HDD (rejected: UltraWarm is fully managed, S3-backed durability)
Cold logs
S3 + Athena
Cheapest tier, Parquet format, serverless SQL
S3 Glacier (rejected: restore latency too high for incident investigation)
Metrics
Timestream
Purpose-built time-series DB, automatic tiering, SQL queries
InfluxDB (rejected: not AWS-native, operational burden)
Config + dashboards
DynamoDB
Single-digit ms reads, proven at AWS scale
RDS PostgreSQL (rejected: overkill for key-value config, more operational overhead)
Chargeback metering
DynamoDB + S3
DynamoDB for real-time counters, S3 for hourly aggregates

Data Lifecycle

HOT (0-24h): OpenSearch SSD, full-text indexed, < 5s query
Every field indexed, replicas for HA
WARM (1-30d): OpenSearch UltraWarm (S3-backed), < 30s query
Reduced replicas, same index structure
COLD (30d-7yr): S3 Parquet (Snappy compressed, 10x ratio), Athena batch query
Partition key: namespace/year/month/day
Query: minutes (partition pruning critical)

Configurable per namespace: security/audit logs retain hot 90 days, cold 7 years with S3 Object Lock (WORM). Development service logs retain hot 24h, warm 7 days, no cold.

5

High-level architecture

Architecture Diagram

[100K+ Servers]
|
[CloudWatch Agent (per-host)] ←── Agent/Collection Team
(file tailing, local buffer,
structured parsing, sampling)
|
[Kinesis Data Streams] ←── Ingestion Pipeline Team
(partitioned by namespace,
200 shards, 24-168h retention)
|
[Apache Flink Cluster] ←── Ingestion Pipeline Team
(enrichment, routing, pre-aggregation,
exactly-once via checkpointing)
|
+----+------+------+
| | |
[OpenSearch] [Timestream] [Alert Evaluator]
(logs) (metrics) (Flink CEP job)
| | |
[UltraWarm] [Warm tier] [SNS → PagerDuty/Slack/Email]
| (downsampled)
[S3 Parquet]
|
[Athena]
|
[Query API Gateway] ←── Query/Analytics Team
(tier routing, caching,
namespace access control)
|
[Dashboard Service] ←── Query/Analytics Team
(pre-aggregated views,
DynamoDB-backed)
|
[Tenancy & Chargeback] ←── Platform/Tenancy Team
(namespace CRUD, quotas,
cost attribution, onboarding)

Service Boundaries with Ownership

Service
Team
Data Store
Scaling Strategy
SLA
CloudWatch Agent
Agent/Collection
Local disk buffer (1GB)
Per-host, deployed via SSM
99.9% delivery rate
Kinesis Data Streams
Ingestion Pipeline
Kinesis shards
Auto-split on throughput
99.99% availability
Flink Enrichment
Ingestion Pipeline
S3 (checkpoints)
KPU auto-scaling
p99 < 10s processing
OpenSearch Hot
Storage
SSD instances
Horizontal (add data nodes)
p99 < 5s query
OpenSearch UltraWarm
Storage
S3-backed warm nodes
Storage auto-scaling
p99 < 30s query
Timestream
Storage
Managed
Auto-scaling writes/reads
p99 < 2s query
Alert Evaluator
Alerting/Anomaly
Flink state + DynamoDB
KPU scaling, separate job
< 60s detection
Query Gateway
Query/Analytics
ElastiCache (results)
Horizontal behind ALB
99.9% availability
Tenancy Service
Platform/Tenancy
DynamoDB
Horizontal
99.95% availability

Sync vs Async Communication

Communication Path
Type
Rationale
Agent → Kinesis
Async batch PUT (5s buffer or 1MB)
Agent should never block application; local buffer absorbs bursts
Kinesis → Flink
Async stream (Kinesis consumer)
Stream processing, backpressure handled by Kinesis buffering
Flink → OpenSearch
Async bulk index (5-10s micro-batches)
Bulk indexing is 10x more efficient than single-doc writes
Flink → Timestream
Async batch write (1s micro-batches)
Timestream optimized for batch writes
Flink → Alert Evaluator
Sync stream (Flink CEP, same job)
Low-latency alerting requires in-stream processing
User → Query Gateway → OpenSearch
Sync (user waiting for results)
Interactive search, user expects response
Chargeback metering
Async (hourly aggregation)
Cost attribution is not latency-sensitive

Failure Domain Boundaries

FAILURE DOMAIN 1: Collection (agents + Kinesis)
[CloudWatch Agent] → [Kinesis Data Streams]
Impact: New logs not ingested. Agents buffer to local disk (1GB, ~15 min at peak).
Mitigation: Kinesis has 99.99% SLA. Agent retries with exponential backoff + jitter.
 
FAILURE DOMAIN 2: Processing (Flink)
[Flink Cluster]
Impact: Logs queue in Kinesis (retains 24-168h). No enrichment or routing.
Mitigation: Flink replays from last checkpoint on restart. Kinesis is the durable buffer.
 
FAILURE DOMAIN 3: Hot Storage (OpenSearch)
[OpenSearch Hot + UltraWarm]
Impact: Cannot search recent logs. Ingestion continues (Flink buffers to DLQ).
Mitigation: Alerting has separate path (FD5). Old data still in S3.
 
FAILURE DOMAIN 4: Warm/Cold Storage (S3 + Athena)
[S3 Parquet] → [Athena]
Impact: Cannot search older logs. Recent data in OpenSearch still available.
Mitigation: S3 has 11 nines durability. Athena is serverless, scales independently.
 
FAILURE DOMAIN 5: Alerting (separate from query)
[Alert Evaluator (Flink CEP)] → [SNS]
Impact: Alerts not firing. Query/search path unaffected.
Mitigation: Dedicated Flink job with independent scaling. Secondary path: CloudWatch alarms.
6

Deep dives

WHERE STAFF IS WON

Deep Dive 1: Log Agent Architecture

Tradeoff: Push vs Pull Ingestion

Dimension
Push (agent → Kinesis)
Pull (central collector polls hosts)
Latency
Low — agent ships immediately on buffer flush
Higher — polling interval adds delay
Network
Agent initiates outbound (simpler firewall rules)
Collector needs access to all hosts (complex)
Reliability
Agent buffers locally during outages
Central collector is SPOF
Host overhead
~50MB memory, ~1% CPU per agent
Minimal on host, heavy on collector
Scalability
Linear — each agent handles its own host
Collector must scale with fleet size
Sampling control
Agent can filter at source (Frugality LP)
All data must traverse network first

Recommendation: Push model. Each host runs a CloudWatch Agent that tails log files and pushes to Kinesis. Agent-side filtering means we never pay to transport DEBUG logs we will discard.

Agent Protocol Detail:

1. File tailing with inode tracking (handles log rotation)
2. Structured log parsing (regex patterns or JSON auto-detect)
3. Agent-level sampling configuration:
- DEBUG: 1% sample rate (configurable per namespace)
- TRACE: 0.1% sample rate
- INFO/WARN/ERROR/FATAL: 100% (no sampling)
4. Local disk buffer: 1GB ring buffer (~15 min at peak rate)
- Circuit breaker at 80%: drop DEBUG first, then TRACE
- At 95%: drop INFO, alert agent-health metric
5. Batch to Kinesis: every 5 seconds OR 1MB, whichever comes first
6. Retry: exponential backoff with jitter (base 100ms, max 30s)
7. Agent health metrics: shipped via separate low-volume Kinesis stream

Deep Dive 2: Ingestion Pipeline (Kinesis + Flink)

Tradeoff: Kinesis vs MSK (Managed Kafka) vs Self-Managed Kafka

Dimension
Kinesis Data Streams
MSK (Managed Kafka)
Self-Managed Kafka
AWS integration
Native (IAM, CloudWatch, Flink connector)
Good (but requires more config)
Manual
Ops overhead
Minimal (serverless-like)
Medium (broker management)
High (ZooKeeper, upgrades)
Cost at 6M events/sec
~$15K/mo (200 shards)
~$20K/mo (brokers + storage)
~$12K/mo (EC2 + EBS)
Ordering
Per-shard (partition key)
Per-partition (same)
Per-partition (same)
Retention
24h default, up to 7 days
Unlimited (disk-based)
Unlimited
Throughput scaling
Shard split/merge (minutes)
Add partitions (fast)
Add partitions + brokers

Recommendation: Kinesis Data Streams. The Frugality LP favors lower operational cost over raw $/event savings. Native Flink connector with enhanced fan-out provides dedicated throughput per consumer. 7-day retention gives us ample replay buffer.

Flink Pipeline Stages:

Stage 1: Deserialize + Validate
- Protobuf deserialization
- Schema validation (reject malformed events to DLQ)
- Namespace existence check (cached from TenancyService, TTL 60s)
 
Stage 2: Enrich
- Add ingestion_timestamp
- Tag with host metadata (AZ, instance type, AMI — from host metadata cache)
- Normalize log levels across different source formats
 
Stage 3: Route
- Logs → OpenSearch sink (bulk index, partitioned by namespace + date)
- Metrics → Timestream sink (batch write)
- All events → Alert Evaluator (Flink CEP branch, in-process)
 
Stage 4: Pre-Aggregate Metrics
- Compute p50, p99, avg, min, max per 10-second tumbling window
- Group by: namespace + metric_name + dimensions
- Output: pre-aggregated datapoints for dashboards
- Reduces Timestream write volume by ~10x for dashboard queries
 
Stage 5: Quota Enforcement
- Per-namespace token bucket (refilled from TenancyService config)
- Over-quota namespaces: progressive sampling (50% → 10% → 1%)
- Over-quota events tagged with "sampled" flag
- Quota breach events → SNS notification to namespace owner
 
Exactly-once guarantee:
- Flink checkpointing to S3 every 60 seconds
- Kinesis consumer uses enhanced fan-out with sequence number tracking
- OpenSearch sink uses idempotent bulk writes (event_id as doc ID)

Deep Dive 3: Multi-Tenant Isolation

Tradeoff: Shared Cluster vs Per-Tenant Cluster vs Hybrid

Dimension
Shared Cluster
Per-Tenant Cluster
Hybrid
Cost efficiency
Best — shared resources, high utilization
Worst — many underutilized clusters
Good — shared for most, dedicated for top-N
Isolation
Noisy neighbor risk on queries + ingestion
Perfect isolation
Strong — dedicated clusters for high-volume
Operational complexity
Low — one cluster to manage
High — 2000 clusters
Medium — ~10 dedicated + 1 shared
Blast radius
Large — one bad query affects all
Minimal
Contained — top teams isolated
Onboarding speed
Fast — create namespace, done
Slow — provision cluster
Fast for most, slower for dedicated

Recommendation: Hybrid model. Shared OpenSearch cluster for ~1,990 standard namespaces. Dedicated clusters for the top-10 highest-volume teams (which generate ~60% of total volume). This provides cost efficiency (Frugality LP) with strong isolation for the teams most likely to cause noisy-neighbor issues.

Namespace Implementation:

Every event tagged with namespace field (required, validated at Flink):
 
OpenSearch:
- Index pattern: {namespace}-logs-{YYYY-MM-DD}
- Index-per-namespace-per-day enables:
- Fine-grained access control (index-level IAM)
- Independent retention (delete old indices per namespace)
- Query isolation (search scoped to namespace indices)
- Cross-namespace search blocked by Query Gateway (not just OpenSearch ACL)
 
Kinesis:
- Partition key = namespace (ensures ordering within namespace)
- High-volume namespaces may get dedicated shards
 
Flink:
- Per-namespace token bucket rate limiting
- Configurable via TenancyService API (hot-reloaded every 60s)
- Over-quota: progressive sampling, not hard rejection
 
Access Control:
- Namespace-scoped API tokens (short-lived, refreshed hourly)
- Query Gateway validates token scope before routing to OpenSearch
- Admin role: can query across namespaces (for platform team debugging)

Chargeback Model (Frugality LP):

Cost Component
Rate
Example (10GB/day team)
Ingestion
$0.50/GB ingested
$5.00/day
Hot storage
$0.10/GB/month
$0.10/day (24h × 10GB)
Warm storage
$0.03/GB/month
$0.30/day (30d × 10GB)
Cold storage
$0.01/GB/month (compressed)
$0.01/day
Query scan
$0.005/GB scanned
$0.25/day (50GB scanned)

Dashboard per namespace showing: daily cost breakdown, ingestion trend, top-10 chattiest services, cost optimization recommendations (“Enable DEBUG sampling to save $X/month”, “Reduce retention from 30d to 14d warm to save $Y/month”).

Deep Dive 4: Data Tiering Architecture

Tradeoff: Age-Based vs Access-Frequency vs Hybrid Transition

Dimension
Age-Based
Access-Frequency
Hybrid
Predictability
High — exact tier by age
Low — depends on query patterns
Medium
Implementation
Simple — ISM policy on age
Complex — track access per index
Medium
Cost optimization
Good — predictable cost curve
Better — hot data stays hot
Good
Incident investigation
Risk — old data slow during incident
Good — frequently accessed stays fast
Good
Operational complexity
Low
High (access tracking overhead)
Medium

Recommendation: Age-based with manual override. Default policy transitions by age. During incidents, operators can “promote” a cold time range back to warm for faster investigation (takes ~5 min for Athena-to-OpenSearch rehydration).

Lifecycle Protocol:

HOT (0-24h): OpenSearch SSD
- Index settings: 3 primary shards, 1 replica, refresh_interval=30s
- Full-text indexed (all fields), structured fields as keywords
- ~15TB at steady state across all namespaces
 
WARM (1-30d): OpenSearch UltraWarm (S3-backed)
- ISM policy: migrate index to warm tier at age 24h
- Read-only (no new writes), S3-backed storage
- Same query API, ~6x slower but 80% cheaper
- ~390TB at steady state
 
COLD (30d-7yr): S3 Parquet + Athena
- Nightly Flink batch job: read warm indices aging out → convert to Parquet
- Parquet schema:
Partition keys: namespace / year / month / day
Sort key: timestamp within each partition
Compression: Snappy (~10x ratio)
- Athena query: SQL with partition pruning
SELECT * FROM logs
WHERE namespace = 'payments'
AND year = '2026' AND month = '03' AND day = '20'
AND message LIKE '%timeout%'
- Partition pruning critical: without it, full PB scan costs $5K per query
- ~1.1PB at steady state (compressed)
 
Configurable per namespace:
- Security/audit logs: hot 90 days, cold 7 years (S3 Object Lock for WORM)
- Development logs: hot 24h, warm 7 days, no cold tier
- Payment logs: hot 7 days, warm 90 days, cold 3 years (PCI compliance)

Deep Dive 5: Anomaly Detection on Metrics

Tradeoff: Statistical (Z-Score/EWMA) vs ML (Isolation Forest/LSTM)

Dimension
Statistical (EWMA + Z-Score)
ML (Isolation Forest / LSTM)
Detection latency
< 1s (in-stream computation)
5-30s (model inference)
Interpretability
High — "value is 3.2 stddev above 7-day mean"
Low — "anomaly score 0.87"
Accuracy
Good for simple patterns
Better for complex seasonality
Cost (compute)
Low — basic arithmetic in Flink
High — GPU inference or large CPU
Cold start
Fast — 7 days of baseline data
Slow — needs weeks of training data
False positive rate
Medium-high (~5%)
Low (~1%) with tuning
Maintenance
None — self-adjusting
High — model retraining, drift monitoring

Recommendation: Statistical methods for Phase 1 (EWMA + Z-Score) — immediate value, interpretable alerts, low cost. ML augmentation in Phase 2 — isolation forest for multivariate anomalies, trained offline, inference at edge.

Detection Protocol:

Phase 1 — Statistical Anomaly Detection (Flink CEP):
 
1. Flink aggregates metrics per 10-second tumbling window
- Per metric × namespace × dimension combination
- Compute: count, sum, min, max, p50, p99
 
2. Maintain rolling statistics (Flink keyed state):
- 7-day rolling mean and stddev
- Hour-of-day pattern (24 buckets, updated daily)
- Day-of-week pattern (7 buckets)
- Expected value = hour_of_day_mean × day_of_week_factor
 
3. Anomaly scoring:
- anomaly_score = |current_value - expected_value| / rolling_stddev
- Threshold: score > 3.0 (configurable per alert rule)
 
4. Alert triggering:
- If anomaly_score > threshold for 3 consecutive windows (30 seconds):
→ Trigger alert (prevents single-spike false positives)
- Alert payload includes: metric, current value, expected value,
anomaly score, contributing dimensions
 
5. Correlation engine (async, post-alert):
- Query related metrics from same namespace + time window
- Check: CPU, memory, error_rate, request_count, deployment_events
- Rank by correlation coefficient with anomalous metric
- Root cause suggestion: "latency_p99 spike (42ms → 350ms) correlated
with deployment of checkout-service v2.3.1 at 14:32 UTC
(correlation: 0.94)"
 
Phase 2 — ML Augmentation:
- Isolation forest trained offline on 30-day metric history
- Model deployed as SageMaker endpoint
- Flink sends pre-aggregated windows to SageMaker for scoring
- ML score combined with statistical score (weighted ensemble)
- Reduces false positive rate from ~5% to ~1%
7

Multi-team rollout

Context

Many teams across the organization currently run bespoke observability solutions: per-team ELK stacks, custom Kinesis pipelines to CloudWatch, self-managed Grafana+InfluxDB. These solutions are expensive to maintain, create inconsistent alerting quality, and make cross-team debugging nearly impossible. The rollout must absorb teams gradually without disrupting their existing workflows.

Phase 1: Internal Dogfood (Weeks 1-8)

  • Deploy platform for observability team itself — eat our own dog food
  • 5 pilot teams (volunteers from high-pain teams), 500 servers
  • Agent deployment via SSM Run Command (zero-touch installation)
  • Dual-write: agent ships to both new platform AND existing ELK/CloudWatch
  • Success criteria: ingestion latency p99 < 30s, zero data loss, 5 teams confirm parity
  • Weekly feedback sessions with pilot teams

Phase 2: High-Value Teams (Weeks 9-20)

  • Top-20 highest-volume teams (~60% of total log volume)
  • Chargeback reporting active (visibility only, not charged yet)
  • Dedicated OpenSearch cluster for top-3 teams (noisy neighbor prevention)
  • Per-team feature flag: can route each team's traffic independently
  • Migration tooling: import Kibana dashboards → platform dashboards (automated converter)
  • Success criteria: 20 teams migrated, chargeback reports accurate to 99%

Phase 3: Broad Rollout (Weeks 21-36)

  • Self-service onboarding portal: create namespace, install agent, configure retention
  • 500 teams, 50K servers
  • One-click migration from ELK: import dashboards, alert rules, saved searches
  • CloudWatch Logs routing: subscription filter sends to Kinesis (for teams that cannot change agent)
  • Chargeback active in team cost reporting (charged to team's AWS account)
  • Success criteria: 80% of teams migrated, self-service onboarding < 30 minutes

Phase 4: Mandatory Adoption (Months 9-15)

  • VP mandate: all teams must migrate by Month 15
  • Decommission per-team ELK stacks (save ~$200K/month in duplicated infrastructure)
  • Full chargeback in team cost reporting — visible in monthly business reviews
  • Platform team provides migration office hours and dedicated support
  • Success criteria: 100% migration, per-team ELK decommissioned

Rollback Plan

Stage
Rollback Action
Data Impact
Phase 1
Disable agent via SSM, revert to legacy pipeline
None — dual-write means both systems have data
Phase 2
Per-team feature flag → route back to ELK
Minimal — replay from Kinesis (7-day retention)
Phase 3
Per-team rollback via self-service portal
Same as Phase 2
Phase 4
Cannot rollback (ELK decommissioned)
Fix forward — Kinesis retains 7 days, platform has all data
8

Bottlenecks & evolution

Bottleneck Analysis

Component
Bottleneck
Mitigation
Kinesis shard throughput
1MB/s per shard write limit, hot partitions
Auto-split shards, pre-provision for peak, use random suffix for high-volume namespaces
Flink backpressure
Slow OpenSearch bulk indexing causes upstream backpressure
Auto-scale KPU, Kinesis buffers burst (24-168h retention), async sinks with buffer
OpenSearch hot write throughput
Bulk index saturation during ingestion spikes
Tune refresh_interval (30s), dedicated ingest nodes, bulk size optimization (5-15MB)
OpenSearch query slowness
Large time-range queries scan too many shards
Warm replicas for read scaling, query result caching (ElastiCache), pagination limits (max 10K hits)
Athena cold query cost
Unpartitioned scans cost $5/TB
Enforce partition pruning at Query Gateway (reject queries without namespace + date range), show cost estimate before execution
Agent buffer disk full
Host-level disk pressure during Kinesis outages
Circuit breaker: drop DEBUG at 80%, drop TRACE at 85%, drop INFO at 95%, alert at 70%

Observability (RED Method)

Service
Rate
Errors
Duration
CloudWatch Agent
Events shipped/sec, bytes/sec per host
Ship failures, parse errors, buffer overflow drops
Batch ship latency (agent → Kinesis ack)
Kinesis Data Streams
Records/sec, PutRecords/sec
WriteProvisionedThroughputExceeded, InternalFailure
PutRecords latency, iterator age (consumer lag)
Flink Cluster
Records processed/sec, checkpoints/min
Deserialization errors, DLQ events, checkpoint failures
End-to-end processing latency (Kinesis → sink)
OpenSearch
Index rate (docs/sec), query rate (queries/sec)
Index rejection rate, query timeouts, circuit breaker trips
Bulk index latency, search latency (p50/p99)
Alert Evaluator
Rules evaluated/sec, alerts fired/sec
Missed evaluations, SNS publish failures
Alert detection latency (metric emission → SNS publish)

Critical Alerts (Meta-Monitoring)

1. Ingestion lag > 5 minutes — Kinesis iterator age (GetRecords.IteratorAgeMilliseconds) exceeds 300s → page on-call

2. OpenSearch cluster health RED/YELLOW > 5 min — shard allocation failure, potential data loss risk → page on-call

3. Alert evaluation latency p99 > 30s — alerting pipeline degraded, critical alerts may be delayed → page on-call

4. Namespace ingestion exceeds quota by 2x — runaway logging, potential noisy neighbor → notify namespace owner + platform on-call

5. Agent deployment coverage < 99% — hosts missing agent, blind spots in observability → alert platform team

Distributed Tracing (Own Platform)

  • Trace from log emission → agent batch → Kinesis PutRecords → Flink processing → OpenSearch bulk index
  • Each stage adds span with timing: agent.batch_ms, kinesis.put_ms, flink.process_ms, opensearch.index_ms
  • Own services instrumented with X-Ray — traces visible in the platform itself (dogfooding)
  • End-to-end latency breakdown enables targeted optimization (identify which stage contributes most to p99)

2-3 Year Evolution

Year 1: Core Platform

  • Core logging + metrics with tiered storage operational
  • 80% of teams migrated from bespoke solutions
  • Statistical anomaly detection live
  • Chargeback active, driving 20% volume reduction through team accountability
  • Single-region, with Kinesis cross-region replication for DR

Year 2: Intelligence

  • ML anomaly detection (isolation forest + LSTM for seasonality)
  • Automated root cause analysis — correlate metrics, logs, and deployment events
  • Distributed tracing integration (X-Ray span ingestion, trace_id correlation with logs)
  • Log pattern detection — automatically cluster similar error messages (Dive Deep LP)
  • Cross-namespace correlation — detect cascading failures across service boundaries

Year 3: Unified Platform

  • Natural language log search — "show me errors from payment service yesterday during the latency spike"
  • Predictive alerting — forecast metric trends, alert before threshold breach
  • Unified observability: logs + metrics + traces + profiling in single pane of glass
  • Self-healing: anomaly detection triggers automated runbooks (SSM Automation)
  • Potential external offering — productize as managed service for AWS customers (Think Big LP)

Cost Optimization Roadmap

Strategy
Timeline
Expected Savings
Agent-side DEBUG sampling (1% default)
Month 2
30% ingestion volume reduction
Parquet cold tier (S3 + Athena)
Month 3
90% cold storage cost reduction vs keeping in OpenSearch
Metric pre-aggregation in Flink
Month 4
90% warm metrics storage reduction
Chargeback visibility
Month 6
20% overall volume reduction (teams self-optimize)
Reserved capacity (Kinesis + OpenSearch)
Month 6
30% cost reduction on compute/ingestion
Query result caching (ElastiCache)
Month 9
40% OpenSearch query load reduction
Intelligent tiering (access-frequency override)
Month 12
15% warm storage reduction
Spot instances for Flink workers
Month 12
60% Flink compute cost reduction

Summary

The key staff-level insights in this design:

1. Tiered storage as the central cost optimization — hot/warm/cold architecture reduces storage costs by 75% vs all-hot, directly aligned with Frugality LP. Age-based transitions with manual override for incidents.

2. Multi-tenant chargeback creates natural cost pressure — namespaces with per-team quotas and cost attribution incentivize teams to log at appropriate levels, reducing volume 20% without mandating specific behaviors.

3. Kinesis + Flink exactly-once pipeline — Kinesis as durable buffer (7-day retention) decouples collection from processing. Flink checkpointing to S3 ensures exactly-once semantics. Pre-aggregation in Flink reduces downstream write volume 10x.

4. Separate failure domains for ingestion vs alerting — alerting runs as a dedicated Flink CEP job, independent from the OpenSearch query path. An OpenSearch outage does not delay critical alerts.

5. Migration strategy for diverse existing solutions — phased rollout from pilot → high-value → broad → mandatory, with per-team feature flags, dual-write validation, and ELK dashboard import tooling. This is where most “platform” projects fail.

6. 3-year evolution from logs+metrics → unified observability — statistical anomaly detection (Year 1) → ML root cause analysis + tracing (Year 2) → NL search + predictive alerting + self-healing (Year 3). Each phase delivers standalone value while building toward the unified platform.

Rubric — Senior vs Staff

Dimension
Senior signal
Staff signal
Ingestion pipeline
Streams logs through a queue into a stream processor and indexes them.
Justifies Kinesis-vs-Kafka, exactly-once via checkpoints, and pre-aggregation that cuts downstream writes 10x.
Storage & cost
Keeps recent logs in a search cluster and archives older data to object storage.
Designs hot/warm/cold tiers with age-based transitions, partition pruning, and chargeback that pushes cost back to teams.
Alerting path
Evaluates threshold rules and sends notifications.
Isolates alerting as a separate stream job so query-tier failures never delay a sub-minute page.
Multi-tenancy
Tags every event with a namespace and filters queries by it.
Compares shared/dedicated/hybrid, picks hybrid, and enforces quotas with progressive sampling rather than hard drops.
Rollout / org
Rolls the platform out team by team.
Phases pilot → high-value → broad → mandatory with dual-write, per-team flags, and a concrete rollback at each stage.
★ MORE WALKTHROUGHS

Want more breakdowns like this?

Join free early access for upcoming RAG, LLM eval, agents, and AI infrastructure walkthroughs.

Join Free Early Access →