4 releases
0.2.0 | Jul 3, 2024 |
---|---|
0.1.4 | Jun 29, 2024 |
#56 in Caching
211 downloads per month
88KB
1.5K
SLoC
SineCache
SineCache is a high-performance, in-memory caching library for Rust, designed to efficiently store and manage key-value pairs with support for various eviction policies and persistence options using Append-Only Files (AOF).
Features
Powerful Caching Mechanism
SineCache provides a robust caching solution with flexible configurations and support for multiple eviction policies, ensuring optimal performance for varying application needs.
Eviction Policies
Choose from FIFO (First-In, First-Out), LRU (Least Recently Used), and LFU (Least Frequently Used) eviction policies. Additionally, define custom eviction policies through a simple trait implementation.
Asynchronous Support
- AsyncCache: Wraps the
Cache
struct with atokio::sync::Mutex
, enabling safe concurrent access with asynchronous operations (async
versions ofget
,put
,remove
, etc.).
Persistence with Append-Only Files (AOF)
Optionally persist cache data using AOF, ensuring durability and recovery of cache state across application restarts. If flush_time
is provided (milliseconds), data is flushed to disk after every flush_time
milliseconds to disk without blocking the main thread. In case of None
, every operation is flushed to disk in the same thread.
Thread Safety
Ensures thread safety with appropriate locking mechanisms (tokio::sync::Mutex
for AsyncCache
), making it suitable for multi-threaded environments.
Configuration Flexibility
Configure cache size limits, eviction policies, AOF settings, and more through intuitive configuration structs (CacheSyncConfig
and AsyncCacheConfig
).
Comprehensive Documentation
Extensive API documentation and examples facilitate easy integration and customization within applications.
Safety and Reliability
Built with Rust's ownership model and type system, ensuring memory safety and preventing common bugs like null pointer dereferencing and data races.
Getting Started
To use SineCache in your Rust project, add it to your Cargo.toml
:
[dependencies]
sine_cache = "0.2.0"
Examples
Some examples are listed below but for the more detailed documentation, visit: https://docs.rs/sine_cache/latest/sine_cache/
Cache
- Synchronous Cache:
Simple methods related to Cache
. For using it in concurrent environment, customize on top of it like wrapping in Mutex and using async
methods etc.
use sine_cache::{cache::Cache, config::CacheConfig};
fn main() {
let capacity = 10; // Maximum number of entries in the cache.
let mut cache = Cache::new(sine_cache::config::CacheSyncConfig::LFU(CacheConfig{max_size: capacity}));
// Inserting key-value pairs into the cache
cache.put(1, "One");
cache.put(1, "one"); // Overwrites previous value
cache.put(2, "Two");
// Retrieving a value from the cache
let value = cache.get(&1);
assert!(value.is_some_and(|x| x == &"one"));
}
AsyncCache
- Asynchronous Cache:
Some examples related to AsyncCache
are listed below:
-
Without
AOF
:When
AOF
is not required:
use sine_cache::{cache::AsyncCache, config::{AsyncCacheConfig, EvictionAsyncConfig}};
#[tokio::main]
async fn main() {
let capacity = 10; // Maximum number of entries in the cache.
let mut cache = AsyncCache::new(AsyncCacheConfig::LFU(EvictionAsyncConfig {max_size: capacity, aof_config: None})).await;
// Inserting key-value pairs into the cache
cache.put(1, String::from("One")).await;
cache.put(1, String::from("one")).await; // Overwrites previous value
cache.put(2, String::from("Two")).await;
// Retrieving a value from the cache
let value = cache.get(&1).await;
assert!(value.is_some_and(|x| x == "one"));
}
-
With
AOF
:When AOF is required, we can pass details related to AOF in the configurations and set the periodic flushes to disk or each operation record to disk based on setting
flush_time
in milliseconds orNone
.
use sine_cache::{cache::AsyncCache, config::{AsyncCacheConfig, EvictionAsyncConfig, EvictionAOFConfig}};
#[tokio::main]
async fn main() {
let capacity = 10; // Maximum number of entries in the cache.
let mut cache = AsyncCache::new(AsyncCacheConfig::LFU(EvictionAsyncConfig {
max_size: capacity,
aof_config: Some(EvictionAOFConfig {
folder: String::from("./data"), //folder in which persistent file should be written.
cache_name: String::from("async_lof_cache"), //Unique cache name as with same name file will be created.
flush_time: Some(5000) //After every 5000 milliseconds data will be flushed to disk.
})
})).await;
// Inserting key-value pairs into the cache
cache.put(1, String::from("One")).await;
cache.put(1, String::from("one")).await; // Overwrites previous value
cache.put(2, String::from("Two")).await;
// Retrieving a value from the cache
let value = cache.get(&1).await;
assert!(value.is_some_and(|x| x == "one"));
}
Custom eviction policy
Custom evicton policies can also be defined and used with all the features of AsyncCache
and Cache
.
use sine_cache::eviction_policies::common::EvictionPolicy;
use sine_cache::{cache::AsyncCache, config::{AsyncCacheConfig, CustomEvictionAsyncConfig, CustomEvictionAOFConfig}};
pub struct CustomEviction<K> {
_phantom: std::marker::PhantomData<K>,
}
impl<K: Eq + std::hash::Hash + Clone> CustomEviction<K> {
pub fn new() -> Self{
Self{
_phantom: std::marker::PhantomData
}
}
}
impl<K: Eq + std::hash::Hash + Clone> EvictionPolicy<K> for CustomEviction<K> {
fn on_get(&mut self, key: &K) {
// nothing to do.
}
fn on_set(&mut self, key: K) {
// nothing to do.
}
fn evict(&mut self) -> Option<K> {
// nothing to do
None
}
fn remove(&mut self, key: K) {
//nothing to do
}
}
#[tokio::main]
async fn main() {
let capacity = 10; // Maximum number of entries in the cache.
let mut cache = AsyncCache::new(AsyncCacheConfig::Custom(CustomEvictionAsyncConfig {
max_size: capacity,
aof_config: Some(CustomEvictionAOFConfig {
folder: String::from("./data"), //folder in which persistent file should be written.
cache_name: String::from("async_lof_custom_cache"), //Unique cache name as with same name file will be created.
flush_time: Some(5000), //After every 5000 milliseconds data will be flushed to disk.
persist_read_ops: true //whether to store reads also, true generally.
}),
policy: Box::new(CustomEviction::new())
})).await;
// Inserting key-value pairs into the cache
cache.put(1, String::from("One")).await;
cache.put(1, String::from("one")).await; // Overwrites previous value
cache.put(2, String::from("Two")).await;
// Retrieving a value from the cache
let value = cache.get(&1).await;
assert!(value.is_some_and(|x| x == "one"));
}
Planned Features
AOF Compaction Periodically
Compact AOF files periodically to stop the append only file becoming too large.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Dependencies
~3–10MB
~97K SLoC