| Crates.io | minicache |
| lib.rs | minicache |
| version | 0.1.1 |
| created_at | 2025-10-20 07:33:53.452911+00 |
| updated_at | 2025-10-20 07:39:28.478702+00 |
| description | A fast, lightweight, async-compatible in-memory cache with TTL support and automatic cleanup |
| homepage | https://github.com/execute-soft/minicache |
| repository | https://github.com/execute-soft/minicache |
| max_upload_size | |
| id | 1891554 |
| size | 82,953 |
A fast, lightweight, async-compatible in-memory cache for Rust with TTL (Time-To-Live) support and automatic cleanup. Perfect for async applications that need efficient caching without the complexity.
tokio and async applicationsArc + RwLockAdd to your Cargo.toml:
[dependencies]
minicache = "0.1.0"
tokio = { version = "1.0", features = ["full"] }
use minicache::MiniCache;
use std::time::Duration;
#[tokio::main]
async fn main() {
// Create cache with 60-second cleanup interval
let cache = MiniCache::new(Duration::from_secs(60));
// Set a value (no expiration)
cache.set("user:123", "John Doe", None).await;
// Set a value with TTL
cache.set("session:abc", "temp_data", Some(Duration::from_secs(300))).await;
// Get values
if let Some(user) = cache.get(&"user:123").await {
println!("User: {}", user);
}
// Check if key exists
if cache.contains(&"session:abc").await {
println!("Session is active");
}
// Remove a key
cache.remove(&"user:123").await;
// Get cache statistics
println!("Cache size: {}", cache.len().await);
println!("All keys: {:?}", cache.keys().await);
}
use minicache::MiniCache;
use std::time::Duration;
#[tokio::main]
async fn main() {
let cache = MiniCache::new(Duration::from_secs(60));
// String keys and values
cache.set("name", "Alice", None).await;
assert_eq!(cache.get(&"name").await, Some("Alice"));
// Numeric keys
cache.set(42, "The Answer", None).await;
assert_eq!(cache.get(&42).await, Some("The Answer"));
// Custom types (must implement Clone)
#[derive(Clone, PartialEq, Debug)]
struct User { id: u32, name: String }
let user = User { id: 1, name: "Bob".to_string() };
cache.set("user:1", user.clone(), None).await;
assert_eq!(cache.get(&"user:1").await, Some(user));
}
use minicache::MiniCache;
use std::time::Duration;
use tokio::time::sleep;
#[tokio::main]
async fn main() {
let cache = MiniCache::new(Duration::from_millis(100));
// Set with 200ms TTL
cache.set("temp", "expires soon", Some(Duration::from_millis(200))).await;
// Value exists immediately
assert_eq!(cache.get(&"temp").await, Some("expires soon"));
// Wait for expiration
sleep(Duration::from_millis(250)).await;
// Value has expired
assert_eq!(cache.get(&"temp").await, None);
}
use minicache::MiniCache;
use std::sync::Arc;
use std::time::Duration;
#[tokio::main]
async fn main() {
let cache = Arc::new(MiniCache::new(Duration::from_secs(60)));
let mut handles = vec![];
// Spawn multiple tasks
for i in 0..10 {
let cache_clone = cache.clone();
let handle = tokio::spawn(async move {
// Each task writes 1000 entries
for j in 0..1000 {
let key = format!("task_{}_{}", i, j);
let value = format!("value_{}_{}", i, j);
cache_clone.set(key, value, None).await;
}
});
handles.push(handle);
}
// Wait for all tasks
for handle in handles {
handle.await.unwrap();
}
println!("Total entries: {}", cache.len().await);
}
use minicache::MiniCache;
use std::sync::Arc;
use std::time::Duration;
// Shared cache instance
type SharedCache = Arc<MiniCache<String, String>>;
async fn get_user_profile(cache: SharedCache, user_id: &str) -> Option<String> {
let cache_key = format!("user_profile:{}", user_id);
// Try cache first
if let Some(profile) = cache.get(&cache_key).await {
return Some(profile);
}
// Simulate database lookup
let profile = fetch_from_database(user_id).await;
// Cache for 5 minutes
cache.set(cache_key, profile.clone(), Some(Duration::from_secs(300))).await;
Some(profile)
}
async fn fetch_from_database(user_id: &str) -> String {
// Simulate slow database query
tokio::time::sleep(Duration::from_millis(100)).await;
format!("Profile data for user {}", user_id)
}
#[tokio::main]
async fn main() {
let cache = Arc::new(MiniCache::new(Duration::from_secs(60)));
// Multiple requests for same user - only first hits database
for _ in 0..5 {
let profile = get_user_profile(cache.clone(), "123").await;
println!("Got profile: {:?}", profile);
}
}
| Method | Description |
|---|---|
new(cleanup_interval) |
Create new cache with cleanup interval |
set(key, value, ttl) |
Store key-value pair with optional TTL |
get(key) |
Retrieve value by key |
remove(key) |
Delete specific key |
contains(key) |
Check if key exists (and not expired) |
clear() |
Remove all entries |
len() |
Get number of valid entries |
keys() |
Get all valid keys |
MiniCache<K, V>
where
K: Hash + Eq + Clone + Send + Sync + 'static,
V: Clone + Send + Sync + 'static,
Based on benchmarks (MacBook Pro M1):
Run benchmarks yourself:
cargo run --release --example quick_demo
| Feature | MiniCache | HashMap | DashMap | moka |
|---|---|---|---|---|
| Async/Await | โ | โ | โ | โ |
| TTL Support | โ | โ | โ | โ |
| Auto Cleanup | โ | โ | โ | โ |
| Zero Dependencies* | โ | โ | โ | โ |
| Memory Efficient | โ | โ | โ | โ |
*Except tokio for async runtime
// Fast cleanup for short-lived data
let fast_cache = MiniCache::new(Duration::from_millis(100));
// Slow cleanup for long-lived data
let slow_cache = MiniCache::new(Duration::from_secs(300));
// MiniCache operations don't return Results - they're designed to never fail
// However, you might want to handle potential issues:
#[tokio::main]
async fn main() {
let cache = MiniCache::new(Duration::from_secs(60));
// These operations are guaranteed to succeed
cache.set("key", "value", None).await;
let value = cache.get(&"key").await; // Returns Option<V>
// Handle missing values
match cache.get(&"missing").await {
Some(val) => println!("Found: {}", val),
None => println!("Key not found or expired"),
}
}
use minicache::MiniCache;
use std::time::Duration;
#[tokio::main]
async fn main() {
let cache = MiniCache::new(Duration::from_secs(60));
// Add some data
cache.set("key1", "value1", Some(Duration::from_secs(10))).await;
cache.set("key2", "value2", None).await;
// Monitor cache state
println!("Cache size: {}", cache.len().await);
println!("All keys: {:?}", cache.keys().await);
// Check specific keys
for key in ["key1", "key2", "key3"] {
if cache.contains(&key).await {
println!("{}: exists", key);
} else {
println!("{}: missing or expired", key);
}
}
}
Run the test suite:
cargo test
Run with output:
cargo test -- --nocapture
Test specific modules:
cargo test cache_operations
Quick performance demo:
cargo run --release --example quick_demo
Detailed benchmarks:
cargo bench
Memory profiling:
cargo run --release --example memory_profiler
Contributions are welcome! Please read our Contributing Guide for details.
git checkout -b feature/amazing-feature)cargo test)git commit -am 'Add amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the LICENSE file for details.
See CHANGELOG.md for version history and breaking changes.
Made with โค๏ธ for the Rust community