Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions ffi/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use firewood::{
use crate::{BorrowedBytes, CView, CreateProposalResult, KeyValuePair, arc_cache::ArcCache};

use crate::revision::{GetRevisionResult, RevisionHandle};
use metrics::counter;
use firewood_storage::firewood_counter;

/// Arguments for creating or opening a database. These are passed to [`fwd_open_db`]
///
Expand Down Expand Up @@ -176,11 +176,16 @@ impl DatabaseHandle {
self.create_proposal_handle(values.as_ref())?;

let root_hash = handle.commit_proposal(|commit_time| {
counter!("firewood.ffi.commit_ms").increment(commit_time.as_millis());
firewood_counter!(
"firewood.ffi.commit_ms",
"FFI commit timing in milliseconds"
)
.increment(commit_time.as_millis());
})?;

counter!("firewood.ffi.batch_ms").increment(start_time.elapsed().as_millis());
counter!("firewood.ffi.batch").increment(1);
firewood_counter!("firewood.ffi.batch_ms", "FFI batch timing in milliseconds")
.increment(start_time.elapsed().as_millis());
firewood_counter!("firewood.ffi.batch", "Number of FFI batch operations").increment(1);

Ok(root_hash)
}
Expand Down Expand Up @@ -208,9 +213,17 @@ impl DatabaseHandle {
})?;

if cache_miss {
counter!("firewood.ffi.cached_view.miss").increment(1);
firewood_counter!(
"firewood.ffi.cached_view.miss",
"Number of FFI cached view misses"
)
.increment(1);
} else {
counter!("firewood.ffi.cached_view.hit").increment(1);
firewood_counter!(
"firewood.ffi.cached_view.hit",
"Number of FFI cached view hits"
)
.increment(1);
}

Ok(view)
Expand Down
11 changes: 9 additions & 2 deletions ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ mod proposal;
mod revision;
mod value;

use firewood_storage::firewood_counter;

use firewood::v2::api::DbView;

pub use crate::handle::*;
Expand Down Expand Up @@ -527,8 +529,13 @@ pub unsafe extern "C" fn fwd_commit_proposal(
) -> HashResult {
invoke_with_handle(proposal, move |proposal| {
proposal.commit_proposal(|commit_time| {
metrics::counter!("firewood.ffi.commit_ms").increment(commit_time.as_millis());
metrics::counter!("firewood.ffi.commit").increment(1);
firewood_counter!(
"firewood.ffi.commit_ms",
"FFI commit timing in milliseconds"
)
.increment(commit_time.as_millis());
firewood_counter!("firewood.ffi.commit", "Number of FFI commit operations")
.increment(1);
})
})
}
Expand Down
9 changes: 7 additions & 2 deletions ffi/src/proofs/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::{
BorrowedBytes, DatabaseHandle, HashResult, Maybe, NextKeyRangeResult, RangeProofResult,
ValueResult, VoidResult,
};
use firewood_storage::firewood_counter;

/// A key range represented by a start key and an optional end key.
pub type KeyRange = (Box<[u8]>, Option<Box<[u8]>>);
Expand Down Expand Up @@ -224,8 +225,12 @@ impl<'db> RangeProofContext<'db> {
};

let metrics_cb = |commit_time: coarsetime::Duration| {
metrics::counter!("firewood.ffi.commit_ms").increment(commit_time.as_millis());
metrics::counter!("firewood.ffi.merge").increment(1);
firewood_counter!(
"firewood.ffi.commit_ms",
"FFI commit timing in milliseconds"
)
.increment(commit_time.as_millis());
firewood_counter!("firewood.ffi.merge", "Number of FFI merge operations").increment(1);
};

let result = proposal_handle.commit_proposal(metrics_cb);
Expand Down
10 changes: 7 additions & 3 deletions ffi/src/proposal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use firewood::v2::api::{self, DbView, HashKey, IntoBatchIter, Proposal as _};

use crate::{IteratorHandle, iterator::CreateIteratorResult};
use metrics::counter;
use firewood_storage::firewood_counter;

/// An opaque wrapper around a Proposal that also retains a reference to the
/// database handle it was created from.
Expand Down Expand Up @@ -125,8 +125,12 @@ impl<'db> CreateProposalResult<'db> {
let start_time = coarsetime::Instant::now();
let proposal = f()?;
let propose_time = start_time.elapsed();
counter!("firewood.ffi.propose_ms").increment(propose_time.as_millis());
counter!("firewood.ffi.propose").increment(1);
firewood_counter!(
"firewood.ffi.propose_ms",
"FFI propose timing in milliseconds"
)
.increment(propose_time.as_millis());
firewood_counter!("firewood.ffi.propose", "Number of FFI propose operations").increment(1);

let hash_key = proposal.root_hash()?;

Expand Down
13 changes: 9 additions & 4 deletions firewood/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ use crate::v2::api::{
use crate::manager::{ConfigManager, RevisionManager, RevisionManagerConfig};
use firewood_storage::{
CheckOpt, CheckerReport, Committed, FileBacked, FileIoError, HashedNodeReader,
ImmutableProposal, NodeStore, Parentable, ReadableStorage, TrieReader,
ImmutableProposal, NodeStore, Parentable, ReadableStorage, TrieReader, firewood_counter,
};
use metrics::{counter, describe_counter};
use std::io::Write;
use std::num::NonZeroUsize;
use std::path::Path;
Expand Down Expand Up @@ -168,9 +167,8 @@ impl Db {
/// Create a new database instance.
pub fn new<P: AsRef<Path>>(db_dir: P, cfg: DbConfig) -> Result<Self, api::Error> {
let metrics = Arc::new(DbMetrics {
proposals: counter!("firewood.proposals"),
proposals: firewood_counter!("firewood.proposals", "Number of proposals created"),
});
describe_counter!("firewood.proposals", "Number of proposals created");
let config_manager = ConfigManager::builder()
.create(cfg.create_if_missing)
.truncate(cfg.truncate)
Expand Down Expand Up @@ -385,6 +383,13 @@ impl<'db> api::Proposal for Proposal<'db> {
impl Proposal<'_> {
#[crate::metrics("firewood.proposal.create", "database proposal creation")]
fn create_proposal(&self, batch: impl IntoBatchIter) -> Result<Self, api::Error> {
// Proposal created based on another proposal
firewood_storage::firewood_counter!(
"firewood.proposals.based_on_proposal",
"Number of proposals created with a proposal parent"
)
.increment(1);

self.db.propose_with_parent(batch, &self.nodestore)
}

Expand Down
88 changes: 78 additions & 10 deletions firewood/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use std::path::PathBuf;
use std::sync::{Arc, OnceLock};

use firewood_storage::logger::{trace, warn};
use metrics::gauge;
use rayon::{ThreadPool, ThreadPoolBuilder};
use typed_builder::TypedBuilder;

Expand All @@ -29,7 +28,7 @@ use crate::v2::api::{ArcDynDbView, HashKey, OptionalHashKeyExt};
pub use firewood_storage::CacheReadStrategy;
use firewood_storage::{
BranchNode, Committed, FileBacked, FileIoError, HashedNodeReader, ImmutableProposal, NodeStore,
TrieHash,
TrieHash, firewood_counter, firewood_gauge,
};

const DB_FILE_NAME: &str = "firewood.db";
Expand Down Expand Up @@ -249,16 +248,45 @@ impl RevisionManager {
// This guarantee is there because we have a `&mut self` reference to the manager, so
// the compiler guarantees we are the only one using this manager.
match Arc::try_unwrap(oldest) {
Ok(oldest) => oldest.reap_deleted(&mut committed)?,
Ok(oldest) => {
oldest.reap_deleted(&mut committed)?;
firewood_counter!(
"firewood.revisions.reaped",
"Number of revisions removed from memory",
"mode" => "without_rootstore"
)
.increment(1);
}
Err(original) => {
warn!("Oldest revision could not be reaped; still referenced");
firewood_counter!(
"firewood.revisions.reaping_failed",
"Reaping attempts that failed due to outstanding references"
)
.increment(1);
self.in_memory_revisions.write().push_front(original);
break;
}
}
} else {
// RootStore is enabled: drop only in-memory handle and keep history on disk
firewood_counter!(
"firewood.revisions.reaped",
"Number of revisions removed from memory",
"mode" => "with_rootstore"
)
.increment(1);
}
gauge!("firewood.active_revisions").set(self.in_memory_revisions.read().len() as f64);
gauge!("firewood.max_revisions").set(self.max_revisions as f64);
firewood_gauge!(
"firewood.active_revisions",
"Current number of active revisions in memory"
)
.set(self.in_memory_revisions.read().len() as f64);
firewood_gauge!(
"firewood.max_revisions",
"Maximum number of revisions configured"
)
.set(self.max_revisions as f64);
}

// 3. Persist to disk.
Expand Down Expand Up @@ -289,10 +317,30 @@ impl RevisionManager {

// 6. Proposal Cleanup
// Free proposal that is being committed as well as any proposals no longer
// referenced by anyone else.
self.proposals
.lock()
.retain(|p| !Arc::ptr_eq(&proposal, p) && Arc::strong_count(p) > 1);
// referenced by anyone else. Track how many were discarded (dropped without commit).
{
let mut lock = self.proposals.lock();
let discarded = lock
.iter()
.filter(|p| !Arc::ptr_eq(&proposal, p) && Arc::strong_count(p) <= 1)
.count();
if discarded > 0 {
firewood_counter!(
"firewood.proposals.discarded",
"Number of proposals dropped without commit"
)
.increment(discarded as u64);
}

lock.retain(|p| !Arc::ptr_eq(&proposal, p) && Arc::strong_count(p) > 1);

// Update outstanding proposals gauge after cleanup
firewood_gauge!(
"firewood.proposals.outstanding",
"Current number of outstanding proposals"
)
.set(lock.len() as f64);
}

// then reparent any proposals that have this proposal as a parent
for p in &*self.proposals.lock() {
Expand Down Expand Up @@ -331,7 +379,14 @@ impl RevisionManager {
}

pub fn add_proposal(&self, proposal: ProposedRevision) {
self.proposals.lock().push(proposal);
let mut lock = self.proposals.lock();
lock.push(proposal);
// Update outstanding proposals gauge when adding
firewood_gauge!(
"firewood.proposals.outstanding",
"Current number of outstanding proposals"
)
.set(lock.len() as f64);
}

/// Retrieve a committed revision by its root hash.
Expand All @@ -341,6 +396,12 @@ impl RevisionManager {
pub fn revision(&self, root_hash: HashKey) -> Result<CommittedRevision, RevisionManagerError> {
// 1. Check the in-memory revision manager.
if let Some(revision) = self.by_hash.read().get(&root_hash).cloned() {
firewood_counter!(
"firewood.revisions.historical_queries",
"Historical revision queries by source",
"source" => "memory"
)
.increment(1);
return Ok(revision);
}

Expand All @@ -358,6 +419,13 @@ impl RevisionManager {
provided: root_hash.clone(),
})?;

firewood_counter!(
"firewood.revisions.historical_queries",
"Historical revision queries by source",
"source" => "rootstore"
)
.increment(1);

Ok(revision)
}

Expand Down
23 changes: 11 additions & 12 deletions firewood/src/merkle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ use firewood_storage::{
BranchNode, Child, Children, FileIoError, HashType, HashedNodeReader, ImmutableProposal,
IntoHashType, LeafNode, MaybePersistedNode, MutableProposal, NibblesIterator, Node, NodeStore,
Parentable, Path, PathComponent, ReadableStorage, SharedNode, TrieHash, TrieReader,
ValueDigest,
ValueDigest, firewood_counter,
};
use metrics::counter;
use std::collections::HashSet;
use std::fmt::Debug;
use std::io::Error;
Expand Down Expand Up @@ -659,7 +658,7 @@ impl<S: ReadableStorage> Merkle<NodeStore<MutableProposal, S>> {
(None, None) => {
// 1. The node is at `key`
node.update_value(value);
counter!("firewood.insert", "merkle" => "update").increment(1);
firewood_counter!("firewood.insert", "Number of merkle insert operations", "merkle" => "update").increment(1);
Ok(node)
}
(None, Some((child_index, partial_path))) => {
Expand All @@ -680,7 +679,7 @@ impl<S: ReadableStorage> Merkle<NodeStore<MutableProposal, S>> {
// Shorten the node's partial path since it has a new parent.
node.update_partial_path(partial_path);
branch.children[child_index] = Some(Child::Node(node));
counter!("firewood.insert", "merkle"=>"above").increment(1);
firewood_counter!("firewood.insert", "Number of merkle insert operations", "merkle"=>"above").increment(1);

Ok(Node::Branch(Box::new(branch)))
}
Expand All @@ -702,7 +701,7 @@ impl<S: ReadableStorage> Merkle<NodeStore<MutableProposal, S>> {
partial_path,
});
branch.children[child_index] = Some(Child::Node(new_leaf));
counter!("firewood.insert", "merkle"=>"below").increment(1);
firewood_counter!("firewood.insert", "Number of merkle insert operations", "merkle"=>"below").increment(1);
return Ok(node);
};
let child = self.read_for_update(child)?;
Expand All @@ -725,7 +724,7 @@ impl<S: ReadableStorage> Merkle<NodeStore<MutableProposal, S>> {

branch.children[child_index] = Some(Child::Node(new_leaf));

counter!("firewood.insert", "merkle"=>"split").increment(1);
firewood_counter!("firewood.insert", "Number of merkle insert operations", "merkle"=>"split").increment(1);
Ok(Node::Branch(Box::new(branch)))
}
}
Expand Down Expand Up @@ -755,7 +754,7 @@ impl<S: ReadableStorage> Merkle<NodeStore<MutableProposal, S>> {
});
branch.children[key_index] = Some(Child::Node(new_leaf));

counter!("firewood.insert", "merkle" => "split").increment(1);
firewood_counter!("firewood.insert", "Number of merkle insert operations", "merkle" => "split").increment(1);
Ok(Node::Branch(Box::new(branch)))
}
}
Expand All @@ -781,17 +780,17 @@ impl<S: ReadableStorage> Merkle<NodeStore<MutableProposal, S>> {
let root = self.nodestore.root_mut();
let Some(root_node) = std::mem::take(root) else {
// The trie is empty. There is nothing to remove.
counter!("firewood.remove", "prefix" => "false", "result" => "nonexistent")
firewood_counter!("firewood.remove", "Number of merkle remove operations", "prefix" => "false", "result" => "nonexistent")
.increment(1);
return Ok(None);
};

let (root_node, removed_value) = self.remove_helper(root_node, &key)?;
*self.nodestore.root_mut() = root_node;
if removed_value.is_some() {
counter!("firewood.remove", "prefix" => "false", "result" => "success").increment(1);
firewood_counter!("firewood.remove", "Number of merkle remove operations", "prefix" => "false", "result" => "success").increment(1);
} else {
counter!("firewood.remove", "prefix" => "false", "result" => "nonexistent")
firewood_counter!("firewood.remove", "Number of merkle remove operations", "prefix" => "false", "result" => "nonexistent")
.increment(1);
}
Ok(removed_value)
Expand Down Expand Up @@ -880,13 +879,13 @@ impl<S: ReadableStorage> Merkle<NodeStore<MutableProposal, S>> {
let root = self.nodestore.root_mut();
let Some(root_node) = std::mem::take(root) else {
// The trie is empty. There is nothing to remove.
counter!("firewood.remove", "prefix" => "true", "result" => "nonexistent").increment(1);
firewood_counter!("firewood.remove", "Number of merkle remove operations", "prefix" => "true", "result" => "nonexistent").increment(1);
return Ok(0);
};

let mut deleted = 0;
let root_node = self.remove_prefix_helper(root_node, &prefix, &mut deleted)?;
counter!("firewood.remove", "prefix" => "true", "result" => "success")
firewood_counter!("firewood.remove", "Number of merkle remove operations", "prefix" => "true", "result" => "success")
.increment(deleted as u64);
*self.nodestore.root_mut() = root_node;
Ok(deleted)
Expand Down
Loading
Loading