diff options
| author | Jan Tuomi <jan@jantuomi.fi> | 2025-01-06 19:28:41 +0200 |
|---|---|---|
| committer | Jan Tuomi <jan@jantuomi.fi> | 2025-01-06 19:34:51 +0200 |
| commit | a4b813881a47bb9d8aa528a9598cfdb052ae231e (patch) | |
| tree | f9a57b5049a69b6272c5da90a9abdb0d6ff17607 /log_db | |
| parent | 21d0b647d6916c4dbbde8a310a0760872d44f7a2 (diff) | |
Move primary_key and secondary_keys configuration to Recordable
Diffstat (limited to 'log_db')
| -rw-r--r-- | log_db/src/common.rs | 2 | ||||
| -rw-r--r-- | log_db/src/lib.rs | 62 | ||||
| -rw-r--r-- | log_db/src/record.rs | 9 | ||||
| -rw-r--r-- | log_db/tests/integration.rs | 34 |
4 files changed, 52 insertions, 55 deletions
diff --git a/log_db/src/common.rs b/log_db/src/common.rs index a3ff5c2..4f5751d 100644 --- a/log_db/src/common.rs +++ b/log_db/src/common.rs @@ -242,6 +242,7 @@ impl Display for WriteDurability { #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] pub enum IndexableValue { + Null, Int(i64), String(String), } @@ -393,6 +394,7 @@ impl Value { pub fn as_indexable(&self) -> Option<IndexableValue> { match self { + Value::Null => Some(IndexableValue::Null), Value::Int(i) => Some(IndexableValue::Int(*i)), Value::String(s) => Some(IndexableValue::String(s.clone())), _ => None, diff --git a/log_db/src/lib.rs b/log_db/src/lib.rs index 2cfcfbe..0709a8e 100644 --- a/log_db/src/lib.rs +++ b/log_db/src/lib.rs @@ -22,26 +22,25 @@ use std::collections::BTreeMap; use std::fmt::Debug; use std::fs::{self}; use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::marker::PhantomData; use std::path::{Path, PathBuf}; pub struct ConfigBuilder<R: Recordable> { data_dir: Option<String>, segment_size: Option<usize>, - primary_key: Option<R::Field>, - secondary_keys: Option<Vec<R::Field>>, write_durability: Option<WriteDurability>, read_consistency: Option<ReadConsistency>, + _marker: PhantomData<R>, } -impl<'a, R: Recordable> ConfigBuilder<R> { +impl<R: Recordable> ConfigBuilder<R> { pub fn new() -> ConfigBuilder<R> { - ConfigBuilder::<R> { + ConfigBuilder { data_dir: None, segment_size: None, - primary_key: None, - secondary_keys: None, write_durability: None, read_consistency: None, + _marker: PhantomData, } } @@ -60,21 +59,6 @@ impl<'a, R: Recordable> ConfigBuilder<R> { self } - /// The primary key of the database, used to construct - /// the primary memtable index. This should be the field - /// that is most frequently queried. - pub fn primary_key(&mut self, primary_key: R::Field) -> &mut Self { - self.primary_key = Some(primary_key); - self - } - - /// The secondary keys of the database, used to construct - /// the secondary memtable indexes. - pub fn secondary_keys(&mut self, secondary_keys: &[R::Field]) -> &mut Self { - self.secondary_keys = Some(secondary_keys.to_vec()); - self - } - /// The write durability policy for the database. /// This determines how writes are persisted to disk. /// The default is WriteDurability::Flush. @@ -93,15 +77,12 @@ impl<'a, R: Recordable> ConfigBuilder<R> { } pub fn initialize(&self) -> Result<DB<R>, DBError> { - let config = Config::<R> { + let config = Config { + fields: R::schema(), + primary_key: R::primary_key(), + secondary_keys: R::secondary_keys(), data_dir: self.data_dir.clone().unwrap_or("db_data".to_string()), segment_size: self.segment_size.unwrap_or(4 * 1024 * 1024), // 4MB - fields: R::schema(), - primary_key: self.primary_key.clone().ok_or(io::Error::new( - io::ErrorKind::InvalidInput, - "Required config value \"primary_key\" is not set", - ))?, - secondary_keys: self.secondary_keys.clone().unwrap_or(Vec::new()), write_durability: self .write_durability .clone() @@ -118,17 +99,18 @@ impl<'a, R: Recordable> ConfigBuilder<R> { #[derive(Clone)] struct Config<R: Recordable> { - pub data_dir: String, - pub segment_size: usize, pub fields: Vec<(R::Field, ValueType)>, pub primary_key: R::Field, pub secondary_keys: Vec<R::Field>, + pub data_dir: String, + pub segment_size: usize, pub write_durability: WriteDurability, pub read_consistency: ReadConsistency, } pub struct DB<R: Recordable> { config: Config<R>, + data_dir: PathBuf, active_metadata_file: fs::File, active_data_file: fs::File, @@ -879,6 +861,12 @@ mod tests { impl Recordable for TestInst1 { type Field = Field; + fn schema() -> Vec<(Field, ValueType)> { + vec![(Field::Id, ValueType::int())] + } + fn primary_key() -> Self::Field { + Field::Id + } fn into_record(self) -> Vec<Value> { vec![Value::Int(self.id)] @@ -893,10 +881,6 @@ mod tests { }, } } - - fn schema() -> Vec<(Field, ValueType)> { - vec![(Field::Id, ValueType::int())] - } } struct TestInst2 { @@ -906,6 +890,12 @@ mod tests { impl Recordable for TestInst2 { type Field = Field; + fn primary_key() -> Self::Field { + Field::Id + } + fn secondary_keys() -> Vec<Self::Field> { + vec![Field::Name] + } fn into_record(self) -> Vec<Value> { vec![Value::Int(self.id), Value::String(self.name)] @@ -944,7 +934,6 @@ mod tests { let mut db = DB::<TestInst1>::configure() .data_dir(data_dir.to_str().unwrap()) .segment_size(segment_size) - .primary_key(Field::Id) .initialize() .expect("Failed to create DB"); @@ -1028,7 +1017,6 @@ mod tests { let mut db = DB::<TestInst1>::configure() .data_dir(data_dir.to_str().unwrap()) - .primary_key(Field::Id) .initialize() .expect("Failed to create DB"); @@ -1080,8 +1068,6 @@ mod tests { let mut db = DB::<TestInst2>::configure() .data_dir(data_dir.to_str().unwrap()) - .primary_key(Field::Id) - .secondary_keys(&[Field::Name]) .initialize() .expect("Failed to create DB"); diff --git a/log_db/src/record.rs b/log_db/src/record.rs index 1b882ed..8d518f0 100644 --- a/log_db/src/record.rs +++ b/log_db/src/record.rs @@ -104,8 +104,15 @@ impl Record { pub trait Recordable { /// The field type of the data structure implementing the `Recordable` trait. type Field: Eq + Clone + Debug; - /// Define the schema of the data structure implementing the `Recordable` trait. + /// Define the schema of the instance implementing the `Recordable` trait. fn schema() -> Vec<(Self::Field, ValueType)>; + /// Define the primary key of the instance implementing the `Recordable` trait. + fn primary_key() -> Self::Field; + /// Define the secondary keys of the instance implementing the `Recordable` trait. + fn secondary_keys() -> Vec<Self::Field> { + Vec::new() + } + /// Convert the data structure implementing the `Recordable` trait into a vector of database values. fn into_record(self) -> Vec<Value>; /// Convert a vector of database values into the data structure implementing the `Recordable` trait. diff --git a/log_db/tests/integration.rs b/log_db/tests/integration.rs index 37b4182..6ce3930 100644 --- a/log_db/tests/integration.rs +++ b/log_db/tests/integration.rs @@ -51,6 +51,12 @@ impl Recordable for Inst { (Field::Data, ValueType::bytes()), ] } + fn primary_key() -> Self::Field { + Field::Id + } + fn secondary_keys() -> Vec<Self::Field> { + vec![Field::Name] + } fn into_record(self) -> Vec<Value> { vec![ @@ -89,7 +95,6 @@ fn test_initialize_only() { let data_dir = tmp_dir(); let _db = DB::<Inst>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); } @@ -99,7 +104,6 @@ fn test_upsert_and_get_with_primary_memtable() { let data_dir = tmp_dir(); let mut db = DB::<Inst>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); @@ -122,7 +126,6 @@ fn test_upsert_and_get() { let data_dir = tmp_dir(); let mut db = DB::<Inst>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); @@ -175,7 +178,6 @@ fn test_get_nonexistant() { let data_dir = tmp_dir(); let mut db = DB::<Inst>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); @@ -189,6 +191,9 @@ impl Recordable for InstTestNullable { fn schema() -> Vec<(Self::Field, ValueType)> { vec![(Field::Id, ValueType::int())] } + fn primary_key() -> Self::Field { + Field::Id + } fn into_record(self) -> Vec<Value> { vec![Value::Null] @@ -204,7 +209,6 @@ fn test_upsert_fails_on_null_in_non_nullable_field() { let data_dir = tmp_dir(); let mut db = DB::<InstTestNullable>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); @@ -221,6 +225,9 @@ impl Recordable for InstTestNumValues { (Field::Name, ValueType::string()), ] } + fn primary_key() -> Self::Field { + Field::Id + } fn into_record(self) -> Vec<Value> { vec![Value::Int(0)] @@ -236,7 +243,6 @@ fn test_upsert_fails_on_invalid_number_of_values() { let data_dir = tmp_dir(); let mut db = DB::<InstTestNumValues>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); @@ -250,6 +256,9 @@ impl Recordable for InstTestInvalidType { fn schema() -> Vec<(Self::Field, ValueType)> { vec![(Field::Id, ValueType::int())] } + fn primary_key() -> Self::Field { + Field::Id + } fn into_record(self) -> Vec<Value> { vec![Value::String("foo".to_string())] @@ -265,7 +274,6 @@ fn test_upsert_fails_on_invalid_value_type() { let data_dir = tmp_dir(); let mut db = DB::<InstTestInvalidType>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); @@ -278,8 +286,6 @@ fn test_upsert_and_find_all() { let data_dir = tmp_dir(); let mut db = DB::<Inst>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) - .secondary_keys(&[Field::Name]) .initialize() .expect("Failed to initialize DB instance"); @@ -322,6 +328,9 @@ impl Recordable for InstSingleId { fn schema() -> Vec<(Self::Field, ValueType)> { vec![(Field::Id, ValueType::int())] } + fn primary_key() -> Self::Field { + Field::Id + } fn into_record(self) -> Vec<Value> { vec![Value::Int(self.id)] @@ -350,7 +359,6 @@ fn test_multiple_writing_threads() { threads.push(thread::spawn(move || { let mut db = DB::<InstSingleId>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); @@ -366,7 +374,6 @@ fn test_multiple_writing_threads() { // Read the records let mut db = DB::<InstSingleId>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); @@ -394,7 +401,6 @@ fn test_one_writer_and_multiple_reading_threads() { let mut db = DB::<InstSingleId>::configure() .data_dir(&data_dir) .segment_size(1000) // should cause rotations - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); @@ -420,7 +426,6 @@ fn test_one_writer_and_multiple_reading_threads() { threads.push(thread::spawn(move || { let mut db = DB::<InstSingleId>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); @@ -452,7 +457,6 @@ fn test_log_is_rotated_when_capacity_reached() { let mut db = DB::<Inst>::configure() .data_dir(&data_dir) .segment_size(10 * record_len) // small log segment size - .primary_key(Field::Id) .initialize() .expect("Failed to initialize DB instance"); @@ -482,8 +486,6 @@ fn test_delete() { let data_dir = tmp_dir(); let mut db = DB::<Inst>::configure() .data_dir(&data_dir) - .primary_key(Field::Id) - .secondary_keys(&[Field::Name]) .initialize() .expect("Failed to initialize DB instance"); |
