From 9961e1577fb7a108fac3ba8400a6ff666204e256 Mon Sep 17 00:00:00 2001 From: Adewumi Sunkanmi Date: Tue, 6 Aug 2024 23:38:59 +0100 Subject: [PATCH] [Feat] Block Index Writer --- README.md | 7 +- bench.png | Bin 0 -> 41779 bytes doc_logo.png | Bin 11260 -> 7434 bytes src/lib.rs | 11 ++- v2/src/sst/block_index/mod.rs | 1 + v2/src/sst/block_index/writer.rs | 158 +++++++++++++++++++++++++++++++ 6 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 bench.png create mode 100644 v2/src/sst/block_index/writer.rs diff --git a/README.md b/README.md index b1965fb..6a405ce 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,14 @@ According to the benchmarks presented in the WiscKey paper, implementations can - **2.5x to 111x** for database loading - **1.6x to 14x** for random lookups +## Addressing major concerns +- **Range Query**: Since keys are separate from values, won't that affect range queries performance. Well, we now how have internal parallelism in SSDs, as we fetch the keys from the LSM tree we can fetch the values in parallel from the vlog file. This [benchmark](https://github.com/Gifted-s/velarixdb/blob/main/bench.png) from the Wisckey Paper shows how for request size ≥ 64KB, the aggregate throughput of random reads with 32 threads matches the sequential read throughput. +- **More Disk IO for Reads**: Since keys are now seperate from values, we have to make extra disk IO to fetch values? Yes, but since the key density now increases for each level (since we are only storing keys and value offsets in the sstable), we will most likely search fewer levels compared to LevelDB or RocksDB for thesame query. A significant portion of the LSM tree can also be cached in memory. + ## Designed for asynchronous runtime (unstable) Based on the introduction and efficiency of async IO at the OS kernel level e.g **io_uring** for the Linux kernel, VelarixDB is designed for asynchronous runtime. In this case Tokio runtime. Tokio allows for efficient and scalable asynchronous operations, making the most of modern multi-core processors. Frankly, most OS File System does not provide async API currently but Tokio uses a thread pool to offload blocking file system operations. -This means that even though the file system operations themselves are blocking at the OS level, Tokio can handle them without blocking the main async task executor. -Tokio might adopt [io_uring](https://docs.rs/tokio/latest/tokio/fs/index.html#:~:text=Currently%2C%20Tokio%20will%20always%20use%20spawn_blocking%20on%20all%20platforms%2C%20but%20it%20may%20be%20changed%20to%20use%20asynchronous%20file%20system%20APIs%20such%20as%20io_uring%20in%20the%20future.) in the future +This means that even though the file system operations themselves are blocking at the OS level, Tokio can handle them without blocking the main async task executor. Tokio might adopt [io_uring](https://docs.rs/tokio/latest/tokio/fs/index.html#:~:text=Currently%2C%20Tokio%20will%20always%20use%20spawn_blocking%20on%20all%20platforms%2C%20but%20it%20may%20be%20changed%20to%20use%20asynchronous%20file%20system%20APIs%20such%20as%20io_uring%20in%20the%20future.) in the future. (We haven't benchmarked the async version therefore this is unstable and might be removed in future versions) ## Disclaimer diff --git a/bench.png b/bench.png new file mode 100644 index 0000000000000000000000000000000000000000..22d883391a5cf56437bfbcb90500c504f79a7288 GIT binary patch literal 41779 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 literal 0 HcmV?d00001 diff --git a/doc_logo.png b/doc_logo.png index 253ed048b6e03a8ce35014e5ad869f805518cab2..55ffd84a87957f07e5acea1eb932685a3eda2094 100644 GIT binary patch literal 7434 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 literal 11260 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, W: Write>(src_path: P, sink: &mut W) -> crate::Result<()> { + let reader = File::open(src_path)?; + let mut reader = BufReader::new(reader); + std::io::copy(&mut reader, sink)?; + sink.flush()?; + + Ok(()) +} + +pub struct Writer { + pub index_block_tmp_file_path: PathBuf, + file_pos: u64, + + prev_pos: (u64, u64), + block_writer: Option>, + block_size: u32, + block_counter: u32, + block_handles: Vec, + tli_pointers: Vec, +} + +impl Writer { + pub fn new>(segment_id: SegmentId, folder: P, block_size: u32) -> crate::Result { + let index_block_tmp_file_path = folder.as_ref().join(format!("tmp_ib{segment_id}")); + + let writer = File::create(&index_block_tmp_file_path)?; + let block_writer = BufWriter::with_capacity(u16::MAX.into(), writer); + + Ok(Self { + index_block_tmp_file_path, + file_pos: 0, + prev_pos: (0, 0), + block_writer: Some(block_writer), + block_counter: 0, + block_size, + block_handles: Vec::with_capacity(1_000), + tli_pointers: Vec::with_capacity(1_000), + }) + } + + pub fn write_block(&mut self) -> crate::Result<()> { + let mut block_writer = self.block_writer.as_mut().expect("should exist"); + let (header, data) = IndexBlock::to_bytes(&self.block_handles, self.prev_pos.0)?; + header.serialize(&mut block_writer)?; + + block_writer.write_all(&data)?; + + let bytes_written = (BlockHeader::serialized_len() + data.len()) as u64; + + let last = self.block_handles.last().expect("Chunk should not be empty"); + + let index_block_handle = KeyedBlockHandle { + end_key: last.end_key.clone(), + offset: self.file_pos, + }; + + self.tli_pointers.push(index_block_handle); + + self.block_counter = 0; + self.file_pos += bytes_written; + + self.prev_pos.0 = self.prev_pos.1; + self.prev_pos.1 += bytes_written; + + self.block_handles.clear(); + + Ok(()) + } + + pub fn register_block(&mut self, start_key: UserKey, offset: u64) -> crate::Result<()> { + let block_handle_size = (start_key.len() + std::mem::size_of::()) as u32; + + let block_handle = KeyedBlockHandle { + end_key: start_key, + offset, + }; + + self.block_handles.push(block_handle); + + self.block_counter += block_handle_size; + if self.block_counter >= self.block_size { + self.write_block()?; + } + Ok(()) + } + + fn write_top_level_index( + &mut self, + block_file_writer: &mut BufWriter, + file_offset: u64, + ) -> crate::Result { + // we need to drop the writer so the file is closed + // so it can be replaced when using Windows + self.block_writer = None; + + pipe_file_into_writer(&self.index_block_tmp_file_path, block_file_writer)?; + + let tli_ptr = block_file_writer.stream_position()?; + + // since we piped index block to block_file_writer and + // file_offset is the last offset of block_file_writer + // before the pipe, all item offset in top level index should + // be shifted by offset + for item in &mut self.tli_pointers { + item.offset += file_offset; + } + + let (header, data) = IndexBlock::to_bytes(&self.tli_pointers, 0)?; + header.serialize(block_file_writer)?; + block_file_writer.write_all(&data)?; + + let bytes_written = BlockHeader::serialized_len() + data.len(); + + block_file_writer.flush()?; + block_file_writer.get_mut().sync_all()?; + + log::trace!( + "Written top level index, with {} pointers ({} bytes)", + self.tli_pointers.len(), + bytes_written, + ); + Ok(tli_ptr) + } + + /// Returns the offset in the file to TLI + pub fn finish(&mut self, block_file_writer: &mut BufWriter) -> crate::Result { + if self.block_counter > 0 { + self.write_block()?; + } + { + let block_writer = self.block_writer.as_mut().expect("should exist"); + block_writer.flush()?; + block_writer.get_mut().sync_all()?; + } + + let index_block_ptr = block_file_writer.stream_position()?; + let tli_ptr = self.write_top_level_index(block_file_writer, index_block_ptr)?; + + std::fs::remove_file(&self.index_block_tmp_file_path)?; + + Ok(tli_ptr) + } +}