mirror of https://github.com/spacejam/sled
[new file format] use crc on frame lengths in metadata store
This commit is contained in:
parent
795a2217df
commit
cb14155b89
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "sled"
|
||||
version = "1.0.0-alpha.120"
|
||||
version = "1.0.0-alpha.121"
|
||||
edition = "2021"
|
||||
authors = ["Tyler Neely <tylerneely@gmail.com>"]
|
||||
documentation = "https://docs.rs/sled/"
|
||||
|
|
38
src/heap.rs
38
src/heap.rs
|
@ -409,7 +409,7 @@ pub(crate) fn recover<P: AsRef<Path>>(
|
|||
slabs.push(Slab {
|
||||
slot_size,
|
||||
file,
|
||||
max_allocated_slot_since_last_truncation: AtomicU64::new(0),
|
||||
max_live_slot_since_last_truncation: AtomicU64::new(0),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -586,7 +586,7 @@ mod sys_io {
|
|||
struct Slab {
|
||||
file: fs::File,
|
||||
slot_size: usize,
|
||||
max_allocated_slot_since_last_truncation: AtomicU64,
|
||||
max_live_slot_since_last_truncation: AtomicU64,
|
||||
}
|
||||
|
||||
impl Slab {
|
||||
|
@ -990,40 +990,44 @@ impl Heap {
|
|||
let before_truncate = Instant::now();
|
||||
let mut truncated_files = 0;
|
||||
let mut truncated_bytes = 0;
|
||||
for (i, current_allocated) in self.table.get_max_allocated_per_slab() {
|
||||
for (i, max_live_slot) in self.table.get_max_allocated_per_slab() {
|
||||
let slab = &self.slabs[i];
|
||||
|
||||
let last_max = slab
|
||||
.max_allocated_slot_since_last_truncation
|
||||
.fetch_max(current_allocated, Ordering::SeqCst);
|
||||
.max_live_slot_since_last_truncation
|
||||
.fetch_max(max_live_slot, Ordering::SeqCst);
|
||||
|
||||
let max_since_last_truncation = last_max.max(current_allocated);
|
||||
let max_since_last_truncation = last_max.max(max_live_slot);
|
||||
|
||||
let currently_occupied =
|
||||
(current_allocated + 1) * slab.slot_size as u64;
|
||||
let currently_occupied_bytes =
|
||||
(max_live_slot + 1) * slab.slot_size as u64;
|
||||
|
||||
let max_occupied =
|
||||
let max_occupied_bytes =
|
||||
(max_since_last_truncation + 1) * slab.slot_size as u64;
|
||||
|
||||
let ratio = currently_occupied * 100 / max_occupied;
|
||||
let ratio = currently_occupied_bytes * 100 / max_occupied_bytes;
|
||||
|
||||
if ratio < FILE_TARGET_FILL_RATIO {
|
||||
let target_len = currently_occupied * FILE_RESIZE_MARGIN / 100;
|
||||
let target_len = if max_live_slot < 16 {
|
||||
currently_occupied_bytes
|
||||
} else {
|
||||
currently_occupied_bytes * FILE_RESIZE_MARGIN / 100
|
||||
};
|
||||
|
||||
assert!(target_len < max_occupied);
|
||||
assert!(target_len < max_occupied_bytes);
|
||||
assert!(
|
||||
target_len > currently_occupied,
|
||||
target_len >= currently_occupied_bytes,
|
||||
"target_len of {} is above actual occupied len of {}",
|
||||
target_len,
|
||||
currently_occupied
|
||||
currently_occupied_bytes
|
||||
);
|
||||
|
||||
if slab.file.set_len(target_len).is_ok() {
|
||||
slab.max_allocated_slot_since_last_truncation
|
||||
.store(current_allocated, Ordering::SeqCst);
|
||||
slab.max_live_slot_since_last_truncation
|
||||
.store(max_live_slot, Ordering::SeqCst);
|
||||
|
||||
let file_truncated_bytes =
|
||||
currently_occupied.saturating_sub(target_len);
|
||||
currently_occupied_bytes.saturating_sub(target_len);
|
||||
self.truncated_file_bytes
|
||||
.fetch_add(file_truncated_bytes, Ordering::Release);
|
||||
|
||||
|
|
|
@ -481,14 +481,20 @@ fn read_frame(
|
|||
file: &mut fs::File,
|
||||
reusable_frame_buffer: &mut Vec<u8>,
|
||||
) -> io::Result<Vec<UpdateMetadata>> {
|
||||
let mut frame_size_buf: [u8; 8] = [0; 8];
|
||||
let mut frame_size_with_crc_buf: [u8; 8] = [0; 8];
|
||||
// TODO only break if UnexpectedEof, otherwise propagate
|
||||
fallible!(file.read_exact(&mut frame_size_buf));
|
||||
fallible!(file.read_exact(&mut frame_size_with_crc_buf));
|
||||
|
||||
let expected_len_hash_buf = [frame_size_buf[6], frame_size_buf[7]];
|
||||
let expected_len_hash_buf =
|
||||
[frame_size_with_crc_buf[6], frame_size_with_crc_buf[7]];
|
||||
|
||||
let actual_len_hash_buf: [u8; 2] =
|
||||
(crc32fast::hash(&frame_size_buf[..6]) as u16).to_le_bytes();
|
||||
(crc32fast::hash(&frame_size_with_crc_buf[..6]) as u16).to_le_bytes();
|
||||
|
||||
// clear crc bytes before turning into usize
|
||||
let mut frame_size_buf = frame_size_with_crc_buf;
|
||||
frame_size_buf[6] = 0;
|
||||
frame_size_buf[7] = 0;
|
||||
|
||||
if actual_len_hash_buf != expected_len_hash_buf {
|
||||
return Err(annotate!(io::Error::new(
|
||||
|
@ -498,7 +504,6 @@ fn read_frame(
|
|||
}
|
||||
|
||||
let len_u64: u64 = u64::from_le_bytes(frame_size_buf);
|
||||
// TODO make sure len < max len
|
||||
let len: usize = usize::try_from(len_u64).unwrap();
|
||||
|
||||
reusable_frame_buffer.clear();
|
||||
|
@ -506,7 +511,7 @@ fn read_frame(
|
|||
unsafe {
|
||||
reusable_frame_buffer.set_len(len + 12);
|
||||
}
|
||||
reusable_frame_buffer[..8].copy_from_slice(&frame_size_buf);
|
||||
reusable_frame_buffer[..8].copy_from_slice(&frame_size_with_crc_buf);
|
||||
|
||||
fallible!(file.read_exact(&mut reusable_frame_buffer[8..]));
|
||||
|
||||
|
|
|
@ -1403,6 +1403,8 @@ impl<const LEAF_FANOUT: usize> Tree<LEAF_FANOUT> {
|
|||
leaf.insert(key, value);
|
||||
merges.remove(lo);
|
||||
|
||||
merges.remove(&leaf.lo);
|
||||
|
||||
if let Some((split_key, rhs_node)) = leaf.split_if_full(
|
||||
new_epoch,
|
||||
&self.cache,
|
||||
|
@ -1418,7 +1420,8 @@ impl<const LEAF_FANOUT: usize> Tree<LEAF_FANOUT> {
|
|||
leaf.remove(&key);
|
||||
|
||||
if leaf.is_empty() {
|
||||
merges.insert(lo.clone(), object.clone());
|
||||
assert_eq!(leaf.lo, lo);
|
||||
merges.insert(leaf.lo.clone(), object.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,31 @@
|
|||
mod common;
|
||||
mod tree;
|
||||
|
||||
use std::alloc::{Layout, System};
|
||||
|
||||
use tree::{prop_tree_matches_btreemap, Key, Op::*};
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: ShredAllocator = ShredAllocator;
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
struct ShredAllocator;
|
||||
|
||||
unsafe impl std::alloc::GlobalAlloc for ShredAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
assert!(layout.size() < 1_000_000_000);
|
||||
let ret = System.alloc(layout);
|
||||
assert_ne!(ret, std::ptr::null_mut());
|
||||
std::ptr::write_bytes(ret, 0xa1, layout.size());
|
||||
ret
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
std::ptr::write_bytes(ptr, 0xde, layout.size());
|
||||
System.dealloc(ptr, layout)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
const INTENSITY: usize = 10;
|
||||
|
||||
|
|
|
@ -1238,9 +1238,13 @@ fn tree_gc() {
|
|||
"{stats:?}"
|
||||
);
|
||||
|
||||
// TODO test this after we implement file truncation
|
||||
// let expected_max_size = size_on_disk_after_inserts / 100;
|
||||
// assert!(size_on_disk_after_deletes <= expected_max_size);
|
||||
let expected_max_size = size_on_disk_after_inserts / 15;
|
||||
assert!(
|
||||
size_on_disk_after_deletes <= expected_max_size,
|
||||
"expected file truncation to take size under {expected_max_size} \
|
||||
but it was {size_on_disk_after_deletes}"
|
||||
);
|
||||
// TODO assert!(stats.cache.heap.truncated_file_bytes > 0);
|
||||
|
||||
println!(
|
||||
"after writing {N} items and removing them, disk size went \
|
||||
|
|
Loading…
Reference in New Issue