Monitoring & Metrics

Monitoring cache-kit allows you to:

  • Detect problems early — Catch issues before users notice
  • Understand performance — Hit rates, latency, throughput
  • Optimize configuration — Data-driven tuning decisions
  • Alert on degradation — Automated on-call notifications
  • Troubleshoot quickly — Historical data for diagnosis

The 4 Golden Signals for Caching

  1. Latency — How fast are cache operations? (p50, p99)
  2. Traffic — How much are we using the cache? (ops/sec)
  3. Errors — What percentage of operations fail? (error rate)
  4. Hit rate — What percentage of requests hit cache? (cache efficiency)

Metrics Implementation

Simple Metrics Struct

use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::time::Instant;

#[derive(Clone)]
pub struct CacheMetrics {
    hits: Arc<AtomicU64>,
    misses: Arc<AtomicU64>,
    errors: Arc<AtomicU64>,
    latency_total_us: Arc<AtomicU64>,
    latency_count: Arc<AtomicU64>,
}

impl CacheMetrics {
    pub fn new() -> Self {
        CacheMetrics {
            hits: Arc::new(AtomicU64::new(0)),
            misses: Arc::new(AtomicU64::new(0)),
            errors: Arc::new(AtomicU64::new(0)),
            latency_total_us: Arc::new(AtomicU64::new(0)),
            latency_count: Arc::new(AtomicU64::new(0)),
        }
    }

    /// Record a cache hit
    pub fn record_hit(&self, latency_us: u64) {
        self.hits.fetch_add(1, Ordering::Relaxed);
        self.latency_total_us.fetch_add(latency_us, Ordering::Relaxed);
        self.latency_count.fetch_add(1, Ordering::Relaxed);
    }

    /// Record a cache miss
    pub fn record_miss(&self, latency_us: u64) {
        self.misses.fetch_add(1, Ordering::Relaxed);
        self.latency_total_us.fetch_add(latency_us, Ordering::Relaxed);
        self.latency_count.fetch_add(1, Ordering::Relaxed);
    }

    /// Record a cache error
    pub fn record_error(&self, latency_us: u64) {
        self.errors.fetch_add(1, Ordering::Relaxed);
        self.latency_total_us.fetch_add(latency_us, Ordering::Relaxed);
        self.latency_count.fetch_add(1, Ordering::Relaxed);
    }

    /// Get current hit rate (0.0 to 1.0)
    pub fn hit_rate(&self) -> f64 {
        let hits = self.hits.load(Ordering::Relaxed) as f64;
        let misses = self.misses.load(Ordering::Relaxed) as f64;
        if (hits + misses) == 0.0 {
            return 0.0;
        }
        hits / (hits + misses)
    }

    /// Get error rate (0.0 to 1.0)
    pub fn error_rate(&self) -> f64 {
        let errors = self.errors.load(Ordering::Relaxed) as f64;
        let total = self.hits.load(Ordering::Relaxed) as f64
                  + self.misses.load(Ordering::Relaxed) as f64
                  + errors;
        if total == 0.0 {
            return 0.0;
        }
        errors / total
    }

    /// Get average latency in microseconds
    pub fn avg_latency_us(&self) -> f64 {
        let count = self.latency_count.load(Ordering::Relaxed);
        if count == 0 {
            return 0.0;
        }
        let total = self.latency_total_us.load(Ordering::Relaxed) as f64;
        total / count as f64
    }

    /// Get total operations
    pub fn total_ops(&self) -> u64 {
        self.hits.load(Ordering::Relaxed)
            + self.misses.load(Ordering::Relaxed)
            + self.errors.load(Ordering::Relaxed)
    }

    /// Get throughput (ops/sec) — pass elapsed_secs
    pub fn throughput_ops_sec(&self, elapsed_secs: f64) -> f64 {
        self.total_ops() as f64 / elapsed_secs
    }
}

Instrument Your Code

pub async fn get_user_with_metrics(
    cache: &mut CacheExpander<impl CacheBackend>,
    repo: &UserRepository,
    user_id: String,
    metrics: &CacheMetrics,
) -> Result<Option<User>> {
    let start = Instant::now();
    let mut feeder = UserFeeder {
        id: user_id.clone(),
        user: None,
    };

    match cache.with(&mut feeder, repo, CacheStrategy::Refresh) {
        Ok(_) => {
            let latency_us = start.elapsed().as_micros() as u64;
            if feeder.user.is_some() {
                metrics.record_hit(latency_us);
                info!("Cache HIT for user {}", user_id);
            } else {
                metrics.record_miss(latency_us);
                info!("Cache MISS for user {}", user_id);
            }
            Ok(feeder.user)
        }
        Err(e) => {
            let latency_us = start.elapsed().as_micros() as u64;
            metrics.record_error(latency_us);
            error!("Cache ERROR for user {}: {}", user_id, e);
            Err(e)
        }
    }
}

Prometheus Integration

Expose Metrics Endpoint

See the Axum example for a complete working implementation with:

  • Metrics HTTP endpoint
  • API server with cache instrumentation
  • Prometheus scrape configuration

The metrics endpoint exposes the standard Prometheus format:

cache_hits_total (counter)
cache_misses_total (counter)
cache_errors_total (counter)
cache_hit_rate (gauge, 0.0-1.0)
cache_error_rate (gauge, 0.0-1.0)
cache_avg_latency_us (gauge)

Prometheus Scrape Configuration

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: "cache-kit"
    static_configs:
      - targets: ["localhost:3000"]
    metrics_path: "/metrics"

Alert Rules

# alerts.yml
groups:
  - name: cache-kit
    interval: 30s
    rules:
      # Alert if hit rate drops below 30%
      - alert: LowCacheHitRate
        expr: cache_hit_rate < 0.3
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Low cache hit rate ()"
          description: "Cache hit rate below 30% for 5 minutes"

      # Alert if error rate exceeds 5%
      - alert: HighCacheErrorRate
        expr: cache_error_rate > 0.05
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "High cache error rate ()"
          description: "Cache errors above 5% - likely backend down"

      # Alert if average latency exceeds 100ms
      - alert: SlowCacheLatency
        expr: cache_avg_latency_us > 0.9.00
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Slow cache latency ()"
          description: "Cache operations averaging > 100ms"

Key Metrics to Monitor

Business Metrics

MetricTargetAlert >
Cache Hit Rate> 70%< 30%
Error Rate< 1%> 5%
P99 Latency< 50ms> 100ms
ThroughputBaselineDrop > 20%

System Metrics

MetricTargetAlert >
Connection Pool Active< max== max (5 min)
Memory UsageStable+50% from baseline
Cache SizeBoundedGrowing unbounded
EvictionsNone> 0/min

Next Steps