Async-First Philosophy

cache-kit is built from the ground up as an async-first library. This design choice reflects the reality of modern Rust services where:

  • Database queries are async (SQLx, SeaORM, tokio-postgres)
  • HTTP handlers are async (Axum, Actix, warp)
  • gRPC services are async (tonic)
  • Background workers are async (tokio, async-std)

The cache layer sits between these components and must integrate seamlessly with async workflows.


Tokio Runtime Integration

cache-kit is designed for tokio-based applications. The library does not:

  • Spawn its own runtime
  • Require a specific runtime configuration
  • Impose threading models on your application

Instead, cache-kit operates within your existing tokio runtime.

Runtime Requirements

[dependencies]
tokio = { version = "1.41", features = ["rt", "sync", "macros"] }
cache-kit = "0.9"

The minimum required tokio features:

  • rt — Runtime support
  • sync — Synchronization primitives (Arc, Mutex, RwLock)
  • macros#[tokio::main] attribute macro

Interaction Model

The typical interaction flow follows this pattern:

Async Database → Async Cache → Async Application

All cache operations work seamlessly within async contexts. For detailed ORM integration examples (SQLx, SeaORM, Diesel), see Database & ORM Compatibility.


Why DataRepository is Async

The DataRepository trait uses async methods:

pub trait DataRepository<T: CacheEntity>: Send + Sync {
    async fn fetch_by_id(&self, id: &T::Key) -> Result<Option<T>>;
}

This design is intentional and provides several benefits:

  1. Native async support — Aligns with modern Rust practices and integrates seamlessly with async databases
  2. Flexibility — Works with both sync and async database layers (see Database Compatibility for Diesel example using spawn_blocking)
  3. Backend compatibility — Cache backends (Redis, Memcached) are inherently async

Recommended async databases:

  • SQLx — Async, compile-time checked SQL
  • SeaORM — Async ORM for Rust
  • tokio-postgres — Pure async PostgreSQL client

For detailed repository implementation examples, see Database & ORM Compatibility.

⚠️ NEVER use block_in_place + block_on

NEVER use block_in_place + Handle::current().block_on() — this pattern is incorrect. Always use async fn with .await for async databases. For synchronous ORMs like Diesel, use tokio::task::spawn_blocking (see Database Compatibility for examples).


Async Cache Backends

Cache backends are fully async and follow the same initialization pattern:

// Redis
use cache_kit::backend::{RedisBackend, RedisConfig};
let config = RedisConfig::default(); // Uses localhost:6379
let backend = RedisBackend::new(config).await?;

// Memcached
use cache_kit::backend::{MemcachedBackend, MemcachedConfig};
let config = MemcachedConfig { servers: vec!["localhost:11211".to_string()], ..Default::default() };
let backend = MemcachedBackend::new(config)?;

// InMemory (lock-free via DashMap)
use cache_kit::backend::InMemoryBackend;
let backend = InMemoryBackend::new();

// All use the same expander API
let mut expander = CacheExpander::new(backend);

All backends work seamlessly within your async context — no special handling required.


Runtime Choice is Yours

cache-kit does not:

  • Require a specific tokio runtime configuration
  • Spawn background tasks (no tokio::spawn calls)
  • Create thread pools
  • Impose executor choices

You control:

  • Runtime flavor (multi-thread, current-thread)
  • Worker thread count
  • Task spawning strategy
  • Shutdown behavior

Best Practices

DO

  • ✅ Use tokio::main for your application entry point
  • ✅ Make DataRepository::fetch_by_id an async function
  • ✅ Use async database drivers (SQLx, SeaORM, tokio-postgres)
  • ✅ Let cache-kit operate within your existing runtime
  • ✅ Keep async boundaries explicit and clear

DON’T

  • ❌ Use block_in_place + block_on (incorrect pattern)
  • ❌ Call block_on inside async contexts
  • ❌ Create multiple tokio runtimes unnecessarily
  • ❌ Assume cache-kit manages runtime lifecycle

Example: Full Async Service

For complete working examples of tokio-based services using cache-kit with async operations:


Next Steps

Updated: