title: "Two Tier Cache Example" description: "" category: "Documentation" tags: [] last_updated: "March 28, 2025" version: "1.0"
title: "Two-Tier Cache Implementation Example" description: "Code examples demonstrating how to implement and use the two-tier caching system in Navius applications" category: examples tags:
- examples
- caching
- redis
- performance
- two-tier
- code related:
- ../guides/caching-strategies.md
- ../reference/configuration/cache-config.md
- ../reference/patterns/caching-patterns.md last_updated: March 27, 2025 version: 1.0
Two-Tier Cache Implementation Example
This example demonstrates how to implement and use the two-tier caching system in a Navius application.
Basic Implementation
use navius::app::cache::{create_two_tier_cache, create_typed_two_tier_cache};
use navius::core::services::cache_service::CacheService;
use std::time::Duration;
use serde::{Serialize, Deserialize};
// Define a type to cache
#[derive(Serialize, Deserialize, Clone, Debug)]
struct User {
id: String,
name: String,
email: String,
}
// Create a function to set up the cache
async fn setup_user_cache(cache_service: &CacheService) -> Result<(), AppError> {
// Create a two-tier cache for user data
// Fast cache (memory) TTL: 60 seconds
// Slow cache (Redis) TTL: 1 hour
let user_cache = create_typed_two_tier_cache::<User>(
"users",
cache_service,
Some(Duration::from_secs(60)),
Some(Duration::from_secs(3600)),
).await?;
// Store a user in the cache
let user = User {
id: "123".to_string(),
name: "Alice".to_string(),
email: "[email protected]".to_string(),
};
user_cache.set("user:123", &user, None).await?;
// Later, retrieve the user from the cache
if let Some(cached_user) = user_cache.get("user:123").await? {
println!("Found user: {:?}", cached_user);
}
Ok(())
}
Fallback Behavior Demonstration
This example shows how the two-tier cache handles fallback behavior:
use navius::app::cache::create_two_tier_cache;
use navius::core::services::cache_service::CacheService;
use std::time::Duration;
async fn demonstrate_fallback(cache_service: &CacheService) -> Result<(), AppError> {
// Create a two-tier cache
let cache = create_two_tier_cache(
"demo-cache",
cache_service,
Some(Duration::from_secs(30)), // Fast cache TTL
Some(Duration::from_secs(300)), // Slow cache TTL
).await?;
// Set a value in both caches
cache.set("key1", "value1".as_bytes(), None).await?;
// This will fetch from the fast cache (in-memory)
let fast_result = cache.get("key1").await?;
println!("Fast cache result: {:?}", fast_result);
// Simulate clearing the fast cache
// In a real scenario, this might happen when the app restarts
cache.fast_cache.clear().await?;
// This will now fetch from the slow cache (Redis) and promote to fast cache
let fallback_result = cache.get("key1").await?;
println!("After fallback result: {:?}", fallback_result);
// This will now fetch from the fast cache again as the value was promoted
let promoted_result = cache.get("key1").await?;
println!("After promotion result: {:?}", promoted_result);
Ok(())
}
Development Environment Setup
For development environments without Redis, you can use the memory-only two-tier cache:
use navius::app::cache::create_memory_only_two_tier_cache;
use navius::core::services::cache_service::CacheService;
use std::time::Duration;
async fn setup_dev_cache(cache_service: &CacheService) -> Result<(), AppError> {
// Create a memory-only two-tier cache
// Small fast cache TTL: 10 seconds
// Larger slow cache TTL: 60 seconds
let dev_cache = create_memory_only_two_tier_cache(
"dev-cache",
cache_service,
Some(Duration::from_secs(10)),
Some(Duration::from_secs(60)),
).await?;
// Use it like a normal cache
dev_cache.set("dev-key", "dev-value".as_bytes(), None).await?;
Ok(())
}
Integration with Service Layer
Here's how to integrate the two-tier cache with a service layer:
use navius::app::cache::create_typed_two_tier_cache;
use navius::core::services::cache_service::CacheService;
use std::time::Duration;
use std::sync::Arc;
struct UserService {
cache: Arc<Box<dyn TypedCache<User>>>,
repository: Arc<dyn UserRepository>,
}
impl UserService {
async fn new(
cache_service: &CacheService,
repository: Arc<dyn UserRepository>,
) -> Result<Self, AppError> {
let cache = Arc::new(create_typed_two_tier_cache::<User>(
"users",
cache_service,
Some(Duration::from_secs(60)),
Some(Duration::from_secs(3600)),
).await?);
Ok(Self { cache, repository })
}
async fn get_user(&self, id: &str) -> Result<Option<User>, AppError> {
let cache_key = format!("user:{}", id);
// Try to get from cache first
if let Some(user) = self.cache.get(&cache_key).await? {
return Ok(Some(user));
}
// If not in cache, get from repository
if let Some(user) = self.repository.find_by_id(id).await? {
// Store in cache for next time
self.cache.set(&cache_key, &user, None).await?;
return Ok(Some(user));
}
Ok(None)
}
}
Complete Application Example
Here's a complete example showing how to set up and use the two-tier cache in an API endpoint:
use axum::{
routing::get,
Router,
extract::{State, Path},
response::Json,
};
use navius::app::cache::create_typed_two_tier_cache;
use navius::core::services::cache_service::CacheService;
use std::sync::Arc;
use std::time::Duration;
// Application state with cache service
struct AppState {
user_service: Arc<UserService>,
}
async fn setup_app() -> Router {
// Create cache service
let cache_service = CacheService::new().await.unwrap();
// Create user repository
let user_repository = Arc::new(UserRepositoryImpl::new());
// Create user service with caching
let user_service = Arc::new(
UserService::new(&cache_service, user_repository).await.unwrap()
);
// Create application state
let app_state = Arc::new(AppState { user_service });
// Create router with API endpoints
Router::new()
.route("/users/:id", get(get_user))
.with_state(app_state)
}
// API endpoint that uses the cached service
async fn get_user(
State(state): State<Arc<AppState>>,
Path(id): Path<String>,
) -> Json<Option<User>> {
let user = state.user_service.get_user(&id).await.unwrap();
Json(user)
}
Best Practices
- Correctly size your caches: Small, frequently accessed data works best
- Set appropriate TTLs: Fast cache should have shorter TTL than slow cache
- Handle errors gracefully: The two-tier cache handles most errors internally
- Monitor performance: Track hit/miss rates to fine-tune cache settings
- Use typed caches: They provide type safety and easier code maintenance
Read More
For more details on caching strategies, see the Caching Strategies Guide.