aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--log_db/src/common.rs2
-rw-r--r--log_db/src/lib.rs62
-rw-r--r--log_db/src/record.rs9
-rw-r--r--log_db/tests/integration.rs34
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");