Skip to content

Commit

Permalink
Allow for caching of reads (#789)
Browse files Browse the repository at this point in the history
  • Loading branch information
rkuris authored Feb 12, 2025
1 parent 88754d3 commit 0f9d959
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 8 deletions.
8 changes: 7 additions & 1 deletion firewood/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use typed_builder::TypedBuilder;

use crate::v2::api::HashKey;

use storage::{Committed, FileBacked, ImmutableProposal, NodeStore, Parentable, TrieHash};
use storage::{
CacheReadStrategy, Committed, FileBacked, ImmutableProposal, NodeStore, Parentable, TrieHash,
};

#[derive(Clone, Debug, TypedBuilder)]
/// Revision manager configuratoin
Expand All @@ -29,6 +31,9 @@ pub struct RevisionManagerConfig {

#[builder(default_code = "NonZero::new(40000).expect(\"non-zero\")")]
free_list_cache_size: NonZero<usize>,

#[builder(default = CacheReadStrategy::WritesOnly)]
cache_read_strategy: CacheReadStrategy,
}

type CommittedRevision = Arc<NodeStore<Committed, FileBacked>>;
Expand Down Expand Up @@ -73,6 +78,7 @@ impl RevisionManager {
config.node_cache_size,
config.free_list_cache_size,
truncate,
config.cache_read_strategy,
)?);
let nodestore = match truncate {
true => Arc::new(NodeStore::new_empty_committed(storage.clone())?),
Expand Down
16 changes: 16 additions & 0 deletions storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,19 @@ pub use linear::filebacked::FileBacked;
pub use linear::memory::MemStore;

pub use trie_hash::TrieHash;

/// The strategy for caching nodes that are read
/// from the storage layer. Generally, we only want to
/// cache write operations, but for some read-heavy workloads
/// you can enable caching of branch reads or all reads.
#[derive(Clone, Debug)]
pub enum CacheReadStrategy {
/// Only cache writes (no reads will be cached)
WritesOnly,

/// Cache branch reads (reads that are not leaf nodes)
BranchReads,

/// Cache all reads (leaves and branches)
All,
}
29 changes: 28 additions & 1 deletion storage/src/linear/filebacked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::sync::{Arc, Mutex};
use lru::LruCache;
use metrics::counter;

use crate::{LinearAddress, Node};
use crate::{CacheReadStrategy, LinearAddress, Node};

use super::{ReadableStorage, WritableStorage};

Expand All @@ -26,6 +26,7 @@ pub struct FileBacked {
fd: File,
cache: Mutex<LruCache<LinearAddress, Arc<Node>>>,
free_list_cache: Mutex<LruCache<LinearAddress, Option<LinearAddress>>>,
cache_read_strategy: CacheReadStrategy,
}

impl FileBacked {
Expand All @@ -35,6 +36,7 @@ impl FileBacked {
node_cache_size: NonZero<usize>,
free_list_cache_size: NonZero<usize>,
truncate: bool,
cache_read_strategy: CacheReadStrategy,
) -> Result<Self, Error> {
let fd = OpenOptions::new()
.read(true)
Expand All @@ -47,6 +49,7 @@ impl FileBacked {
fd,
cache: Mutex::new(LruCache::new(node_cache_size)),
free_list_cache: Mutex::new(LruCache::new(free_list_cache_size)),
cache_read_strategy,
})
}
}
Expand Down Expand Up @@ -74,6 +77,28 @@ impl ReadableStorage for FileBacked {
counter!("firewood.cache.freelist", "type" => if cached.is_some() { "hit" } else { "miss" }).increment(1);
cached
}

fn cache_read_strategy(&self) -> &CacheReadStrategy {
&self.cache_read_strategy
}

fn cache_node(&self, addr: LinearAddress, node: Arc<Node>) {
match self.cache_read_strategy {
CacheReadStrategy::WritesOnly => {
// we don't cache reads
}
CacheReadStrategy::All => {
let mut guard = self.cache.lock().expect("poisoned lock");
guard.put(addr, node);
}
CacheReadStrategy::BranchReads => {
if !node.is_leaf() {
let mut guard = self.cache.lock().expect("poisoned lock");
guard.put(addr, node);
}
}
}
}
}

impl WritableStorage for FileBacked {
Expand Down Expand Up @@ -169,6 +194,7 @@ mod test {
NonZero::new(10).unwrap(),
NonZero::new(10).unwrap(),
false,
CacheReadStrategy::WritesOnly,
)
.unwrap();
let mut reader = fb.stream_from(0).unwrap();
Expand Down Expand Up @@ -208,6 +234,7 @@ mod test {
NonZero::new(10).unwrap(),
NonZero::new(10).unwrap(),
false,
CacheReadStrategy::WritesOnly,
)
.unwrap();
let mut reader = fb.stream_from(0).unwrap();
Expand Down
10 changes: 9 additions & 1 deletion storage/src/linear/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use std::io::{Error, Read};
use std::num::NonZero;
use std::sync::Arc;

use crate::{LinearAddress, Node};
use crate::{CacheReadStrategy, LinearAddress, Node};
pub(super) mod filebacked;
pub mod memory;

Expand Down Expand Up @@ -51,6 +51,14 @@ pub trait ReadableStorage: Debug + Sync + Send {
fn free_list_cache(&self, _addr: LinearAddress) -> Option<Option<LinearAddress>> {
None
}

/// Return the cache read strategy for this readable storage
fn cache_read_strategy(&self) -> &CacheReadStrategy {
&CacheReadStrategy::WritesOnly
}

/// Cache a node for future reads
fn cache_node(&self, _addr: LinearAddress, _node: Arc<Node>) {}
}

/// Trait for writable storage.
Expand Down
21 changes: 16 additions & 5 deletions storage/src/nodestore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ use std::sync::Arc;

use crate::hashednode::hash_node;
use crate::node::{ByteCounter, Node};
use crate::{Child, FileBacked, Path, ReadableStorage, TrieHash};
use crate::{CacheReadStrategy, Child, FileBacked, Path, ReadableStorage, TrieHash};

use super::linear::WritableStorage;

Expand Down Expand Up @@ -217,13 +217,24 @@ impl<T: ReadInMemoryNode, S: ReadableStorage> NodeStore<T, S> {

debug_assert!(addr.get() % 8 == 0);

let addr = addr.get() + 1; // skip the length byte
let actual_addr = addr.get() + 1; // skip the length byte

let _span = LocalSpan::enter_with_local_parent("read_and_deserialize");

let area_stream = self.storage.stream_from(addr)?;
let node = Node::from_reader(area_stream)?;
Ok(node.into())
let area_stream = self.storage.stream_from(actual_addr)?;
let node = Arc::new(Node::from_reader(area_stream)?);
match self.storage.cache_read_strategy() {
CacheReadStrategy::All => {
self.storage.cache_node(addr, node.clone());
}
CacheReadStrategy::BranchReads => {
if !node.is_leaf() {
self.storage.cache_node(addr, node.clone());
}
}
CacheReadStrategy::WritesOnly => {}
}
Ok(node)
}
}

Expand Down

0 comments on commit 0f9d959

Please sign in to comment.