1use crate::be_tree::BeTree;
13use crate::epoch::Epoch;
14use crate::rowid::RowId;
15use serde::{Deserialize, Serialize};
16use std::collections::{BTreeMap, HashMap};
17use std::sync::Arc;
18
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22pub enum Value {
23 Null,
24 Bool(bool),
25 Int64(i64),
26 Float64(f64),
27 Bytes(Vec<u8>),
28 Embedding(Vec<f32>),
29 Decimal(i128),
32 Interval {
34 months: i64,
35 days: i32,
36 nanos: i64,
37 },
38 Uuid([u8; 16]),
40 Json(Vec<u8>),
42 GeneratedEmbedding(Box<crate::embedding::GeneratedEmbeddingValue>),
46}
47
48impl Value {
49 pub fn as_embedding(&self) -> Option<&[f32]> {
50 match self {
51 Self::Embedding(values) => Some(values),
52 Self::GeneratedEmbedding(value) => Some(&value.vector),
53 _ => None,
54 }
55 }
56
57 pub fn generated_embedding_metadata(
58 &self,
59 ) -> Option<&crate::embedding::GeneratedEmbeddingMetadata> {
60 match self {
61 Self::GeneratedEmbedding(value) => Some(&value.metadata),
62 _ => None,
63 }
64 }
65
66 pub fn encode_key(&self) -> Vec<u8> {
69 match self {
70 Value::Null => Vec::new(),
71 Value::Bool(b) => vec![*b as u8],
72 Value::Int64(n) => n.to_be_bytes().to_vec(),
73 Value::Float64(f) => f.to_bits().to_be_bytes().to_vec(),
74 Value::Bytes(b) => b.clone(),
75 Value::Embedding(v) => {
76 let mut out = Vec::with_capacity(v.len() * 4);
77 for x in v {
78 out.extend_from_slice(&x.to_bits().to_be_bytes());
79 }
80 out
81 }
82 Value::GeneratedEmbedding(value) => {
83 let mut out = Vec::with_capacity(value.vector.len() * 4);
84 for x in &value.vector {
85 out.extend_from_slice(&x.to_bits().to_be_bytes());
86 }
87 out
88 }
89 Value::Decimal(d) => d.to_be_bytes().to_vec(),
90 Value::Interval {
91 months,
92 days,
93 nanos,
94 } => {
95 let mut out = Vec::with_capacity(20);
96 out.extend_from_slice(&months.to_be_bytes());
97 out.extend_from_slice(&days.to_be_bytes());
98 out.extend_from_slice(&nanos.to_be_bytes());
99 out
100 }
101 Value::Uuid(b) => b.to_vec(),
102 Value::Json(b) => b.clone(),
103 }
104 }
105
106 pub(crate) fn estimated_bytes(&self) -> u64 {
107 match self {
108 Value::Null => 1,
109 Value::Bool(_) => 1,
110 Value::Int64(_) | Value::Float64(_) => 8,
111 Value::Bytes(bytes) | Value::Json(bytes) => 16 + bytes.len() as u64,
112 Value::Embedding(values) => 16 + (values.len() as u64) * 4,
113 Value::GeneratedEmbedding(value) => {
114 16 + (value.vector.len() as u64) * 4
115 + value.metadata.provider_id.len() as u64
116 + value.metadata.model_id.len() as u64
117 + value.metadata.model_version.len() as u64
118 + value.metadata.preprocessing_version.len() as u64
119 + 48
120 }
121 Value::Decimal(_) | Value::Uuid(_) => 16,
122 Value::Interval { .. } => 20,
123 }
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct Row {
130 pub row_id: RowId,
131 pub committed_epoch: Epoch,
132 pub columns: HashMap<u16, Value>,
133 pub deleted: bool,
134}
135
136impl Row {
137 pub fn new(row_id: RowId, committed_epoch: Epoch) -> Self {
138 Self {
139 row_id,
140 committed_epoch,
141 columns: HashMap::new(),
142 deleted: false,
143 }
144 }
145
146 pub fn with_column(mut self, column_id: u16, value: Value) -> Self {
147 self.columns.insert(column_id, value);
148 self
149 }
150
151 pub fn estimated_bytes(&self) -> u64 {
153 self.columns
154 .values()
155 .fold(32, |bytes, value| bytes + value.estimated_bytes())
156 }
157}
158
159#[derive(Clone)]
163struct MemtableSegment {
164 tree: BeTree,
165 byte_size: u64,
166}
167
168#[derive(Clone)]
170pub struct Memtable {
171 frozen: Arc<Vec<Arc<MemtableSegment>>>,
172 active: MemtableSegment,
173 byte_size: u64,
174}
175
176impl Default for Memtable {
177 fn default() -> Self {
178 Self::new()
179 }
180}
181
182impl Memtable {
183 pub fn new() -> Self {
184 Self {
185 frozen: Arc::new(Vec::new()),
186 active: MemtableSegment {
187 tree: BeTree::new(),
188 byte_size: 0,
189 },
190 byte_size: 0,
191 }
192 }
193
194 pub fn upsert(&mut self, row: Row) {
197 let bytes = row.estimated_bytes();
198 self.byte_size = self.byte_size.saturating_add(bytes);
199 self.active.byte_size = self.active.byte_size.saturating_add(bytes);
200 self.active.tree.insert_row(row);
201 }
202
203 pub fn tombstone(&mut self, row_id: RowId, epoch: Epoch) {
207 let mut columns = HashMap::new();
208 if let Some(live) = self.get(row_id, Epoch(epoch.0.saturating_sub(1))) {
209 columns = live.columns;
210 }
211 let row = Row {
212 row_id,
213 committed_epoch: epoch,
214 columns,
215 deleted: true,
216 };
217 self.upsert(row);
218 }
219
220 pub fn get(&self, row_id: RowId, snapshot_epoch: Epoch) -> Option<Row> {
224 self.get_version(row_id, snapshot_epoch)
225 .and_then(|(_, row)| (!row.deleted).then_some(row))
226 }
227
228 pub fn get_version(&self, row_id: RowId, snapshot_epoch: Epoch) -> Option<(Epoch, Row)> {
232 let mut best = self.active.tree.get_version(row_id, snapshot_epoch);
233 for segment in self.frozen.iter().rev() {
234 let Some(candidate) = segment.tree.get_version(row_id, snapshot_epoch) else {
235 continue;
236 };
237 if best.as_ref().is_none_or(|(epoch, _)| candidate.0 > *epoch) {
238 best = Some(candidate);
239 }
240 }
241 best
242 }
243
244 pub fn len(&self) -> usize {
246 self.active.tree.mutations()
247 + self
248 .frozen
249 .iter()
250 .map(|segment| segment.tree.mutations())
251 .sum::<usize>()
252 }
253
254 pub fn is_empty(&self) -> bool {
255 self.active.tree.is_empty() && self.frozen.is_empty()
256 }
257
258 pub fn approx_bytes(&self) -> u64 {
259 self.byte_size
260 }
261
262 pub fn visible_rows(&self, snapshot_epoch: Epoch) -> Vec<Row> {
265 self.visible_versions(snapshot_epoch)
266 .into_iter()
267 .filter(|r| !r.deleted)
268 .collect()
269 }
270
271 pub fn visible_versions(&self, snapshot_epoch: Epoch) -> Vec<Row> {
275 let mut by_row: BTreeMap<RowId, Row> = BTreeMap::new();
276 for segment in self
277 .frozen
278 .iter()
279 .map(|segment| &segment.tree)
280 .chain(std::iter::once(&self.active.tree))
281 {
282 for row in segment.versions() {
283 if row.committed_epoch <= snapshot_epoch {
284 by_row
285 .entry(row.row_id)
286 .and_modify(|existing| {
287 if row.committed_epoch > existing.committed_epoch {
288 *existing = row.clone();
289 }
290 })
291 .or_insert(row);
292 }
293 }
294 }
295 by_row.into_values().collect()
296 }
297
298 pub(crate) fn seal(&mut self) {
300 if self.active.tree.is_empty() {
301 return;
302 }
303 let active = std::mem::replace(
304 &mut self.active,
305 MemtableSegment {
306 tree: BeTree::new(),
307 byte_size: 0,
308 },
309 );
310 Arc::make_mut(&mut self.frozen).push(Arc::new(active));
311 if self.frozen.len() >= crate::MAX_READ_GENERATION_LAYERS {
312 self.consolidate();
313 }
314 }
315
316 fn consolidate(&mut self) {
317 let mut tree = BeTree::new();
318 for row in self
319 .frozen
320 .iter()
321 .flat_map(|segment| segment.tree.versions())
322 {
323 tree.insert_row(row);
324 }
325 self.frozen = Arc::new(vec![Arc::new(MemtableSegment {
326 tree,
327 byte_size: self.byte_size,
328 })]);
329 }
330
331 #[cfg(test)]
332 pub(crate) fn frozen_layer_count(&self) -> usize {
333 self.frozen.len()
334 }
335
336 pub fn drain_sorted(&mut self) -> Vec<Row> {
339 let mut out = self
340 .frozen
341 .iter()
342 .flat_map(|segment| segment.tree.versions())
343 .chain(self.active.tree.versions())
344 .collect::<Vec<_>>();
345 out.sort_by_key(|row| (row.row_id, row.committed_epoch));
346 self.frozen = Arc::new(Vec::new());
347 self.active = MemtableSegment {
348 tree: BeTree::new(),
349 byte_size: 0,
350 };
351 self.byte_size = 0;
352 out
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359
360 fn row(id: u64, epoch: u64) -> Row {
361 Row::new(RowId(id), Epoch(epoch)).with_column(1, Value::Int64(id as i64 * 10))
362 }
363
364 #[test]
365 fn upsert_get_and_visibility() {
366 let mut m = Memtable::new();
367 m.upsert(row(1, 5));
368 assert_eq!(m.len(), 1);
369 assert!(m.get(RowId(1), Epoch(5)).is_some());
370 assert!(m.get(RowId(1), Epoch(4)).is_none()); assert!(m.get(RowId(2), Epoch(9)).is_none()); }
373
374 #[test]
375 fn tombstone_supersedes_at_its_epoch() {
376 let mut m = Memtable::new();
377 m.upsert(row(1, 1));
378 assert!(m.get(RowId(1), Epoch(1)).is_some());
380 m.tombstone(RowId(1), Epoch(2));
381 assert!(m.get(RowId(1), Epoch(2)).is_none());
383 assert!(m.get(RowId(1), Epoch(9)).is_none());
384 assert!(m.get(RowId(1), Epoch(1)).is_some());
386 }
387
388 #[test]
389 fn sealed_generations_share_rows_and_consolidate() {
390 let mut writer = Memtable::new();
391 for id in 0..crate::MAX_READ_GENERATION_LAYERS as u64 + 2 {
392 writer.upsert(row(id, id + 1));
393 writer.seal();
394 }
395 assert!(writer.frozen_layer_count() < crate::MAX_READ_GENERATION_LAYERS);
396 let generation = writer.clone();
397 writer.upsert(row(99, 99));
398 assert!(generation.get(RowId(99), Epoch(99)).is_none());
399 assert!(writer.get(RowId(99), Epoch(99)).is_some());
400 }
401
402 #[test]
403 fn drain_sorted_is_ascending_and_empties() {
404 let mut m = Memtable::new();
405 m.upsert(row(3, 1));
406 m.upsert(row(1, 1));
407 m.upsert(row(2, 1));
408 let out = m.drain_sorted();
409 let ids: Vec<u64> = out.iter().map(|r| r.row_id.0).collect();
410 assert_eq!(ids, vec![1, 2, 3]);
411 assert!(m.is_empty());
412 assert_eq!(m.approx_bytes(), 0);
413 }
414
415 #[test]
416 fn visible_rows_dedups_to_newest_version() {
417 let mut m = Memtable::new();
418 m.upsert(row(1, 1));
419 m.upsert(row(2, 9)); m.upsert(row(3, 1));
421 m.upsert(row(1, 3)); let ids: Vec<u64> = m
423 .visible_rows(Epoch(5))
424 .iter()
425 .map(|r| r.row_id.0)
426 .collect();
427 assert_eq!(ids, vec![1, 3]);
428 }
429}