{"id":15370269,"url":"https://github.com/jedisct1/rust-sieve-cache","last_synced_at":"2025-08-21T15:31:11.930Z","repository":{"id":217555070,"uuid":"744262769","full_name":"jedisct1/rust-sieve-cache","owner":"jedisct1","description":"SIEVE cache replacement policy for Rust.","archived":false,"fork":false,"pushed_at":"2024-07-01T20:28:20.000Z","size":24,"stargazers_count":16,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-10T19:39:25.813Z","etag":null,"topics":["cache","caching","lru","policy","replacement","rust","sieve","sieve-cache"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jedisct1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-16T23:54:29.000Z","updated_at":"2024-11-18T02:56:11.000Z","dependencies_parsed_at":"2024-01-17T04:46:21.863Z","dependency_job_id":"69fa1742-bec9-4664-8df5-1a40b874d896","html_url":"https://github.com/jedisct1/rust-sieve-cache","commit_stats":null,"previous_names":["jedisct1/rust-sieve-cache"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedisct1%2Frust-sieve-cache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedisct1%2Frust-sieve-cache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedisct1%2Frust-sieve-cache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedisct1%2Frust-sieve-cache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jedisct1","download_url":"https://codeload.github.com/jedisct1/rust-sieve-cache/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230520390,"owners_count":18238948,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","caching","lru","policy","replacement","rust","sieve","sieve-cache"],"created_at":"2024-10-01T13:40:40.558Z","updated_at":"2025-08-21T15:31:11.914Z","avatar_url":"https://github.com/jedisct1.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![dependency status](https://deps.rs/repo/github/jedisct1/rust-sieve-cache/status.svg)](https://deps.rs/repo/github/jedisct1/rust-sieve-cache)\n\n# SIEVE cache\n\nA high-performance implementation of the [SIEVE](http://sievecache.com) cache replacement algorithm for Rust, with both single-threaded and thread-safe variants.\n\n- [API documentation](https://docs.rs/sieve-cache)\n- [`crates.io` page](https://crates.io/crates/sieve-cache)\n\n## Overview\n\nSIEVE is an eviction algorithm that is simpler than LRU but achieves state-of-the-art efficiency on skewed workloads. It works by maintaining a single bit per entry that tracks whether an item has been \"visited\" since it was last considered for eviction.\n\n### Key Features\n\n- **Simple and efficient**: SIEVE requires less state than LRU or LFU algorithms\n- **Good performance on skewed workloads**: Particularly effective for real-world access patterns\n- **Adaptive capacity management**: Recommendation system to optimize cache size based on utilization\n- **Multiple implementations**:\n  - `SieveCache`: Core single-threaded implementation\n  - `SyncSieveCache`: Thread-safe wrapper using a single lock\n  - `ShardedSieveCache`: High-concurrency implementation using multiple locks\n\nThis implementation exposes the same API as the `clock-pro` and `arc-cache` crates, so it can be used as a drop-in replacement for them in existing applications.\n\n## Basic Usage\n\nThe library provides three cache implementations with different threading characteristics.\n\n### `SieveCache` - Single-Threaded Implementation\n\nThe core `SieveCache` implementation is designed for single-threaded use. It's the most efficient option when thread safety isn't required.\n\n```rust\nuse sieve_cache::SieveCache;\n\n// Create a new cache with a specific capacity\nlet mut cache: SieveCache\u003cString, String\u003e = SieveCache::new(100000).unwrap();\n\n// Insert key/value pairs into the cache\ncache.insert(\"foo\".to_string(), \"foocontent\".to_string());\ncache.insert(\"bar\".to_string(), \"barcontent\".to_string());\n\n// Retrieve a value from the cache (returns a reference to the value)\nassert_eq!(cache.get(\"foo\"), Some(\u0026\"foocontent\".to_string()));\n\n// Check if a key exists in the cache\nassert!(cache.contains_key(\"foo\"));\nassert!(!cache.contains_key(\"missing_key\"));\n\n// Get a mutable reference to a value\nif let Some(value) = cache.get_mut(\"foo\") {\n   *value = \"updated_content\".to_string();\n}\n\n// Remove an entry from the cache\nlet removed_value = cache.remove(\"bar\");\nassert_eq!(removed_value, Some(\"barcontent\".to_string()));\n\n// Query cache information\nassert_eq!(cache.len(), 1);       // Number of entries\nassert_eq!(cache.capacity(), 100000);  // Maximum capacity\nassert!(!cache.is_empty());       // Check if empty\n\n// Manual eviction (normally handled automatically when capacity is reached)\nlet evicted = cache.evict();  // Returns and removes a value that wasn't recently accessed\n\n// Get a recommendation for optimal cache capacity based on current utilization\nlet recommended = cache.recommended_capacity(0.5, 2.0, 0.3, 0.7);\nprintln!(\"Recommended capacity: {}\", recommended);\n```\n\n## Thread-Safe Implementations\n\nThese implementations are available when using the appropriate feature flags:\n- `SyncSieveCache` is available with the `sync` feature (enabled by default)\n- `ShardedSieveCache` is available with the `sharded` feature (enabled by default)\n\n### `SyncSieveCache` - Basic Thread-Safe Cache\n\nFor concurrent access from multiple threads, you can use the `SyncSieveCache` wrapper, which provides thread safety with a single global lock:\n\n```rust\nuse sieve_cache::SyncSieveCache;\nuse std::thread;\n\n// Create a thread-safe cache\nlet cache = SyncSieveCache::new(100000).unwrap();\n\n// The cache can be safely cloned and shared between threads\nlet cache_clone = cache.clone();\n\n// Insert from the main thread\ncache.insert(\"foo\".to_string(), \"foocontent\".to_string());\n\n// Access from another thread\nlet handle = thread::spawn(move || {\n    // Insert a new key\n    cache_clone.insert(\"bar\".to_string(), \"barcontent\".to_string());\n\n    // Get returns a clone of the value, not a reference (unlike non-thread-safe version)\n    assert_eq!(cache_clone.get(\u0026\"foo\".to_string()), Some(\"foocontent\".to_string()));\n});\n\n// Wait for the thread to complete\nhandle.join().unwrap();\n\n// Check if keys exist\nassert!(cache.contains_key(\u0026\"foo\".to_string()));\nassert!(cache.contains_key(\u0026\"bar\".to_string()));\n\n// Remove an entry\nlet removed = cache.remove(\u0026\"bar\".to_string());\nassert_eq!(removed, Some(\"barcontent\".to_string()));\n\n// Perform multiple operations atomically with exclusive access\ncache.with_lock(|inner_cache| {\n    // Operations inside this closure have exclusive access to the cache\n    inner_cache.insert(\"atomic1\".to_string(), \"value1\".to_string());\n    inner_cache.insert(\"atomic2\".to_string(), \"value2\".to_string());\n\n    // We can check internal state as part of the transaction\n    assert_eq!(inner_cache.len(), 3);\n});\n```\n\nKey differences from the non-thread-safe version:\n- Methods take `\u0026self` instead of `\u0026mut self`\n- `get()` returns a clone of the value instead of a reference\n- `with_lock()` method provides atomic multi-operation transactions\n\n### `ShardedSieveCache` - High-Performance Thread-Safe Cache\n\nFor applications with high concurrency requirements, the `ShardedSieveCache` implementation uses multiple internal locks (sharding) to reduce contention and improve throughput:\n\n```rust\nuse sieve_cache::ShardedSieveCache;\nuse std::thread;\nuse std::sync::Arc;\n\n// Create a sharded cache with default shard count (16)\n// We use Arc for sharing between threads\nlet cache = Arc::new(ShardedSieveCache::new(100000).unwrap());\n\n// Alternatively, specify a custom number of shards\n// let cache = Arc::new(ShardedSieveCache::with_shards(100000, 32).unwrap());\n\n// Insert data from the main thread\ncache.insert(\"foo\".to_string(), \"foocontent\".to_string());\n\n// Use multiple worker threads to insert data concurrently\nlet mut handles = vec![];\nfor i in 0..8 {\n    let cache_clone = Arc::clone(\u0026cache);\n    let handle = thread::spawn(move || {\n        // Each thread inserts multiple values\n        for j in 0..100 {\n            let key = format!(\"key_thread{}_item{}\", i, j);\n            let value = format!(\"value_{}\", j);\n            cache_clone.insert(key, value);\n        }\n    });\n    handles.push(handle);\n}\n\n// Wait for all threads to complete\nfor handle in handles {\n    handle.join().unwrap();\n}\n\n// Shard-specific atomic operations\n// with_key_lock locks only the shard containing the key\ncache.with_key_lock(\u0026\"foo\", |shard| {\n    // Operations inside this closure have exclusive access to the specific shard\n    shard.insert(\"related_key1\".to_string(), \"value1\".to_string());\n    shard.insert(\"related_key2\".to_string(), \"value2\".to_string());\n\n    // We can check internal state within the transaction\n    assert!(shard.contains_key(\u0026\"related_key1\".to_string()));\n});\n\n// Get the number of entries across all shards\n// Note: This acquires all shard locks sequentially\nlet total_entries = cache.len();\nassert_eq!(total_entries, 803); // 800 from threads + 1 \"foo\" + 2 related keys\n\n// Access shard information\nprintln!(\"Cache has {} shards with total capacity {}\",\n         cache.num_shards(), cache.capacity());\n```\n\n#### How Sharding Works\n\nThe `ShardedSieveCache` divides the cache into multiple independent segments (shards), each protected by its own mutex. When an operation is performed on a key:\n\n1. The key is hashed to determine which shard it belongs to\n2. Only that shard's lock is acquired for the operation\n3. Operations on keys in different shards can proceed in parallel\n\nThis design significantly reduces lock contention when operations are distributed across different keys, making it ideal for high-concurrency workloads.\n\n## Feature Flags\n\nThis crate provides the following feature flags to control which implementations are available:\n\n- `sync`: Enables the thread-safe `SyncSieveCache` implementation (enabled by default)\n- `sharded`: Enables the sharded `ShardedSieveCache` implementation (enabled by default)\n\nIf you only need specific implementations, you can select just the features you need:\n\n```toml\n# Only use the core implementation\nsieve-cache = { version = \"1\", default-features = false }\n\n# Only use the core and sync implementations\nsieve-cache = { version = \"1\", default-features = false, features = [\"sync\"] }\n\n# Only use the core and sharded implementations\nsieve-cache = { version = \"1\", default-features = false, features = [\"sharded\"] }\n\n# For documentation tests to work correctly\nsieve-cache = { version = \"1\", features = [\"doctest\"] }\n```\n\n## Adaptive Cache Sizing\n\nThe library includes a mechanism to recommend optimal cache sizes based on current utilization patterns.\n\n### How it Works\n\nThe `recommended_capacity` function analyzes:\n- The ratio of \"visited\" entries (recently accessed) to total entries\n- How full the cache is relative to its capacity\n- User-defined thresholds for scaling decisions\n\nBased on this analysis, it recommends:\n- Increasing capacity when many entries are frequently accessed (high utilization)\n- Decreasing capacity when few entries are frequently accessed (low utilization)\n- Maintaining current capacity when utilization is within normal parameters\n\n### Usage\n\n```rust\nuse sieve_cache::SieveCache;\n\nlet mut cache = SieveCache::\u003cString, String\u003e::new(1000).unwrap();\n\n// Add and access items...\n// (usage pattern will affect the recommendation)\n\n// Get recommended capacity with custom parameters\nlet recommended = cache.recommended_capacity(\n    0.5,    // min_factor: Never go below 50% of current capacity\n    2.0,    // max_factor: Never exceed 200% of current capacity\n    0.3,    // low_threshold: Consider decreasing below 30% utilization\n    0.7     // high_threshold: Consider increasing above 70% utilization\n);\n\nprintln!(\"Current capacity: {}\", cache.capacity());\nprintln!(\"Recommended capacity: {}\", recommended);\n\n// Optionally resize your cache based on the recommendation\n// (requires creating a new cache with the recommended size)\nif recommended != cache.capacity() {\n    let mut new_cache = SieveCache::new(recommended).unwrap();\n\n    // Transfer entries from old cache to new cache\n    for (key, value) in cache.iter() {\n        new_cache.insert(key.clone(), value.clone());\n    }\n\n    // Use the new cache from now on\n    cache = new_cache;\n}\n```\n\nThis adaptive sizing capability is available in all three cache implementations:\n\n```\n// Thread-safe version (with \"sync\" feature enabled)\n// use sieve_cache::SyncSieveCache;\n// let cache = SyncSieveCache::\u003cString, u32\u003e::new(1000).unwrap();\n// let recommended = cache.recommended_capacity(0.5, 2.0, 0.3, 0.7);\n\n// Sharded high-concurrency version (with \"sharded\" feature enabled)\n// use sieve_cache::ShardedSieveCache;\n// let cache = ShardedSieveCache::\u003cString, u32\u003e::new(1000).unwrap();\n// let recommended = cache.recommended_capacity(0.5, 2.0, 0.3, 0.7);\n```\n\n## Performance Considerations\n\nChoosing the right cache implementation depends on your workload:\n\n- **Single-threaded usage**: Use `SieveCache` - it's the most efficient with the lowest overhead\n- **Moderate concurrency**: Use `SyncSieveCache` - simple and effective with moderate thread count\n- **High concurrency**: Use `ShardedSieveCache` - best performance with many threads accessing different keys\n  - Sharding is most effective when operations are distributed across many keys\n  - If most operations target the same few keys (which map to the same shards), the benefits may be limited\n  - Generally, 16-32 shards provide a good balance of concurrency and overhead for most applications\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjedisct1%2Frust-sieve-cache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjedisct1%2Frust-sieve-cache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjedisct1%2Frust-sieve-cache/lists"}