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:
CacheEntity- defines cache key and prefixDataRepository<T>- fetches from databaseCacheFeed<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
| Type | Constructor/Method | Purpose | |||
|---|---|---|---|---|---|
CacheService<B> | new(backend) | Main cache orchestration | |||
CacheService<B> | execute(&mut feeder, &repo, strategy) | Execute cache operation | |||
CacheStrategy | Fresh \ | Refresh \ | Invalidate \ | Bypass | Cache execution strategy |
InMemoryBackend | new() | Development backend | |||
RedisBackend | new(config).await? | Production Redis backend | |||
MemcachedBackend | new(config).await? | Production Memcached backend |
Common Integration Patterns
Pattern A: Basic Entity Caching
- Define entity struct with
#[derive(Serialize, Deserialize)] - Implement
CacheEntity- define cache prefix and key - Implement
DataRepository- fetch from database - Implement
CacheFeed- store fetched entity - Call
cache.execute()in service layer - Handle
Resulterror 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
Decimaltoi64cents - 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
| Strategy | Behavior | Use Case |
|---|---|---|
Refresh | Try cache first, fallback to DB on miss | Default, balanced approach |
Fresh | Use cache only, return None on miss | When data must be cached |
Invalidate | Clear cache, fetch fresh from DB | After mutations, need fresh data |
Bypass | Skip cache entirely, always use DB | Testing 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
- Installation (Human) - Detailed human-readable guide
- Core Concepts - Conceptual deep-dive
- Database Compatibility - Supported ORMs and query builders
- Cache Backends - Backend detailed reference
- Serialization - Serialization format and limitations
- Async Model - Async/await patterns