akar_storage/update_info.rs
1//! UpdateInfo — MVCC version chain for column updates.
2//!
3//! Each `ColumnChunk` can have an optional `UpdateInfo` that tracks
4//! versioned updates to individual rows. On a versioned write, the old
5//! value is preserved in a version chain. Readers at a given snapshot
6//! timestamp traverse the chain to find the value visible to them.
7//!
8//! This is the Rust port of Vela C++'s `UpdateInfo` / `VectorUpdateInfo`.
9
10use std::sync::Mutex;
11
12/// A single node in the update version chain for one vector (1024 rows).
13///
14/// Each node stores the version (transaction commit timestamp) at which an
15/// update takes effect, the OLD value that update replaced, and a link to
16/// the previous (older) version node.
17#[derive(Debug, Clone)]
18pub struct VectorUpdateInfo {
19 /// The commit timestamp at which this update takes effect.
20 pub version: u64,
21 /// The serialized value that this update replaced (for undo / snapshot reads).
22 pub data: Vec<u8>,
23 /// Link to the previous (older) version in the chain.
24 pub prev: Option<Box<VectorUpdateInfo>>,
25}
26
27impl VectorUpdateInfo {
28 pub fn new(version: u64, data: Vec<u8>, prev: Option<Box<VectorUpdateInfo>>) -> Self {
29 Self { version, data, prev }
30 }
31}
32
33/// Manages update version chains for a column chunk.
34///
35/// The structure is a per-vector (1024 rows) linked list of `VectorUpdateInfo`
36/// nodes. Each update prepends a new node to the chain for the affected
37/// vector, so recent versions are found first.
38///
39/// Note: `Clone` is implemented manually because `Mutex` is not `Clone`.
40#[derive(Debug)]
41pub struct UpdateInfo {
42 /// Per-vector version chains. Indexed by `vector_idx`.
43 vectors: Mutex<Vec<Option<Box<VectorUpdateInfo>>>>,
44 /// Number of rows per vector.
45 vector_size: u32,
46}
47
48impl Clone for UpdateInfo {
49 fn clone(&self) -> Self {
50 let vectors = self.vectors.lock().unwrap().clone();
51 Self {
52 vectors: Mutex::new(vectors),
53 vector_size: self.vector_size,
54 }
55 }
56}
57
58impl UpdateInfo {
59 /// Create a new UpdateInfo for a column chunk with `total_rows` capacity.
60 pub fn new(total_rows: usize) -> Self {
61 let vector_size = 1024u32;
62 let num_vectors = total_rows.div_ceil(vector_size as usize);
63 Self {
64 vectors: Mutex::new(vec![None; num_vectors]),
65 vector_size,
66 }
67 }
68
69 fn vector_idx(&self, row: u32) -> usize {
70 (row / self.vector_size) as usize
71 }
72
73 /// Append an update: create a new `VectorUpdateInfo` node at the head
74 /// of the chain for the vector containing `row`.
75 ///
76 /// `version` is the commit timestamp that will make this update visible.
77 /// `data` is the serialized old data being replaced (for undo).
78 pub fn append_update(&self, row: u32, version: u64, data: Vec<u8>) {
79 let v_idx = self.vector_idx(row);
80 let mut vectors = self.vectors.lock().unwrap();
81 if v_idx >= vectors.len() {
82 vectors.resize(v_idx + 1, None);
83 }
84 let prev = vectors[v_idx].take();
85 vectors[v_idx] = Some(Box::new(VectorUpdateInfo::new(version, data, prev)));
86 }
87
88 /// Get the value visible to a snapshot at `snapshot_ts` for a given row.
89 ///
90 /// Each chain node stores the OLD value its update replaced; the base
91 /// (latest) value lives in the `ColumnChunk.values` array. The value
92 /// visible at `snapshot_ts` is the data of the newest node whose version
93 /// is still STRICTLY GREATER than the snapshot — that node holds the value
94 /// that the next update hasn't replaced yet. So the chain is walked from
95 /// newest to oldest and the node immediately before the first node with
96 /// `version <= snapshot_ts` is the answer.
97 ///
98 /// Returns `None` when every update is visible (`version <= snapshot_ts`),
99 /// meaning the caller should use the base (latest) value.
100 pub fn get_version(&self, row: u32, snapshot_ts: u64) -> Option<Vec<u8>> {
101 let v_idx = self.vector_idx(row);
102 let vectors = self.vectors.lock().unwrap();
103 if v_idx >= vectors.len() {
104 return None;
105 }
106 let mut current = vectors[v_idx].as_ref()?;
107 let mut prev: Option<&VectorUpdateInfo> = None;
108 loop {
109 if current.version <= snapshot_ts {
110 break;
111 }
112 prev = Some(current);
113 match ¤t.prev {
114 Some(next) => current = next,
115 None => break,
116 }
117 }
118 prev.map(|p| p.data.clone())
119 }
120
121 /// Get the most recent update data for a row (regardless of visibility).
122 /// Used internally during commit to resolve the latest version.
123 pub fn latest(&self, row: u32) -> Option<Vec<u8>> {
124 let v_idx = self.vector_idx(row);
125 let vectors = self.vectors.lock().unwrap();
126 vectors
127 .get(v_idx)
128 .as_ref()
129 .and_then(|o| o.as_ref().map(|node| node.data.clone()))
130 }
131
132 /// Number of vectors with at least one update.
133 pub fn num_dirty_vectors(&self) -> usize {
134 self.vectors.lock().unwrap().iter().filter(|v| v.is_some()).count()
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn test_update_info_append_and_get() {
144 let ui = UpdateInfo::new(4096);
145
146 // Update for row 5 at version 10 replaces value 0xAA.
147 ui.append_update(5, 10, vec![0xAA]);
148
149 // Before v10 the replaced value (0xAA) is still visible.
150 assert_eq!(ui.get_version(5, 5), Some(vec![0xAA]));
151 // At/after v10 the update is visible → base (latest) value is visible.
152 assert_eq!(ui.get_version(5, 10), None);
153 assert_eq!(ui.get_version(5, 20), None);
154 }
155
156 #[test]
157 fn test_update_info_version_chain() {
158 let ui = UpdateInfo::new(4096);
159
160 // Row 5: value 0xAA replaced at v10, then value 0xBB replaced at v20.
161 ui.append_update(5, 10, vec![0xAA]);
162 ui.append_update(5, 20, vec![0xBB]);
163
164 // Before v10 the value replaced at v10 (0xAA) is visible.
165 assert_eq!(ui.get_version(5, 5), Some(vec![0xAA]));
166 // Between v10 and v20 the value replaced at v20 (0xBB) is visible.
167 assert_eq!(ui.get_version(5, 15), Some(vec![0xBB]));
168 // At/after v20 both updates are visible → base (latest) value visible.
169 assert_eq!(ui.get_version(5, 25), None);
170 }
171
172 #[test]
173 fn test_update_info_no_update() {
174 let ui = UpdateInfo::new(4096);
175 assert_eq!(ui.get_version(0, 100), None);
176 assert_eq!(ui.latest(0), None);
177 }
178
179 #[test]
180 fn test_update_info_latest() {
181 let ui = UpdateInfo::new(4096);
182 ui.append_update(5, 10, vec![0xAA]);
183 ui.append_update(5, 20, vec![0xBB]);
184 assert_eq!(ui.latest(5), Some(vec![0xBB]));
185 }
186}