Skip to content

Commit ce2ddb3

Browse files
authored
Merge pull request #1523 from spacejam/tyler_merge_right
[bloodstone] merge empty leaf with right sibling, consuming right sibling
2 parents c83d2ef + db36bf0 commit ce2ddb3

File tree

9 files changed

+322
-205
lines changed

9 files changed

+322
-205
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
CLAUDE.md
12
fuzz-*.log
23
default.sled
34
timing_test*

src/block_checker.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use std::collections::BTreeMap;
2+
use std::panic::Location;
3+
use std::sync::atomic::{AtomicU64, Ordering};
4+
use std::sync::{LazyLock, Mutex};
5+
6+
static COUNTER: AtomicU64 = AtomicU64::new(0);
7+
static CHECK_INS: LazyLock<BlockChecker> = LazyLock::new(|| {
8+
std::thread::spawn(move || {
9+
let mut last_top_10 = Default::default();
10+
loop {
11+
std::thread::sleep(std::time::Duration::from_secs(5));
12+
last_top_10 = CHECK_INS.report(last_top_10);
13+
}
14+
});
15+
16+
BlockChecker::default()
17+
});
18+
19+
type LocationMap = BTreeMap<u64, &'static Location<'static>>;
20+
21+
#[derive(Default)]
22+
pub(crate) struct BlockChecker {
23+
state: Mutex<LocationMap>,
24+
}
25+
26+
impl BlockChecker {
27+
fn report(&self, last_top_10: LocationMap) -> LocationMap {
28+
let state = self.state.lock().unwrap();
29+
println!("top 10 longest blocking sections:");
30+
31+
let top_10: LocationMap =
32+
state.iter().take(10).map(|(k, v)| (*k, *v)).collect();
33+
34+
for (id, location) in &top_10 {
35+
if last_top_10.contains_key(id) {
36+
println!("id: {}, location: {:?}", id, location);
37+
}
38+
}
39+
40+
top_10
41+
}
42+
43+
fn check_in(&self, location: &'static Location) -> BlockGuard {
44+
let next_id = COUNTER.fetch_add(1, Ordering::Relaxed);
45+
let mut state = self.state.lock().unwrap();
46+
state.insert(next_id, location);
47+
BlockGuard { id: next_id }
48+
}
49+
50+
fn check_out(&self, id: u64) {
51+
let mut state = self.state.lock().unwrap();
52+
state.remove(&id);
53+
}
54+
}
55+
56+
pub(crate) struct BlockGuard {
57+
id: u64,
58+
}
59+
60+
impl Drop for BlockGuard {
61+
fn drop(&mut self) {
62+
CHECK_INS.check_out(self.id)
63+
}
64+
}
65+
66+
#[track_caller]
67+
pub(crate) fn track_blocks() -> BlockGuard {
68+
let caller = Location::caller();
69+
CHECK_INS.check_in(caller)
70+
}

src/db.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ impl<const LEAF_FANOUT: usize> Db<LEAF_FANOUT> {
175175
let mut ever_seen = std::collections::HashSet::new();
176176
let before = std::time::Instant::now();
177177

178+
#[cfg(feature = "for-internal-testing-only")]
179+
let _b0 = crate::block_checker::track_blocks();
180+
178181
for (_cid, tree) in self.trees.lock().iter() {
179182
let mut hi_none_count = 0;
180183
let mut last_hi = None;
@@ -200,6 +203,8 @@ impl<const LEAF_FANOUT: usize> Db<LEAF_FANOUT> {
200203
hi_none_count += 1;
201204
}
202205
}
206+
// each tree should have exactly one leaf with no max hi key
207+
assert_eq!(hi_none_count, 1);
203208
}
204209

205210
log::debug!(

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@
130130
//! ).unwrap();
131131
//! # let _ = std::fs::remove_dir_all("my_db");
132132
//! ```
133+
#[cfg(feature = "for-internal-testing-only")]
134+
mod block_checker;
133135
mod config;
134136
mod db;
135137
mod flush_epoch;

src/object_cache.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ impl<const LEAF_FANOUT: usize> ObjectCache<LEAF_FANOUT> {
402402
) -> io::Result<()> {
403403
let mut ca = self.cache_advisor.borrow_mut();
404404
let to_evict = ca.accessed_reuse_buffer(*object_id, size);
405+
let mut not_found = 0;
405406
for (node_to_evict, _rough_size) in to_evict {
406407
let object_id =
407408
if let Some(object_id) = ObjectId::new(*node_to_evict) {
@@ -412,18 +413,11 @@ impl<const LEAF_FANOUT: usize> ObjectCache<LEAF_FANOUT> {
412413

413414
let node = if let Some(n) = self.object_id_index.get(&object_id) {
414415
if *n.object_id != *node_to_evict {
415-
log::debug!(
416-
"during cache eviction, node to evict did not match current occupant for {:?}",
417-
node_to_evict
418-
);
419416
continue;
420417
}
421418
n
422419
} else {
423-
log::debug!(
424-
"during cache eviction, unable to find node to evict for {:?}",
425-
node_to_evict
426-
);
420+
not_found += 1;
427421
continue;
428422
};
429423

@@ -456,6 +450,13 @@ impl<const LEAF_FANOUT: usize> ObjectCache<LEAF_FANOUT> {
456450
}
457451
}
458452

453+
if not_found > 0 {
454+
log::trace!(
455+
"during cache eviction, did not find {} nodes that we were trying to evict",
456+
not_found
457+
);
458+
}
459+
459460
Ok(())
460461
}
461462

@@ -701,9 +702,14 @@ impl<const LEAF_FANOUT: usize> ObjectCache<LEAF_FANOUT> {
701702
self.event_verifier.print_debug_history_for_object(
702703
dirty_object_id,
703704
);
704-
705705
unreachable!(
706-
"a leaf was expected to be cooperatively serialized but it was not available"
706+
"a leaf was expected to be cooperatively serialized but it was not available. \
707+
violation of flush responsibility for second read \
708+
of expected cooperative serialization. leaf in question's \
709+
dirty_flush_epoch is {:?}, our expected key was {:?}. node.deleted: {:?}",
710+
leaf_ref.dirty_flush_epoch,
711+
(dirty_epoch, dirty_object_id),
712+
leaf_ref.deleted,
707713
);
708714
}
709715
};
@@ -736,16 +742,16 @@ impl<const LEAF_FANOUT: usize> ObjectCache<LEAF_FANOUT> {
736742
let before_compute_defrag = Instant::now();
737743

738744
if cfg!(not(feature = "monotonic-behavior")) {
745+
let mut object_not_found = 0;
746+
739747
for fragmented_object_id in objects_to_defrag {
740748
let object_opt =
741749
self.object_id_index.get(&fragmented_object_id);
742750

743751
let object = if let Some(object) = object_opt {
744752
object
745753
} else {
746-
log::debug!(
747-
"defragmenting object not found in object_id_index: {fragmented_object_id:?}"
748-
);
754+
object_not_found += 1;
749755
continue;
750756
};
751757

@@ -781,6 +787,13 @@ impl<const LEAF_FANOUT: usize> ObjectCache<LEAF_FANOUT> {
781787
data,
782788
});
783789
}
790+
791+
if object_not_found > 0 {
792+
log::debug!(
793+
"{} objects not found while defragmenting",
794+
object_not_found
795+
);
796+
}
784797
}
785798

786799
let compute_defrag_latency = before_compute_defrag.elapsed();

0 commit comments

Comments
 (0)