Cache System
High-performance caching with multiple drivers and smart invalidation strategies.
MemoryFileRedisTagsStatistics
Quick Start
Basic Usage
dart
// Basic caching
await Khadem.cache.put('key', 'value', Duration(hours: 1));
final value = await Khadem.cache.get('key');
// Remember pattern
final data = await Khadem.cache.remember('user:123', Duration(hours: 1), () async {
return await User().find(123);
});
💡 Note: Cache is automatically configured via service provider
⚡ Tip: Use descriptive keys like 'user:123'
or 'posts:recent'
Cache Drivers
Memory
Fastest, process-specific
cache
File
Persistent, no external deps
cache.driver('file')
Redis
Distributed, high performance
cache.driver('redis')
Driver Selection Guide
Use Case | Recommended | Why |
---|---|---|
Development | Memory | Fast, no setup |
Production (single server) | File | Persistent, simple |
Distributed system | Redis | Shared across instances |
Core Operations
dart
// Store with TTL
await Khadem.cache.put('temp_data', data, Duration(minutes: 30));
// Retrieve
final data = await Khadem.cache.get('my_key');
// Check existence
if (await Khadem.cache.has('my_key')) {
print('Key exists');
}
// Remove
await Khadem.cache.forget('my_key');
// Clear all
await Khadem.cache.clear();
// Store permanently
await Khadem.cache.forever('app_config', config);
dart
// Get with default value
final settings = await Khadem.cache.get('settings') ?? defaultSettings;
// Remember pattern with complex computation
final result = await Khadem.cache.remember('complex_calc', Duration(hours: 2), () async {
// Expensive computation here
return await performComplexCalculation();
});
// Check multiple keys
final userExists = await Khadem.cache.has('user:123');
final profileExists = await Khadem.cache.has('user:123:profile');
// Batch operations (if supported by driver)
await Khadem.cache.put('key1', 'value1', Duration(hours: 1));
await Khadem.cache.put('key2', 'value2', Duration(hours: 1));
⚠️ Important Notes
- • Cache is not a replacement for proper database design
- • Always handle cache misses gracefully
- • Use appropriate TTL values based on data freshness needs
- • Monitor cache hit rates and adjust strategies as needed
Cache Tags
dart
// Tag items for group operations
await Khadem.cache.tag('user:123', ['users', 'active']);
await Khadem.cache.tag('user:456', ['users', 'active']);
// Clear by tag
await Khadem.cache.forgetByTag('users'); // Clears both users
// Multiple tags per key
await Khadem.cache.tag('post:789', ['posts', 'published', 'featured']);
// Clear specific tag group
await Khadem.cache.forgetByTag('posts'); // Clears all posts
When to Use Tags
- • Clear related cache entries together
- • Invalidate user-specific data on logout
- • Clear category/product caches on updates
- • Group cache by feature or module
Database Query Caching
dart
// Cache database queries
final posts = await Khadem.cache.remember('popular_posts', Duration(hours: 1), () async {
return await Post().query()
.with(['user', 'tags'])
.where('published', true)
.orderBy('views', 'desc')
.limit(10)
.get();
});
// Cache with automatic invalidation
Event.listen('post.created', (post) async {
await Khadem.cache.forget('popular_posts');
});
Best Practices
- • Cache results of complex JOINs and aggregations
- • Use appropriate TTL based on data update frequency
- • Include query parameters in cache key
- • Invalidate cache when underlying data changes
Model Caching
dart
class User extends KhademModel<User> {
static Future<User?> findCached(int id) async {
return await Khadem.cache.remember('user:$id', Duration(hours: 1), () async {
return await User().find(id);
});
}
@override
Future<bool> save() async {
final result = await super.save();
if (result) {
await Khadem.cache.forget('user:${id}'); // Invalidate cache
}
return result;
}
}
// Usage
final user = await User.findCached(123);
final users = await Khadem.cache.remember('users:active', Duration(hours: 2), () async {
return await User().query().where('active', true).get();
});
Model Cache Patterns
- • Cache frequently accessed model instances
- • Cache relationships and computed properties
- • Invalidate cache in model save/update methods
- • Use tags for related model data
Response Caching
dart
class ApiController {
static Future getPosts(Request req, Response res) async {
final posts = await Khadem.cache.remember('api:posts', Duration(minutes: 5), () async {
return await Post().query().with(['user']).paginate(1, 20);
});
res.header('Cache-Control', 'public, max-age=300');
res.sendJson({'data': posts});
}
}
HTTP Headers
Cache-Control: public
Cacheable by browsers/CDNs
ETag
Conditional requests (304)
Cache Statistics
dart
// Get cache statistics
final stats = Khadem.cache.stats;
print('Hit rate: ${stats.hitRate}%');
print('Total operations: ${stats.totalOperations}');
print('Hits: ${stats.hits}, Misses: ${stats.misses}');
// Monitor specific operations
await Khadem.cache.put('monitored_key', data, Duration(hours: 1));
// Stats are automatically updated
// Check cache health
if (stats.hitRate < 50.0) {
print('Warning: Low cache hit rate detected');
}
// Get detailed metrics
print('Read operations: ${stats.readOperations}');
print('Write operations: ${stats.writeOperations}');
print('Cache sets: ${stats.sets}');
print('Cache deletions: ${stats.deletions}');
Key Metrics
- • Hit rate percentage
- • Total operations count
- • Memory usage
- • Cache key count
- • Average response time
Best Practices
✅ Do's
- • Use descriptive, namespaced cache keys
- • Set appropriate TTL values
- • Handle cache misses gracefully
- • Monitor cache performance regularly
- • Use tags for logical grouping
- • Invalidate cache when data changes
- • Cache expensive operations only
❌ Don'ts
- • Don't cache sensitive user data
- • Don't rely on cache for critical business logic
- • Don't use cache as primary data store
- • Don't forget to handle cache failures
- • Don't use overly broad cache invalidation
- • Don't cache frequently changing data
- • Don't ignore memory limits
Common Patterns
Cache-Aside Pattern
Check cache first, then database
dart
// Cache-aside pattern
Future<User?> getUser(int id) async {
final cached = await Khadem.cache.get('user:$id');
if (cached != null) return User().fromJson(cached);
final user = await User().find(id);
if (user != null) {
await Khadem.cache.put('user:$id', user.toJson(), Duration(hours: 1));
}
return user;
}
Write-Through Pattern
Update cache and database together
dart
// Write-through pattern
Future createPost(Map<String, dynamic> data) async {
final post = await Post().create(data);
await Khadem.cache.put('post:${post.id}', post.toJson(), Duration(hours: 2));
return post;
}
Cache Warming
Pre-populate cache for better performance
dart
// Cache warming
class CacheWarmer {
static Future warmPopularContent() async {
await Khadem.cache.remember('posts:popular', Duration(hours: 1), () async {
return await Post().query()
.orderBy('views', 'desc')
.limit(20)
.get();
});
}
}
// Background refresh
Timer.periodic(Duration(minutes: 10), (_) async {
await CacheWarmer.warmPopularContent();
});