From 5ebc81e76e003509f548fb1f013d616dbfabafec Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 5 Jan 2024 12:03:34 +0100 Subject: [PATCH 1/4] Lock memory map when reading from file --- admin/src/bench/mod.rs | 2 +- src/btree/mod.rs | 2 +- src/file.rs | 41 ++++++++++++++++++++++++++++++++--------- src/log.rs | 27 +-------------------------- src/table.rs | 25 +++++++++++++++++++------ 5 files changed, 54 insertions(+), 43 deletions(-) diff --git a/admin/src/bench/mod.rs b/admin/src/bench/mod.rs index 373242d7..8e91aaa2 100644 --- a/admin/src/bench/mod.rs +++ b/admin/src/bench/mod.rs @@ -5,7 +5,7 @@ use super::*; mod sizes; -pub use parity_db::{CompressionType, Db, Key, Value}; +pub use parity_db::{Db, Key}; use rand::{RngCore, SeedableRng}; use std::{ diff --git a/src/btree/mod.rs b/src/btree/mod.rs index e3c456d8..6393a87f 100644 --- a/src/btree/mod.rs +++ b/src/btree/mod.rs @@ -16,7 +16,7 @@ use crate::{ }, Operation, }; -pub use iter::{BTreeIterator, LastIndex, LastKey}; +pub use iter::{BTreeIterator, LastKey}; use node::SeparatorInner; #[allow(clippy::module_inception)] diff --git a/src/file.rs b/src/file.rs index 9136a2c9..bba9af8f 100644 --- a/src/file.rs +++ b/src/file.rs @@ -8,6 +8,7 @@ use crate::{ parking_lot::RwLock, table::TableId, }; +use parking_lot::RwLockReadGuard; use std::sync::atomic::{AtomicU64, Ordering}; #[cfg(target_os = "linux")] @@ -124,15 +125,13 @@ impl TableFile { Ok(()) } - pub fn slice_at(&self, offset: u64, len: usize) -> &[u8] { + pub fn slice_at(&self, offset: u64, len: usize) -> MappedBytesGuard { let offset = offset as usize; let map = self.map.read(); - let (map, _) = map.as_ref().unwrap(); - let data: &[u8] = unsafe { - let ptr = map.as_ptr().add(offset); - std::slice::from_raw_parts(ptr, len) - }; - data + RwLockReadGuard::map(map, |map| { + let (map, _) = map.as_ref().unwrap(); + &map[offset..offset + len] + }) } pub fn write_at(&self, buf: &[u8], offset: u64) -> Result<()> { @@ -169,8 +168,6 @@ impl TableFile { let new_map = mmap(&file, new_len as usize)?; let old_map = std::mem::replace(map, new_map); try_io!(old_map.flush()); - // Leak the old mapping as there might be concurrent readers. - std::mem::forget(old_map); } new_len }, @@ -197,3 +194,29 @@ impl TableFile { Ok(()) } } + +// Loom is missing support for guard projection, so we copy the data as a workaround. +#[cfg(feature = "loom")] +pub struct MappedBytesGuard<'a> { + _phantom: std::marker::PhantomData<&'a ()>, + data: Vec, +} + +#[cfg(feature = "loom")] +impl<'a> MappedBytesGuard<'a> { + fn new(data: Vec) -> Self { + Self { _phantom: std::marker::PhantomData, data } + } +} + +#[cfg(feature = "loom")] +impl<'a> std::ops::Deref for MappedBytesGuard<'a> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.data.as_slice() + } +} + +#[cfg(not(feature = "loom"))] +pub type MappedBytesGuard<'a> = parking_lot::MappedRwLockReadGuard<'a, [u8]>; diff --git a/src/log.rs b/src/log.rs index 71bf7784..07e59cb2 100644 --- a/src/log.rs +++ b/src/log.rs @@ -4,6 +4,7 @@ use crate::{ column::ColId, error::{try_io, Error, Result}, + file::MappedBytesGuard, index::{Chunk as IndexChunk, TableId as IndexTableId, ENTRY_BYTES}, options::Options, parking_lot::{RwLock, RwLockWriteGuard}, @@ -92,32 +93,6 @@ impl LogOverlays { } } -// Loom is missing support for guard projection, so we copy the data as a workaround. -#[cfg(feature = "loom")] -pub struct MappedBytesGuard<'a> { - _phantom: std::marker::PhantomData<&'a ()>, - data: Vec, -} - -#[cfg(feature = "loom")] -impl<'a> MappedBytesGuard<'a> { - fn new(data: Vec) -> Self { - Self { _phantom: std::marker::PhantomData, data } - } -} - -#[cfg(feature = "loom")] -impl<'a> std::ops::Deref for MappedBytesGuard<'a> { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - self.data.as_slice() - } -} - -#[cfg(not(feature = "loom"))] -type MappedBytesGuard<'a> = parking_lot::MappedRwLockReadGuard<'a, [u8]>; - impl LogQuery for RwLock { type ValueRef<'a> = MappedBytesGuard<'a>; diff --git a/src/table.rs b/src/table.rs index e16d4394..7d7e7433 100644 --- a/src/table.rs +++ b/src/table.rs @@ -382,6 +382,20 @@ impl + AsMut<[u8]>> std::ops::IndexMut> fo } } +enum LockedSlice<'a> { + FromOverlay(&'a [u8]), + FromFile(parking_lot::MappedRwLockReadGuard<'a, [u8]>), +} + +impl<'a> LockedSlice<'a> { + fn as_slice(&self) -> &[u8] { + match self { + LockedSlice::FromOverlay(slice) => &*slice, + LockedSlice::FromFile(slice) => &*slice, + } + } +} + impl ValueTable { pub fn open( path: Arc, @@ -458,14 +472,14 @@ impl ValueTable { let entry_size = self.entry_size as usize; loop { let vbuf = log.value_ref(self.id, index); - let buf: &[u8] = if let Some(buf) = vbuf.as_deref() { + let buf: LockedSlice = if let Some(buf) = vbuf.as_deref() { log::trace!( target: "parity-db", "{}: Found in overlay {}", self.id, index, ); - buf + LockedSlice::FromOverlay(buf) } else { log::trace!( target: "parity-db", @@ -473,11 +487,10 @@ impl ValueTable { self.id, index, ); - self.file.slice_at(index * self.entry_size as u64, entry_size) + let buf = self.file.slice_at(index * self.entry_size as u64, entry_size); + LockedSlice::FromFile(buf) }; - let mut buf = EntryRef::new(buf); - - buf.set_offset(0); + let mut buf = EntryRef::new(buf.as_slice()); if buf.is_tombstone() { return Ok((0, false)) From d8c9bffe5b4d643db84fd23219c00cc93e8a9e52 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 5 Jan 2024 12:30:30 +0100 Subject: [PATCH 2/4] Fix loom --- src/file.rs | 12 ++++++++++-- src/table.rs | 14 +++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/file.rs b/src/file.rs index bba9af8f..6bb7a18d 100644 --- a/src/file.rs +++ b/src/file.rs @@ -8,7 +8,6 @@ use crate::{ parking_lot::RwLock, table::TableId, }; -use parking_lot::RwLockReadGuard; use std::sync::atomic::{AtomicU64, Ordering}; #[cfg(target_os = "linux")] @@ -125,6 +124,7 @@ impl TableFile { Ok(()) } + #[cfg(not(feature = "loom"))] pub fn slice_at(&self, offset: u64, len: usize) -> MappedBytesGuard { let offset = offset as usize; let map = self.map.read(); @@ -134,6 +134,14 @@ impl TableFile { }) } + #[cfg(feature = "loom")] + pub fn slice_at(&self, offset: u64, len: usize) -> MappedBytesGuard { + let offset = offset as usize; + let map = self.map.read(); + let (map, _) = map.as_ref().unwrap(); + MappedBytesGuard::new(map[offset..offset + len].to_vec()) + } + pub fn write_at(&self, buf: &[u8], offset: u64) -> Result<()> { let map = self.map.read(); let (map, _) = map.as_ref().unwrap(); @@ -204,7 +212,7 @@ pub struct MappedBytesGuard<'a> { #[cfg(feature = "loom")] impl<'a> MappedBytesGuard<'a> { - fn new(data: Vec) -> Self { + pub fn new(data: Vec) -> Self { Self { _phantom: std::marker::PhantomData, data } } } diff --git a/src/table.rs b/src/table.rs index 7d7e7433..dae909de 100644 --- a/src/table.rs +++ b/src/table.rs @@ -382,12 +382,12 @@ impl + AsMut<[u8]>> std::ops::IndexMut> fo } } -enum LockedSlice<'a> { - FromOverlay(&'a [u8]), - FromFile(parking_lot::MappedRwLockReadGuard<'a, [u8]>), +enum LockedSlice, F: std::ops::Deref> { + FromOverlay(O), + FromFile(F), } -impl<'a> LockedSlice<'a> { +impl, F: std::ops::Deref> LockedSlice { fn as_slice(&self) -> &[u8] { match self { LockedSlice::FromOverlay(slice) => &*slice, @@ -472,7 +472,7 @@ impl ValueTable { let entry_size = self.entry_size as usize; loop { let vbuf = log.value_ref(self.id, index); - let buf: LockedSlice = if let Some(buf) = vbuf.as_deref() { + let buf: LockedSlice<_, _> = if let Some(buf) = vbuf.as_deref() { log::trace!( target: "parity-db", "{}: Found in overlay {}", @@ -487,8 +487,8 @@ impl ValueTable { self.id, index, ); - let buf = self.file.slice_at(index * self.entry_size as u64, entry_size); - LockedSlice::FromFile(buf) + let vbuf = self.file.slice_at(index * self.entry_size as u64, entry_size); + LockedSlice::FromFile(vbuf) }; let mut buf = EntryRef::new(buf.as_slice()); From b96c8f0b0393941102ac012663de607e6988e41f Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 5 Jan 2024 12:32:09 +0100 Subject: [PATCH 3/4] Fix loom --- src/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/file.rs b/src/file.rs index 6bb7a18d..30523a37 100644 --- a/src/file.rs +++ b/src/file.rs @@ -128,7 +128,7 @@ impl TableFile { pub fn slice_at(&self, offset: u64, len: usize) -> MappedBytesGuard { let offset = offset as usize; let map = self.map.read(); - RwLockReadGuard::map(map, |map| { + parking_lot::RwLockReadGuard::map(map, |map| { let (map, _) = map.as_ref().unwrap(); &map[offset..offset + len] }) From 143aaf5898ccd135d78bb649e802937b5e9b52a6 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 5 Jan 2024 12:54:02 +0000 Subject: [PATCH 4/4] Bump version --- CHANGELOG.md | 5 +++++ Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8635a7ef..763352ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog]. ## [Unreleased] +## [v0.4.13] - 2024-01-05 + +- Lock memory map when reading from file [`#234`](https://github.com/paritytech/parity-db/pull/234) +- Disable read-ahead on Windows [`#235`](https://github.com/paritytech/parity-db/pull/235) + ## [v0.4.12] - 2023-10-12 - CI for windows and macos. Also fixes access denied error on windows [`#222`](https://github.com/paritytech/parity-db/pull/222) diff --git a/Cargo.toml b/Cargo.toml index 9c38189a..30e7079d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parity-db" -version = "0.4.12" +version = "0.4.13" authors = ["Parity Technologies "] edition = "2021" license = "MIT OR Apache-2.0"