Module cache

Module cache 

Source
Expand description

A prunable cache for ordered data with index-based lookups.

Data is stored in crate::journal::variable::Journal (an append-only log) and the location of written data is tracked in-memory by index to enable single-read lookups for cached data.

Unlike crate::archive::Archive, the Cache is optimized for simplicity and does not support key-based lookups (only index-based access is provided). This makes it ideal for caching sequential data where you know the exact index of the item you want to retrieve.

§Memory Overhead

Cache maintains a single in-memory map to track the location of each index item. The memory used to track each item is 8 + 4 + 4 bytes (where 8 is the index, 4 is the offset, and 4 is the length). This results in approximately 16 bytes of memory overhead per cached item.

§Pruning

Cache supports pruning up to a minimum index using the prune method. After prune is called on a section, all interaction with a section less than the pruned section will return an error. The pruning granularity is determined by items_per_blob in the configuration.

§Single Operation Reads

To enable single operation reads (i.e. reading all of an item in a single call to commonware_runtime::Blob), Cache stores the length of each item in its in-memory index. This ensures that reading a cached item requires only one disk operation.

§Compression

Cache supports compressing data before storing it on disk. This can be enabled by setting the compression field in the Config struct to a valid zstd compression level. This setting can be changed between initializations of Cache, however, it must remain populated if any data was written with compression enabled.

§Querying for Gaps

Cache tracks gaps in the index space to enable the caller to efficiently fetch unknown keys using next_gap. This is a very common pattern when syncing blocks in a blockchain.

§Example

use commonware_runtime::{Spawner, Runner, deterministic, buffer::PoolRef};
use commonware_storage::cache::{Cache, Config};
use commonware_utils::{NZUsize, NZU64};

let executor = deterministic::Runner::default();
executor.start(|context| async move {
    // Create a cache
    let cfg = Config {
        partition: "cache".into(),
        compression: Some(3),
        codec_config: (),
        items_per_blob: NZU64!(1024),
        write_buffer: NZUsize!(1024 * 1024),
        replay_buffer: NZUsize!(4096),
        buffer_pool: PoolRef::new(NZUsize!(1024), NZUsize!(10)),
    };
    let mut cache = Cache::init(context, cfg).await.unwrap();

    // Put data at index
    cache.put(1, 100u32).await.unwrap();

    // Get data by index
    let data: Option<u32> = cache.get(1).await.unwrap();
    assert_eq!(data, Some(100));

    // Check for gaps in the index space
    cache.put(10, 200u32).await.unwrap();
    let (current_end, start_next) = cache.next_gap(5);
    assert!(current_end.is_none());
    assert_eq!(start_next, Some(10));

    // Close the cache (also closes the journal)
    cache.close().await.unwrap();
});

Structs§

Cache
Implementation of Cache storage.
Config
Configuration for Cache storage.

Enums§

Error
Errors that can occur when interacting with the cache.