Architecture Review: Observability Platform
A metrics, logs, and traces ingestion and query platform built to absorb the telemetry output of a production system fleet: including the telemetry volume spikes that accompany the incidents the platform is meant to detect. ClickHouse stores metrics data with automatic time-based rollup via continuous materialized views; TimescaleDB provides complementary time-series storage for high-cardinality alert evaluation; Kafka buffers the ingestion stream against downstream write pressure, decoupling ingest acceptance rate from storage write throughput; Elasticsearch serves log full-text search and structured field filtering; Redis caches dashboard query results and active alert state for sub-100ms alert evaluation latency. The alert engine evaluates threshold and anomaly rules against pre-computed materialized views, not raw data, to bound alert evaluation cost independent of ingestion volume.
Evidence Confidence
Moderate
strong
Executive Summary
Observability Platform: moderate operational readiness (81% evidence confidence). 0 architectural strengths identified, 6 operational risks to manage. Primary concern: Disk I/O Saturation. Requires Advanced operational maturity.
Readiness Rationale
Overall moderate readiness across 8 dimensions. Limited: team maturity. Strong: migration, observability, failure recovery.
Key Concerns
- !Disk I/O Saturation
- !Hot Partition
Key Strengths
- +Architecture is well-defined for the analytics pipeline problem profile
8
Assessments
3
Tradeoffs
6
Sections
11
Recommendations
Readiness Assessments
8Architectural Tradeoffs
3Recommendations
11Monitor: Disk I/O Saturation
risk_monitoringThe storage device reaches its IOPS or throughput ceiling, causing all disk- dependent database operations to queue behind I/O requests, driving latency from sub-millisecond to hundreds of milliseconds and degrading all database operations simultaneously.
Affects 1 node. (Time-Series Metrics)
Monitor: Hot Partition
risk_monitoringOne partition (a database shard, a Kafka topic partition, a Redis hash slot) receives traffic so far above its peers that it saturates while the others sit idle. Aggregate capacity looks healthy, but the hot partition throttles or lags, and everything routed to it degrades. The cause is skew in how keys map to partitions, and the fix depends on whether the skew is spread across many keys or concentrated in one.
Affects 0 nodes
Implement: Monitor generic risk probe signals
observabilitySeed 'Disk I/O Saturation Risk Probe' identifies 2 metrics relevant to disk_io_saturation.
Metrics to instrument: error_rate, p95_latency_ms
Prometheus + Grafana stack with local time-series storage → Kafka-buffered ClickHouse ingestion with Redis-backed alert evaluation
migration_planningTrigger: Prometheus storage limits reached at production metric volume; ClickHouse required for sub-second aggregate queries across multi-day windows that Prometheus cannot serve; need to retain metrics beyond Prometheus 15-day default retention without capacity-limited remote write targets; cross-service correlation queries not possible in PromQL. Migrate from 'Prometheus + Grafana stack with local time-series storage' to 'Kafka-buffered ClickHouse ingestion with Redis-backed alert evaluation'. Migrate dashboards domain by domain (infrastructure first, then application metrics) rather than all at once. Run Prometheus and ClickHouse in parallel for 30 days, validating that dashboard values match before switching alert evaluation to ClickHouse.
Prometheus metric naming conventions and label schemas must be preserved exactly in ClickHouse: a schema transformation at migration time breaks all existing Grafana dashboards; Alert rules expressed in PromQL must be rewritten for the new alert evaluation engine: the migration window requires running both systems in parallel and validating alert equivalence
Log shipping directly to Elasticsearch without Kafka buffer → Kafka-buffered log ingestion with backpressure and sampling controls
migration_planningTrigger: Elasticsearch indexing pressure causing log rejection (HTTP 429) back-pressuring into application services during incidents; log volume spikes during incidents causing Elasticsearch cluster instability; inability to replay historical logs when the indexing pipeline falls behind. Migrate from 'Log shipping directly to Elasticsearch without Kafka buffer' to 'Kafka-buffered log ingestion with backpressure and sampling controls'. Set Kafka log topic retention to 24 hours minimum. Deploy the Kafka-to-Elasticsearch consumer with auto-scaling enabled before switching log producers to write to Kafka. Validate consumer throughput at 10x normal volume with a load test before production cutover.
Introducing Kafka adds infrastructure complexity for the log shipping agents: every application must be updated to write to Kafka instead of directly to Elasticsearch; Kafka retention must be set to at least the Elasticsearch indexing catch-up time under worst-case incident volume: insufficient retention causes log loss if indexing falls behind more than the retention window
Prepare runbook for: Burst Traffic Cold Cache Stampede
simulation_preparednessSimulation demonstrates critical degradation of redis, postgresql
Without a runbook, recovery from this failure mode will be ad-hoc
Prepare runbook for: CQRS Projection Lag Expansion
simulation_preparednessSimulation demonstrates critical degradation of postgresql, kafka
Without a runbook, recovery from this failure mode will be ad-hoc
Plan evolution: OLTP Analytics Queries → OLTP + OLAP Separation
evolution_planningEvolution from Unified OLTP + Analytics on PostgreSQL → Separated OLTP (PostgreSQL) + OLAP (ClickHouse/Snowflake)
Migration complexity: medium. Rollback: always.
Plan evolution: Single Cache Layer → Distributed Cache
evolution_planningEvolution from Single Redis Node / Sentinel Cluster → Distributed Redis Cluster (Consistent Hash Ring)
Migration complexity: medium. Rollback: complex.
Monitor threshold: Tier 1: Metric Cardinality Budget Exceeded
scaling_monitoringSignal: ClickHouse part merge frequency increasing; dashboard queries timing out on metrics with high label cardinality; ClickHouse system.metrics showing active_parts count elevated; new metric instrumentation causing sudden storage growth disproportionate to fleet size; query_log showing metrics queries scanning full column segments without pruning
Bottleneck: Unbounded label cardinality generating millions of distinct time series that exceed ClickHouse part merge capacity and query planner pruning effectiveness. Evolution: Enforce a cardinality budget at ingestion: before a metric is accepted, evaluate the distinct value count of each label dimension against a per-dimension limit (e.g., max 100 distinct values for any single label key). Reject or rewrite metrics that exceed the budget: rewrite user_id labels to user_cohort or drop them entirely. Implement a cardinality analysis dashboard showing the top 10 highest-cardinality metric series sorted by storage cost. ClickHouse distributed table partitioning by metric name reduces the impact of a single high-cardinality metric on global query performance.
Monitor threshold: Tier 2: Log Volume Spike Exceeding Consumer Throughput
scaling_monitoringSignal: Kafka log topic consumer lag growing > 1 million messages during incident periods; Elasticsearch indexing throughput metrics showing queue buildup; incident post-mortems noting that relevant log records were not available in the search interface during the incident; log consumer memory pressure from unbounded batch accumulation
Bottleneck: Log Kafka consumer sized for normal throughput; unable to drain the spike volume produced during incident-driven log floods. Evolution: Size the log consumer for 10x normal throughput, not 1x: observability platform capacity must be planned for the incident scenario, not the steady state. Implement consumer autoscaling triggered by consumer lag metric: when Kafka consumer lag exceeds a threshold, add consumer instances automatically. Implement log sampling at the producer side for DEBUG and INFO level messages during identified spike periods : preserve all ERROR and WARN messages, sample INFO at 10%, sample DEBUG at 1%. This bounds the worst-case log volume without sacrificing diagnostic signal.
Scaling Pressure Signals
8ClickHouse part merge frequency increasing; dashboard queries timing out on metrics with high label cardinality; ClickHouse system.metrics showing active_parts count elevated; new metric instrumentation causing sudden storage growth disproportionate to fleet size; query_log showing metrics queries scanning full column segments without pruning
Threshold
Tier 1: Metric Cardinality Budget Exceeded
Likely Bottleneck
Unbounded label cardinality generating millions of distinct time series that exceed ClickHouse part merge capacity and query planner pruning effectiveness
Recommended Evolution
Enforce a cardinality budget at ingestion: before a metric is accepted, evaluate the distinct value count of each label dimension against a per-dimension limit (e.g., max 100 distinct values for any single label key). Reject or rewrite metrics that exceed the budget: rewrite user_id labels to user_cohort or drop them entirely. Implement a cardinality analysis dashboard showing the top 10 highest-cardinality metric series sorted by storage cost. ClickHouse distributed table partitioning by metric name reduces the impact of a single high-cardinality metric on global query performance.
Kafka log topic consumer lag growing > 1 million messages during incident periods; Elasticsearch indexing throughput metrics showing queue buildup; incident post-mortems noting that relevant log records were not available in the search interface during the incident; log consumer memory pressure from unbounded batch accumulation
Threshold
Tier 2: Log Volume Spike Exceeding Consumer Throughput
Likely Bottleneck
Log Kafka consumer sized for normal throughput; unable to drain the spike volume produced during incident-driven log floods
Recommended Evolution
Size the log consumer for 10x normal throughput, not 1x: observability platform capacity must be planned for the incident scenario, not the steady state. Implement consumer autoscaling triggered by consumer lag metric: when Kafka consumer lag exceeds a threshold, add consumer instances automatically. Implement log sampling at the producer side for DEBUG and INFO level messages during identified spike periods : preserve all ERROR and WARN messages, sample INFO at 10%, sample DEBUG at 1%. This bounds the worst-case log volume without sacrificing diagnostic signal.
Alert routing system receiving > 10,000 alert events per minute; on-call engineers reporting alert fatigue and inability to identify the root alert in notification floods; PagerDuty or equivalent showing duplicate alerts firing simultaneously for correlated failures; alert evaluation CPU dominating observability platform resource consumption
Threshold
Tier 3: Alert Evaluation Fanout Amplification
Likely Bottleneck
High-cardinality alert rules evaluating independently for every label combination, producing alert notification floods that are operationally equivalent to no alerting
Recommended Evolution
Introduce alert grouping at the evaluation layer: alerts on the same metric name within the same time window are grouped into a single notification with a count of affected series. Implement alert inhibition rules: if a datacenter-level alert fires, suppress region-level and service-level alerts that are downstream of the same failure. Move from per-series alert rules to aggregate alert rules: "more than 10% of service instances have error rate > 5%" is a single alert, not 500 individual alerts.
Monthly storage growth rate for metrics and logs exceeding capacity plan; ClickHouse data volume > 10TB with no retention enforcement in place; Elasticsearch index size growing faster than the retention policy deletes old indices; cost reports showing observability storage as top-3 infrastructure cost item
Threshold
Tier 4: Storage Cost Exceeding Retention Budget
Likely Bottleneck
No tiered retention strategy: all data retained at full resolution indefinitely rather than rolled up and archived to lower-cost storage
Recommended Evolution
Implement a three-tier retention strategy: hot tier (raw data, last 7 days, full resolution, ClickHouse), warm tier (1-hour rollups, last 90 days, ClickHouse compressed), cold tier (daily rollups, 2+ years, S3 Parquet via ClickHouse external tables or Athena). Log retention follows a separate policy: raw logs retained 30 days in Elasticsearch, then archived to S3 with a query interface for compliance replay. Alert evaluation always runs against the hot tier.
ClickHouse part merge frequency increasing; dashboard queries timing out on metrics with high label cardinality; ClickHouse system.metrics showing active_parts count elevated; new metric instrumentation causing sudden storage growth disproportionate to fleet size; query_log showing metrics queries scanning full column segments without pruning
Threshold
Escalation trigger: Unbounded label cardinality generating millions of distinct time series that exceed ClickHouse part merge capacity and query planner pruning effectiveness
Likely Bottleneck
Tier 1: Metric Cardinality Budget Exceeded
Recommended Evolution
Monitor: error_rate, p95_latency_ms
Kafka log topic consumer lag growing > 1 million messages during incident periods; Elasticsearch indexing throughput metrics showing queue buildup; incident post-mortems noting that relevant log records were not available in the search interface during the incident; log consumer memory pressure from unbounded batch accumulation
Threshold
Escalation trigger: Log Kafka consumer sized for normal throughput; unable to drain the spike volume produced during incident-driven log floods
Likely Bottleneck
Tier 2: Log Volume Spike Exceeding Consumer Throughput
Recommended Evolution
Monitor: error_rate, p95_latency_ms
Alert routing system receiving > 10,000 alert events per minute; on-call engineers reporting alert fatigue and inability to identify the root alert in notification floods; PagerDuty or equivalent showing duplicate alerts firing simultaneously for correlated failures; alert evaluation CPU dominating observability platform resource consumption
Threshold
Escalation trigger: High-cardinality alert rules evaluating independently for every label combination, producing alert notification floods that are operationally equivalent to no alerting
Likely Bottleneck
Tier 3: Alert Evaluation Fanout Amplification
Recommended Evolution
Monitor: error_rate, p95_latency_ms
Monthly storage growth rate for metrics and logs exceeding capacity plan; ClickHouse data volume > 10TB with no retention enforcement in place; Elasticsearch index size growing faster than the retention policy deletes old indices; cost reports showing observability storage as top-3 infrastructure cost item
Threshold
Escalation trigger: No tiered retention strategy: all data retained at full resolution indefinitely rather than rolled up and archived to lower-cost storage
Likely Bottleneck
Tier 4: Storage Cost Exceeding Retention Budget
Recommended Evolution
Monitor: error_rate, p95_latency_ms
Migration Readiness
12Migration Stages
3Prometheus + Grafana stack with local time-series storage → Kafka-buffered ClickHouse ingestion with Redis-backed alert evaluation
infoMigration trigger: Prometheus storage limits reached at production metric volume; ClickHouse required for sub-second aggregate queries across multi-day windows that Prometheus cannot serve; need to retain metrics beyond Prometheus 15-day default retention without capacity-limited remote write targets; cross-service correlation queries not possible in PromQL
Log shipping directly to Elasticsearch without Kafka buffer → Kafka-buffered log ingestion with backpressure and sampling controls
infoMigration trigger: Elasticsearch indexing pressure causing log rejection (HTTP 429) back-pressuring into application services during incidents; log volume spikes during incidents causing Elasticsearch cluster instability; inability to replay historical logs when the indexing pipeline falls behind
Direct ClickHouse queries for alert evaluation on every alert tick → TimescaleDB continuous aggregates as pre-computed alert evaluation views
infoMigration trigger: Alert evaluation latency > 1s causing missed alert firing windows; ClickHouse CPU saturated by alert evaluation scans competing with dashboard queries; alert evaluation queries scanning full column segments without partition pruning due to high cardinality in alert rule label selectors
Risks
9Prometheus metric naming conventions and label schemas must
warningPrometheus metric naming conventions and label schemas must be preserved exactly in ClickHouse: a schema transformation at migration time breaks all existing Grafana dashboards
Alert rules expressed in PromQL must be rewritten for the ne
warningAlert rules expressed in PromQL must be rewritten for the new alert evaluation engine: the migration window requires running both systems in parallel and validating alert equivalence
Introducing Kafka adds infrastructure complexity for the log
warningIntroducing Kafka adds infrastructure complexity for the log shipping agents: every application must be updated to write to Kafka instead of directly to Elasticsearch
Kafka retention must be set to at least the Elasticsearch in
warningKafka retention must be set to at least the Elasticsearch indexing catch-up time under worst-case incident volume: insufficient retention causes log loss if indexing falls behind more than the retention window
Continuous aggregate refresh interval determines the minimum
warningContinuous aggregate refresh interval determines the minimum alert detection latency: a 1-minute refresh interval means alerts cannot fire on sub-minute anomalies
TimescaleDB continuous aggregate must be defined for every m
warningTimescaleDB continuous aggregate must be defined for every metric queried by alert rules: adding new alert rules may require adding new continuous aggregates
Projection lag creates a read-after-write window where users
criticalProjection lag creates a read-after-write window where users see stale data after their own writes. Mitigation: Route immediate post-write reads to the write store (session-scoped write token); accept eventual consistency only for non-user-initiated reads
↗ direct-db-to-cqrs
Projection rebuild after schema change can take hours or day
criticalProjection rebuild after schema change can take hours or days on large datasets. Mitigation: Design blue/green projection deployment: build new projection in parallel before switching traffic; test rebuild time in staging
↗ direct-db-to-cqrs
Cross-service workflows that previously used database transa
criticalCross-service workflows that previously used database transactions now require Saga orchestration. Mitigation: Design idempotent event handlers; implement compensating transactions for every multi-step workflow; test failure injection in staging
↗ modular-monolith-to-event-driven
Review Sections
6Referenced Intelligence