Installation for Agents


This page provides structured, machine-readable information for AI agents, code generators, and automated tools integrating cache-kit.

Quick Reference for Agents

Essential imports:

use cache_kit::{
    CacheService, CacheEntity, CacheFeed, DataRepository,
    backend::InMemoryBackend,  // or RedisBackend, MemcachedBackend
    strategy::CacheStrategy,
    Result,
};

Three required trait implementations:

  1. CacheEntity - defines cache key and prefix
  2. DataRepository<T> - fetches from database
  3. CacheFeed<T> - receives cached entity

Basic usage pattern:

let cache = CacheService::new(InMemoryBackend::new());
cache.execute(&mut feeder, &repository, CacheStrategy::Refresh).await?;

Available strategies: Fresh, Refresh (default), Invalidate, Bypass

Backends: InMemoryBackend (dev), RedisBackend (prod), MemcachedBackend (prod)

Dependency Declaration

Minimal Setup (InMemory - Development/Testing)

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

Production Setup (Redis)

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

All Backends

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

Required Trait Implementations

Every cache-kit integration requires implementing three traits:

1. CacheEntity

pub trait CacheEntity {
    type Key: Into<String>;
    fn cache_key(&self) -> Self::Key;
    fn cache_prefix() -> &'static str;
}

Implementation example:

use cache_kit::CacheEntity;
use serde::{Deserialize, Serialize};

#[derive(Clone, Serialize, Deserialize)]
struct User {
    id: String,
    name: String,
}

impl CacheEntity for User {
    type Key = String;
    fn cache_key(&self) -> Self::Key { self.id.clone() }
    fn cache_prefix() -> &'static str { "user" }
}

2. DataRepository

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

Implementation example:

use cache_kit::{DataRepository, Result};

struct UserRepository;

impl DataRepository<User> for UserRepository {
    async fn fetch_by_id(&self, id: &String) -> Result<Option<User>> {
        // Your database fetch logic here
        Ok(Some(User {
            id: id.clone(),
            name: "Alice".to_string(),
        }))
    }
}

3. CacheFeed

pub trait CacheFeed<T: CacheEntity> {
    fn entity_id(&mut self) -> T::Key;
    fn feed(&mut self, entity: Option<T>);
}

Implementation example:

use cache_kit::CacheFeed;

struct UserFeeder {
    id: String,
    user: Option<User>,
}

impl CacheFeed<User> for UserFeeder {
    fn entity_id(&mut self) -> String { self.id.clone() }
    fn feed(&mut self, entity: Option<User>) { self.user = entity; }
}

Alternative: Use GenericFeeder (simpler for basic cases):

use cache_kit::feed::GenericFeeder;

// GenericFeeder already implements CacheFeed
let mut feeder = GenericFeeder::<User>::new("user_123".to_string());
// After cache.execute(), access via feeder.data

Complete Working Example

Copy-paste ready example:

use cache_kit::{
    CacheService, CacheEntity, CacheFeed, DataRepository,
    backend::InMemoryBackend,
    strategy::CacheStrategy,
    Result,
};
use serde::{Deserialize, Serialize};

// 1. Define entity
#[derive(Clone, Serialize, Deserialize)]
struct User {
    id: String,
    name: String,
}

// 2. Implement CacheEntity
impl CacheEntity for User {
    type Key = String;
    fn cache_key(&self) -> Self::Key { self.id.clone() }
    fn cache_prefix() -> &'static str { "user" }
}

// 3. Implement DataRepository
struct UserRepository;

impl DataRepository<User> for UserRepository {
    async fn fetch_by_id(&self, id: &String) -> Result<Option<User>> {
        // Your database fetch logic here
        Ok(Some(User {
            id: id.clone(),
            name: "Alice".to_string(),
        }))
    }
}

// 4. Implement CacheFeed
struct UserFeeder {
    id: String,
    user: Option<User>,
}

impl CacheFeed<User> for UserFeeder {
    fn entity_id(&mut self) -> String { self.id.clone() }
    fn feed(&mut self, entity: Option<User>) { self.user = entity; }
}

// 5. Use it
#[tokio::main]
async fn main() -> Result<()> {
    // Create backend
    let backend = InMemoryBackend::new();

    // Create service
    let cache = CacheService::new(backend);

    // Create repository
    let repository = UserRepository;

    // Create feeder
    let mut feeder = UserFeeder {
        id: "user_123".to_string(),
        user: None,
    };

    // Execute cache operation
    cache.execute(&mut feeder, &repository, CacheStrategy::Refresh).await?;

    // Use the result
    if let Some(user) = feeder.user {
        println!("User: {}", user.name);
    }

    Ok(())
}

Initialization Pattern

use cache_kit::{
    CacheService, CacheEntity, CacheFeed, DataRepository,
    backend::InMemoryBackend,
    strategy::CacheStrategy,
    Result,
};

// Step 1: Create backend
let backend = InMemoryBackend::new();
// OR: RedisBackend::new(config).await?
// OR: MemcachedBackend::new(config).await?

// Step 2: Create service
let cache = CacheService::new(backend);

// Step 3: Execute cache operation (must be in async context)
cache.execute(&mut feeder, &repository, CacheStrategy::Refresh).await?;

Backend Configuration Reference

InMemory Backend

  • Feature: inmemory (enabled by default)
  • Use Case: Development, testing, single-instance services
  • Initialization: InMemoryBackend::new() (no config required)
  • Thread-safe: Yes (uses DashMap internally)
  • External dependencies: None

Redis Backend

  • Feature: redis
  • Use Case: Production distributed caching
  • Initialization: RedisBackend::new(config).await?

Configuration struct:

RedisConfig {
    host: String,                      // Default: "localhost"
    port: u16,                         // Default: 6379
    username: Option<String>,          // Default: None
    password: Option<String>,          // Default: None
    database: u32,                     // Default: 0
    pool_size: u32,                    // Default: 16
    connection_timeout: Duration,      // Default: 5 seconds
}

Configuration Examples:

use cache_kit::backend::RedisConfig;
use std::time::Duration;

// Basic (localhost)
let config = RedisConfig::default();

// With authentication
let config = RedisConfig {
    host: "redis.example.com".to_string(),
    port: 6379,
    password: Some("secret".to_string()),
    ..Default::default()
};

// Custom database
let config = RedisConfig {
    host: "localhost".to_string(),
    port: 6379,
    database: 1,
    ..Default::default()
};

Environment variable pattern:

use cache_kit::backend::{RedisBackend, RedisConfig};
use std::time::Duration;

let redis_host = std::env::var("REDIS_HOST")
    .unwrap_or_else(|_| "localhost".to_string());
let redis_password = std::env::var("REDIS_PASSWORD").ok();
let config = RedisConfig {
    host: redis_host,
    password: redis_password,
    ..Default::default()
};
let backend = RedisBackend::new(config).await?;

Memcached Backend

  • Feature: memcached
  • Use Case: High-performance distributed caching
  • Initialization: MemcachedBackend::new(config).await?

Configuration struct:

use cache_kit::backend::MemcachedConfig;
use std::time::Duration;

MemcachedConfig {
    servers: Vec<String>,              // Required: Memcached server addresses
    connection_timeout: Duration,     // Default: 5 seconds
    pool_size: u32,                   // Default: 16
}

Configuration Examples:

use cache_kit::backend::{MemcachedBackend, MemcachedConfig};
use std::time::Duration;

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

// Multiple servers (uses first server for connection pool)
let config = MemcachedConfig {
    servers: vec![
        "localhost:11211".to_string(),
        "cache-2.example.com:11211".to_string(),
    ],
    pool_size: 20,
    connection_timeout: Duration::from_secs(10),
};
let backend = MemcachedBackend::new(config).await?;

Key Types and Methods

TypeConstructor/MethodPurpose
CacheService<B>new(backend)Main cache orchestration
CacheService<B>execute(&mut feeder, &repo, strategy)Execute cache operation
CacheStrategyFresh \Refresh \Invalidate \BypassCache execution strategy
InMemoryBackendnew()Development backend
RedisBackendnew(config).await?Production Redis backend
MemcachedBackendnew(config).await?Production Memcached backend

Common Integration Patterns

Pattern A: Basic Entity Caching

  1. Define entity struct with #[derive(Serialize, Deserialize)]
  2. Implement CacheEntity - define cache prefix and key
  3. Implement DataRepository - fetch from database
  4. Implement CacheFeed - store fetched entity
  5. Call cache.execute() in service layer
  6. Handle Result error cases

Pattern B: Dependency Injection

use cache_kit::{CacheService, backend::InMemoryBackend};

// Create once at startup
let cache = CacheService::new(InMemoryBackend::new());

// Pass to services
let user_service = UserService::new(cache.clone());
let order_service = OrderService::new(cache.clone());

// Note: CacheService implements Clone (cheap Arc increment)

Pattern C: Environment-Based Backend Selection

use cache_kit::{
    CacheService,
    backend::{InMemoryBackend, RedisBackend, RedisConfig},
    Result,
};

#[tokio::main]
async fn main() -> Result<()> {
    let cache = match std::env::var("ENV").as_deref() {
        Ok("production") => {
            let redis_host = std::env::var("REDIS_HOST")
                .unwrap_or_else(|_| "localhost".to_string());
            let redis_password = std::env::var("REDIS_PASSWORD").ok();

            let config = RedisConfig {
                host: redis_host,
                password: redis_password,
                pool_size: 20,
                ..Default::default()
            };
            CacheService::new(RedisBackend::new(config).await?)
        }
        _ => CacheService::new(InMemoryBackend::new())
    };

    // Use cache...
    Ok(())
}

Note: For type-erased backends across different types, consider using an enum wrapper or trait objects. For most cases, the above pattern works when you know the backend type at compile time.


Serialization Support

Supported formats:

  • Postcard (Tier-1, recommended for performance)
  • Custom implementations via trait impl

Known Limitations:

  • Some types not supported by Postcard (e.g., Decimal)

Workarounds:

  • Convert Decimal to i64 cents
  • Implement custom serialization wrapper
  • Use alternative serialization format

Error Handling

Cache-kit returns Result<T, Error>. Always handle:

  • Backend connection failures
  • Serialization errors
  • Repository fetch failures
  • Network timeouts (Redis/Memcached)

Best practice: Application should gracefully degrade if cache is unavailable.

use cache_kit::{Result, Error};

match cache.execute(&mut feeder, &repository, strategy).await {
    Ok(_) => {
        // Success - use feeder.entity
        if let Some(user) = feeder.user {
            // Process user
        }
    },
    Err(e) => {
        // Log error
        eprintln!("Cache error: {}", e);
        // Fall back to direct repository fetch
        feeder.user = repository.fetch_by_id(&feeder.id).await.ok().flatten();
    }
}

Cache Strategies

StrategyBehaviorUse Case
RefreshTry cache first, fallback to DB on missDefault, balanced approach
FreshUse cache only, return None on missWhen data must be cached
InvalidateClear cache, fetch fresh from DBAfter mutations, need fresh data
BypassSkip cache entirely, always use DBTesting or temporary disable

Deployment Configuration Checklist

  • [ ] Backend selected: InMemory (dev) or Redis/Memcached (prod)
  • [ ] Connection pooling configured for expected load
  • [ ] Serialization format validated for all entity types
  • [ ] Error handlers implemented for all cache calls
  • [ ] Environment variables set (REDIS_URL, backend addresses)
  • [ ] Fallback strategy defined if cache unavailable
  • [ ] TTL policies defined (if using expiration)
  • [ ] Load testing completed
  • [ ] Monitoring/metrics enabled for cache hit/miss rates

Reference Links