# Moka Cache Hit Rate Statistics: Measuring Cache Performance in Real Time

> Discover how to calculate Moka cache hit rate statistics by combining debug counters and TinyLFU data with manual instrumentation for real time performance measurement.

- Repository: [moka-rs/moka](https://github.com/moka-rs/moka)
- Tags: performance
- Published: 2026-03-07

---

**Moka does not expose a built-in hit-rate percentage directly, but provides debug counters and TinyLFU frequency sketch data that you can combine with manual instrumentation to calculate precise cache hit rate statistics.**

Moka is a high-performance concurrent cache library for Rust ([moka-rs/moka](https://github.com/moka-rs/moka)) that uses the TinyLFU admission policy to maintain near-optimal hit ratios. While the library tracks every cache access internally for its admission decisions, retrieving **Moka cache hit rate statistics** requires enabling the `unstable-debug-counters` feature and implementing your own hit/miss counters around lookup operations.

## How Moka Tracks All Accesses Internally

Moka’s **TinyLFU** (Tiny Least Frequently Used) admission policy relies on a probabilistic **Count-Min Sketch** to estimate key popularity. According to the source code in [[`src/policy.rs`](https://github.com/moka-rs/moka/blob/main/src/policy.rs)](https://github.com/moka-rs/moka/blob/main/src/policy.rs#L76-L78), this design intentionally tracks *all* accesses—not just keys currently stored in the cache.

The [`FrequencySketch`](https://github.com/moka-rs/moka/blob/main/src/common/frequency_sketch.rs) struct in [`src/common/frequency_sketch.rs`](https://github.com/moka-rs/moka/blob/main/src/common/frequency_sketch.rs) implements this mechanism. Every cache lookup (hit or miss) invokes `FrequencySketch::increment`, which updates a compact 4-bit counter table. The sketch periodically halves these counters to age out old traffic patterns, ensuring the popularity estimate reflects recent access patterns.

However, the sketch records *frequency*, not outcome. It cannot distinguish between a hit and a miss because its purpose is admission control (deciding whether to cache a new key), not performance metrics.

## Accessing Debug Statistics via the API

To inspect the internal state supporting hit rate analysis, enable the `unstable-debug-counters` feature in your [`Cargo.toml`](https://github.com/moka-rs/moka/blob/main/Cargo.toml):

```toml
[dependencies]
moka = { version = "0.12", features = ["future", "unstable-debug-counters"] }

```

With this feature enabled, the async cache exposes the `debug_stats()` method, implemented in [[`src/future/base_cache.rs`](https://github.com/moka-rs/moka/blob/main/src/future/base_cache.rs)](https://github.com/moka-rs/moka/blob/main/src/future/base_cache.rs#L139-L141) and publicly accessible via [[`src/future/cache.rs`](https://github.com/moka-rs/moka/blob/main/src/future/cache.rs)](https://github.com/moka-rs/moka/blob/main/src/future/cache.rs#L766-L767). This method returns a `CacheDebugStats` struct defined in [[`src/common/concurrent/debug_counters.rs`](https://github.com/moka-rs/moka/blob/main/src/common/concurrent/debug_counters.rs)](https://github.com/moka-rs/moka/blob/main/src/common/concurrent/debug_counters.rs#L119-L141):

```rust
use moka::future::Cache;

#[tokio::main]
async fn main() {
    let cache = Cache::builder()
        .max_capacity(100)
        .build();

    // Perform some cache operations
    cache.insert("key1", "value1").await;
    let _ = cache.get(&"key1").await; // hit
    let _ = cache.get(&"key2").await; // miss

    // Retrieve diagnostic snapshot
    let stats = cache.debug_stats().await;
    println!("Entries: {}", stats.entry_count);
    println!("Weighted size: {}", stats.weighted_size);
    println!("Frequency sketch size (bytes): {}", stats.freq_sketch_size);
    println!("HashMap capacity: {}", stats.hashmap_capacity);
}

```

These metrics help you understand cache occupancy and the memory overhead of the frequency sketch, but they do not provide the hit count directly.

## Calculating Hit Rate Manually

Since the internal `FrequencySketch` does not differentiate hits from misses, you must instrument your cache lookups to compute a traditional hit rate (`hits / (hits + misses)`). Wrap the `get` or `try_get_with` methods with atomic counters:

```rust
use moka::future::Cache;
use std::sync::atomic::{AtomicU64, Ordering};

static HITS: AtomicU64 = AtomicU64::new(0);
static MISSES: AtomicU64 = AtomicU64::new(0);

async fn get_with_metrics<K, V>(cache: &Cache<K, V>, key: &K) -> Option<V>
where
    K: std::hash::Hash + Eq + Clone + Send + Sync + 'static,
    V: Clone + Send + Sync + 'static,
{
    match cache.get(key).await {
        Some(value) => {
            HITS.fetch_add(1, Ordering::Relaxed);
            Some(value)
        }
        None => {
            MISSES.fetch_add(1, Ordering::Relaxed);
            None
        }
    }
}

#[tokio::main]
async fn main() {
    let cache = Cache::builder().max_capacity(10).build();
    
    cache.insert(1, "one").await;
    get_with_metrics(&cache, &1).await; // hit
    get_with_metrics(&cache, &2).await; // miss
    
    let hits = HITS.load(Ordering::Relaxed);
    let misses = MISSES.load(Ordering::Relaxed);
    let hit_rate = hits as f64 / (hits + misses).max(1) as f64;
    
    println!("Hit rate: {:.2}% ({}/{})", hit_rate * 100.0, hits, hits + misses);
}

```

This pattern gives you precise **Moka cache hit rate statistics** while the internal TinyLFU sketch ensures the cache maintains optimal admission policies based on the same access patterns you are measuring.

## Understanding the Frequency Sketch Structure

The `FrequencySketch` in [`src/common/frequency_sketch.rs`](https://github.com/moka-rs/moka/blob/main/src/common/frequency_sketch.rs) uses a fixed-size table of 4-bit counters (16 possible values per slot) to estimate popularity. When you call `debug_stats()`, the `freq_sketch_size` field reveals the memory footprint of this structure.

For advanced debugging, you can inspect the sketch capacity:

```rust
use moka::common::frequency_sketch::FrequencySketch;

fn inspect_sketch(sketch: &FrequencySketch) {
    // Returns the number of counter slots in the table
    println!("Sketch table length: {}", sketch.table_len());
}

```

The sketch automatically handles hash collisions via the Count-Min algorithm and performs periodic aging (halving all counters) to decay old statistics, as implemented in the `reset` and `increment` methods between [lines 70-124](https://github.com/moka-rs/moka/blob/main/src/common/frequency_sketch.rs#L70-L124).

## Summary

- **Moka does not provide a native hit-rate percentage metric**; you must instrument lookups manually using atomic counters or similar metrics libraries.
- Enable the **`unstable-debug-counters`** feature to access `Cache::debug_stats()`, which returns entry counts, weighted size, and frequency sketch memory usage from [`src/common/concurrent/debug_counters.rs`](https://github.com/moka-rs/moka/blob/main/src/common/concurrent/debug_counters.rs).
- The **TinyLFU policy** tracks all accesses (hits and misses) via the `FrequencySketch` in [`src/common/frequency_sketch.rs`](https://github.com/moka-rs/moka/blob/main/src/common/frequency_sketch.rs) to optimize admission decisions, but this data represents popularity, not hit rate.
- Combine **manual hit/miss counters** with the `debug_stats()` metadata to calculate accurate hit rates while monitoring the cache’s internal health.

## Frequently Asked Questions

### Does Moka provide a built-in hit rate metric?

No. According to the source code in [`src/common/concurrent/debug_counters.rs`](https://github.com/moka-rs/moka/blob/main/src/common/concurrent/debug_counters.rs), Moka exposes `CacheDebugStats` containing entry counts and sketch sizes, but not hit/miss counters. You must wrap `Cache::get` calls with your own instrumentation to calculate hit rates.

### What is the TinyLFU frequency sketch?

The `FrequencySketch` is a Count-Min Sketch implementation that uses 4-bit counters to probabilistically estimate how frequently keys are accessed. Located in [`src/common/frequency_sketch.rs`](https://github.com/moka-rs/moka/blob/main/src/common/frequency_sketch.rs), it powers the TinyLFU admission policy by tracking all cache lookups to determine which items deserve cache residency.

### How do I enable debug statistics in Moka?

Add the `unstable-debug-counters` feature to your [`Cargo.toml`](https://github.com/moka-rs/moka/blob/main/Cargo.toml) dependency on Moka, then call `cache.debug_stats().await` on async cache instances. This returns a snapshot of internal metrics including `entry_count`, `weighted_size`, and `freq_sketch_size`.

### Why does Moka track missed keys in its statistics?

The TinyLFU admission policy requires a global view of key popularity to decide whether to admit new entries. As documented in [`src/policy.rs`](https://github.com/moka-rs/moka/blob/main/src/policy.rs), tracking *all* accesses—including misses—allows the cache to recognize "one-hit wonders" (keys accessed only once) and prevent them from evicting frequently used items, thus maintaining the near-optimal hit ratio claimed in the project README.