Summary
Kiwi currently lacks production-grade observability. The existing MetricsCollector uses custom in-memory structs with JSON serialization — it cannot be scraped by Prometheus/Grafana, and the HealthCheckEndpoints HTTP server is a stub. Logging uses env_logger with unstructured text output. There is no distributed tracing.
This issue proposes a phased approach to establish a proper observability stack, informed by patterns from TiKV, Databend, and RisingWave.
Current State
| Dimension |
Status |
Gap |
| Metrics |
Custom RuntimeMetrics struct (in-memory, JSON) |
No Prometheus scrape endpoint; custom percentile calculation; no standard naming |
| Logging |
log crate + env_logger |
Unstructured text; no JSON format; no log level hot-reload; env_logger unsuitable for production |
| Tracing |
None |
Cannot trace request flow across net → channel → storage |
| Health |
HealthCheckEndpoints exists but HTTP server is a stub |
Not functional |
| Raft Metrics |
gRPC RaftMetricsService proto defined |
Not integrated into standard metrics |
Proposed Stack
| Concern |
Crate |
Rationale |
| Metrics |
prometheus v0.13 |
Production-proven by TiKV at scale; native Grafana support; prometheus-static-metric for hot-path optimization |
| Logging |
tracing + tracing-subscriber |
Tokio-native; structured JSON output; env-filter for runtime level control; ecosystem momentum over slog |
| Log bridging |
tracing-log |
Allows gradual migration — existing log::info!() calls work without changes |
| Tracing |
Defer to Phase 4 |
Per-request metrics labels sufficient initially; add opentelemetry later for cross-node tracing |
| Process metrics |
procfs (Linux) |
Collect CPU, memory, FDs, threads — similar to Databend's ProcessCollector |
Metrics Design
Naming Convention
Following TiKV's pattern: kiwi_{subsystem}_{metric}_{unit}
Key Metrics
1. Command Layer
kiwi_cmd_request_duration_seconds{cmd="GET"} # Histogram
kiwi_cmd_requests_total{cmd="SET",status="ok"} # Counter
2. Storage Layer (RocksDB, per CF)
kiwi_rocksdb_write_duration_seconds{cf="default"} # Histogram
kiwi_rocksdb_compaction_duration_seconds{cf="default"} # Histogram
kiwi_rocksdb_write_stall_total # Counter
kiwi_rocksdb_block_cache_hit_ratio # Gauge
kiwi_rocksdb_total_sst_files{cf="default"} # Gauge
3. Raft Layer (waterfall latency breakdown, inspired by TiKV)
kiwi_raft_propose_duration_seconds # Histogram
kiwi_raft_apply_duration_seconds # Histogram
kiwi_raft_log_entries_pending # Gauge
kiwi_raft_leader_changes_total # Counter
kiwi_raft_propose_stage_seconds{stage="batch_wait"} # Histogram (per stage)
4. Dual Runtime Channel
kiwi_channel_messages_total{direction="to_storage"} # Counter
kiwi_channel_buffer_utilization_ratio # Gauge
kiwi_channel_request_duration_seconds # Histogram
5. Connection Layer
kiwi_net_active_connections # Gauge
kiwi_net_bytes_received_total # Counter
Histogram Buckets
Following TiKV's proven bucket strategy:
// Latency: 10μs to ~11 minutes
exponential_buckets(0.00001, 2.0, 26)
// Byte sizes: 1KB to ~1TB
exponential_buckets(1024.0, 2.0, 31)
Per-Module Registration Pattern
Each crate owns its metrics.rs with LazyLock statics (Rust 2024 edition):
use prometheus::{register_histogram_vec, HistogramVec};
use std::sync::LazyLock;
pub static CMD_DURATION: LazyLock<HistogramVec> = LazyLock::new(|| {
register_histogram_vec!(
"kiwi_cmd_request_duration_seconds",
"Command request latency",
&["cmd"],
exponential_buckets(0.00001, 2.0, 26).unwrap()
).unwrap()
});
Phased Implementation
Phase 1 — Core Metrics + Prometheus Endpoint (2-3 days)
Phase 2 — Structured Logging (1-2 days)
Phase 3 — Raft + Channel Metrics + Health (1-2 days)
Phase 4 — Optional Enhancements
Architectural Notes
- Per-crate
metrics.rs: Each workspace crate (cmd, storage, raft, net) owns its metrics definitions. This maps cleanly to Kiwi's existing layout.
- Metrics exposure: Standard Prometheus pull model via HTTP
/metrics endpoint. No push gateway needed.
- Grafana dashboards: Ship JSON files in
metrics/grafana/ directory, following TiKV's pattern.
- Existing code preservation: The current
MetricsCollector in src/common/runtime/metrics.rs can coexist initially. New Prometheus-based metrics are additive. The custom health check structs can be reused as the backing data for the /health endpoint.
- No breaking changes: Phase 1-3 are purely additive. Existing
log crate calls continue to work via tracing-log bridge.
References
Labels
proposal, P2, observability
Summary
Kiwi currently lacks production-grade observability. The existing
MetricsCollectoruses custom in-memory structs with JSON serialization — it cannot be scraped by Prometheus/Grafana, and theHealthCheckEndpointsHTTP server is a stub. Logging usesenv_loggerwith unstructured text output. There is no distributed tracing.This issue proposes a phased approach to establish a proper observability stack, informed by patterns from TiKV, Databend, and RisingWave.
Current State
RuntimeMetricsstruct (in-memory, JSON)logcrate +env_loggerenv_loggerunsuitable for productionHealthCheckEndpointsexists but HTTP server is a stubRaftMetricsServiceproto definedProposed Stack
prometheusv0.13prometheus-static-metricfor hot-path optimizationtracing+tracing-subscriberenv-filterfor runtime level control; ecosystem momentum overslogtracing-loglog::info!()calls work without changesopentelemetrylater for cross-node tracingprocfs(Linux)ProcessCollectorMetrics Design
Naming Convention
Following TiKV's pattern:
kiwi_{subsystem}_{metric}_{unit}Key Metrics
1. Command Layer
2. Storage Layer (RocksDB, per CF)
3. Raft Layer (waterfall latency breakdown, inspired by TiKV)
4. Dual Runtime Channel
5. Connection Layer
Histogram Buckets
Following TiKV's proven bucket strategy:
Per-Module Registration Pattern
Each crate owns its
metrics.rswithLazyLockstatics (Rust 2024 edition):Phased Implementation
Phase 1 — Core Metrics + Prometheus Endpoint (2-3 days)
prometheusdependency to workspaceCargo.tomlsrc/common/metrics/crate with global registry and/metricsHTTP handlersrc/cmd/src/metrics.rs— record request duration and countsrc/storage/src/metrics.rs— RocksDB latency, write stall, cache hit ratio/metricsHTTP endpoint (onraft_addror dedicated port)Phase 2 — Structured Logging (1-2 days)
env_loggerwithtracing-subscriber(JSON format for production, pretty for dev)logcrate calls viatracing-log#[instrument]spans to critical paths:Cmd::do_cmd, storage operationsRUST_LOGenv filter for runtime log level controlPhase 3 — Raft + Channel Metrics + Health (1-2 days)
/healthendpoint (liveness + readiness probes)Phase 4 — Optional Enhancements
procfsTablePropertiesCollectorintegration for granular CF statsArchitectural Notes
metrics.rs: Each workspace crate (cmd,storage,raft,net) owns its metrics definitions. This maps cleanly to Kiwi's existing layout./metricsendpoint. No push gateway needed.metrics/grafana/directory, following TiKV's pattern.MetricsCollectorinsrc/common/runtime/metrics.rscan coexist initially. New Prometheus-based metrics are additive. The custom health check structs can be reused as the backing data for the/healthendpoint.logcrate calls continue to work viatracing-logbridge.References
prometheuscrate, per-modulemetrics.rs, waterfall latency, Grafana dashboardsprometheus-clientcrate,tracing+ OTel,GlobalRegistrypatterntracing+opentelemetry,mixtricswrapperLabels
proposal,P2,observability