aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml2
-rw-r--r--benches/benchmark.rs4
-rw-r--r--src/common.rs11
-rw-r--r--src/lib.rs2
-rw-r--r--src/reverse_log_reader.rs35
-rw-r--r--tests/integration.rs33
-rw-r--r--tests/resources/test_db2bin39 -> 46243 bytes
-rw-r--r--tests/resources/test_db3bin0 -> 46243 bytes
8 files changed, 66 insertions, 21 deletions
diff --git a/Cargo.toml b/Cargo.toml
index a16a243..ca77753 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,9 +15,9 @@ priority-queue = "2.1.1"
ctor = "0.2.8"
env_logger = "0.11.5"
serial_test = "3.1.1"
-tempfile = "3.13.0"
criterion = { version = "0.5", features = ["html_reports"] }
rand = "0.8.5"
+tempfile = "3.13.0"
[[bench]]
name = "benchmark"
diff --git a/benches/benchmark.rs b/benches/benchmark.rs
index 0328ac5..b9ecbb3 100644
--- a/benches/benchmark.rs
+++ b/benches/benchmark.rs
@@ -34,8 +34,8 @@ pub fn upsert_benchmark(c: &mut Criterion) {
prefill_db_with_n_records(&mut db, size).expect("Failed to prefill DB");
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &_size| {
- let record = random_record();
b.iter(|| {
+ let record = random_record();
let _ = db.upsert(black_box(&record));
});
});
@@ -64,8 +64,8 @@ pub fn get_benchmark(c: &mut Criterion) {
prefill_db_with_n_records(&mut db, size).expect("Failed to prefill DB");
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &_size| {
- let id = random_int();
b.iter(|| {
+ let id = random_int();
let _ = db.get(black_box(&RecordValue::Int(id)));
});
});
diff --git a/src/common.rs b/src/common.rs
index 33e99e7..3d31263 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -72,15 +72,16 @@ pub enum MemtableEvictPolicy {
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum WriteDurability {
/// Changes are written to an application-level write buffer without flushing to the OS write buffer or syncing to disk.
+ /// The buffered writer will batch writes to the OS buffer for maximum performance.
/// Offers the lowest durability guarantees but is very fast.
- AsyncWrite,
- /// Changes are written to the OS write buffer but not synced to disk.
- /// Offers better durability guarantees than AsyncWrite but is slower.
+ Async,
+ /// Changes are written to the OS write buffer but not immediately synced to disk.
+ /// Offers better durability guarantees than Async but is slower.
/// This is generally recommended. Most OSes will sync the write buffer to disk within a few seconds.
Flush,
- /// Changes are written to the OS write buffer and synced to disk.
+ /// Changes are written to the OS write buffer and synced to disk immediately.
/// Offers the best durability guarantees but is the slowest.
- SyncWrite,
+ FlushSync,
}
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
diff --git a/src/lib.rs b/src/lib.rs
index 5f98532..0ce40bb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -322,7 +322,7 @@ impl<Field: Eq + Clone + Debug> DB<Field> {
if self.config.write_durability == WriteDurability::Flush {
self.log_file.flush()?;
}
- if self.config.write_durability == WriteDurability::SyncWrite {
+ if self.config.write_durability == WriteDurability::FlushSync {
self.log_file.flush()?;
self.log_file.sync_all()?;
}
diff --git a/src/reverse_log_reader.rs b/src/reverse_log_reader.rs
index d8021ba..8a2126f 100644
--- a/src/reverse_log_reader.rs
+++ b/src/reverse_log_reader.rs
@@ -93,6 +93,7 @@ impl<'a> ReverseLogReader<'a> {
/// Read exactly `buf.len()` bytes from the file, return an error if the file is exhausted.
/// The bytes are returned in start -> end order.
+ /// If an error is returned, the contents of `buf` are in an undefined state.
fn read_exact(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
let mut read = 0;
while read < buf.len() {
@@ -105,13 +106,15 @@ impl<'a> ReverseLogReader<'a> {
));
}
}
- let bytes_to_read = std::cmp::min(buf.len() - read, self.internal_pos);
- buf[read..read + bytes_to_read].copy_from_slice(
- &self.internal_buf[self.internal_pos - bytes_to_read..self.internal_pos],
- );
- self.internal_pos -= bytes_to_read;
- read += bytes_to_read;
+
+ let end = buf.len() - read;
+ let n = std::cmp::min(self.internal_pos, end);
+ buf[end - n..end]
+ .copy_from_slice(&self.internal_buf[self.internal_pos - n..self.internal_pos]);
+ read += n;
+ self.internal_pos -= n;
}
+
Ok(read)
}
@@ -169,10 +172,17 @@ impl<'a> ReverseLogReader<'a> {
match validate_special(&special_buf.as_slice()) {
Some(special) => Ok(special),
- None => Err(io::Error::new(
- io::ErrorKind::InvalidData,
- format!("Not a special sequence: {:?}", special_buf),
- )),
+ None => {
+ let pos = self.file.stream_position().unwrap() + self.internal_pos as u64;
+
+ Err(io::Error::new(
+ io::ErrorKind::InvalidData,
+ format!(
+ "Not a special sequence: {:?} at pos: {:x}",
+ special_buf, pos,
+ ),
+ ))
+ }
}
}
}
@@ -320,7 +330,6 @@ mod reverse_reader_tests {
let mut reader = ReverseLogReader::new(&mut file).unwrap();
let mut buf = vec![0; 10];
assert!(reader.read_exact(&mut buf).unwrap_err().kind() == io::ErrorKind::UnexpectedEof);
- assert_eq!(String::from_utf8(buf[..5].to_vec()).unwrap(), "hello");
}
}
@@ -331,7 +340,9 @@ impl Iterator for ReverseLogReader<'_> {
match self.read_record() {
Ok(Some(record)) => Some(record),
Ok(None) => None,
- Err(err) => panic!("Error reading record: {:?}", err),
+ Err(err) => {
+ panic!("Error reading record: {:?}", err,)
+ }
}
}
}
diff --git a/tests/integration.rs b/tests/integration.rs
index 2bebc6d..fc1d0cc 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -299,6 +299,39 @@ fn test_initialize_and_read_from_primary_memtable_fixture_db2() {
}
#[test]
+fn test_initialize_without_memtables_fixture_db3() {
+ let data_dir = tmp_dir();
+ // Copy the fixture DB to the test data directory
+ fs::create_dir_all(&data_dir).expect("Failed to create the test data directory");
+ fs::copy(
+ &Path::new(TEST_RESOURCES_DIR).join("test_db3"),
+ &Path::new(&data_dir).join("db"),
+ )
+ .expect("Failed to copy the fixture DB");
+
+ let mut db = DB::configure()
+ .data_dir(&data_dir)
+ .fields(&vec![
+ (Field::Id, RecordFieldType::Int),
+ (Field::Name, RecordFieldType::String),
+ (Field::Data, RecordFieldType::Bytes),
+ ])
+ .memtable_capacity(0)
+ .primary_key(Field::Id)
+ .initialize()
+ .expect("Failed to initialize DB instance");
+
+ let result = db.get(&RecordValue::Int(1)).unwrap().unwrap();
+
+ // Check that the IDs match
+ let expected = RecordValue::Int(1);
+ assert!(match (&result.values[0], &expected) {
+ (RecordValue::Int(a), RecordValue::Int(b)) => a == b,
+ _ => false,
+ });
+}
+
+#[test]
fn test_multiple_writing_threads() {
let data_dir = tmp_dir();
let mut threads = vec![];
diff --git a/tests/resources/test_db2 b/tests/resources/test_db2
index 2448640..156659f 100644
--- a/tests/resources/test_db2
+++ b/tests/resources/test_db2
Binary files differ
diff --git a/tests/resources/test_db3 b/tests/resources/test_db3
new file mode 100644
index 0000000..156659f
--- /dev/null
+++ b/tests/resources/test_db3
Binary files differ