1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
|
use super::*;
pub struct Schema<F> {
pub fields: Vec<(F, Type)>,
pub primary_key: F,
pub secondary_keys: Vec<F>,
}
pub struct ConfigBuilder<T, F> {
data_dir: Option<String>,
segment_size: Option<usize>,
write_durability: Option<WriteDurability>,
read_consistency: Option<ReadConsistency>,
schema: Option<Vec<(F, Type)>>,
primary_key: Option<F>,
secondary_keys: Option<Vec<F>>,
from_record: Option<fn(Vec<Value>) -> T>,
into_record: Option<fn(T) -> Vec<Value>>,
_marker: PhantomData<T>,
}
impl<T, F: Eq + Clone> ConfigBuilder<T, F> {
pub fn new() -> ConfigBuilder<T, F> {
ConfigBuilder {
data_dir: None,
segment_size: None,
write_durability: None,
read_consistency: None,
schema: None,
primary_key: None,
secondary_keys: None,
from_record: None,
into_record: None,
_marker: PhantomData,
}
}
/// The directory where the database will store its data.
pub fn data_dir(mut self, data_dir: impl Into<String>) -> Self {
self.data_dir = Some(data_dir.into());
self
}
/// The maximum size of a segment file in bytes.
/// Once a segment file reaches this size, it can be closed, rotated and compacted.
/// Note that this is not a hard limit: if `db.do_maintenance_tasks()` is not called,
/// the segment file may continue to grow.
pub fn segment_size(mut self, segment_size: usize) -> Self {
self.segment_size = Some(segment_size);
self
}
/// The write durability policy for the database.
/// This determines how writes are persisted to disk.
/// The default is WriteDurability::Flush.
pub fn write_durability(mut self, write_durability: WriteDurability) -> Self {
self.write_durability = Some(write_durability);
self
}
/// The read consistency policy for the database.
/// This determines how recent writes are visible when reading.
/// See individual `ReadConsistency` enum values for more information.
/// The default is ReadConsistency::Strong.
pub fn read_consistency(mut self, read_consistency: ReadConsistency) -> Self {
self.read_consistency = Some(read_consistency);
self
}
pub fn schema(mut self, schema: Vec<(F, Type)>) -> Self {
self.schema = Some(schema);
self
}
pub fn primary_key(mut self, primary_key: F) -> Self {
self.primary_key = Some(primary_key);
self
}
pub fn secondary_keys(mut self, secondary_keys: Vec<F>) -> Self {
self.secondary_keys = Some(secondary_keys);
self
}
pub fn from_record(mut self, from_record: fn(Vec<Value>) -> T) -> Self {
self.from_record = Some(from_record);
self
}
pub fn into_record(mut self, into_record: fn(T) -> Vec<Value>) -> Self {
self.into_record = Some(into_record);
self
}
pub fn initialize(self) -> DBResult<DB<T, F>> {
let schema = self
.schema
.ok_or_else(|| DBError::ValidationError("Schema not set".to_string()))?;
let primary_key = self
.primary_key
.ok_or_else(|| DBError::ValidationError("Primary key not set".to_string()))?;
let from_record = self
.from_record
.ok_or_else(|| DBError::ValidationError("Callback from_record not set".to_string()))?;
let into_record = self
.into_record
.ok_or_else(|| DBError::ValidationError("Callback into_record not set".to_string()))?;
let config = Config {
schema,
primary_key,
secondary_keys: self.secondary_keys.unwrap_or_default(),
from_record,
into_record,
data_dir: self.data_dir.clone().unwrap_or("db_data".to_string()),
segment_size: self.segment_size.unwrap_or(4 * 1024 * 1024), // 4MB
write_durability: self
.write_durability
.clone()
.unwrap_or(WriteDurability::Flush),
read_consistency: self
.read_consistency
.clone()
.unwrap_or(ReadConsistency::Strong),
};
DB::initialize(config)
}
}
#[derive(Clone)]
pub struct Config<T, F> {
pub schema: Vec<(F, Type)>,
pub primary_key: F,
pub secondary_keys: Vec<F>,
pub from_record: fn(Vec<Value>) -> T,
pub into_record: fn(T) -> Vec<Value>,
pub data_dir: String,
pub segment_size: usize,
pub write_durability: WriteDurability,
pub read_consistency: ReadConsistency,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ReadConsistency {
/// Reads by client A are guaranteed to see writes by themselves and any writes by other clients B
/// that were done before last index refresh. You must call `refresh_indexes()` manually to refresh indexes.
Eventual,
/// Reads by client A are guaranteed to see all writes. This is slower: all reads must first
/// refresh indexes.
Strong,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum WriteDurability {
/// Changes are written to the OS write buffer but not immediately synced to disk.
/// 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 immediately.
/// Offers the best durability guarantees but is a lot slower.
FlushSync,
}
impl Display for WriteDurability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{:?}", self)?;
Ok(())
}
}
|