featherdb_mvcc/
snapshot.rs1use featherdb_core::TransactionId;
4use std::collections::HashSet;
5
6#[derive(Debug, Clone)]
8pub struct Snapshot {
9 txn_id: TransactionId,
11 high_water_mark: TransactionId,
13 in_progress_at_start: HashSet<TransactionId>,
16 visible_ancestors: HashSet<TransactionId>,
19 is_clean: bool,
22}
23
24impl Snapshot {
25 pub fn new(
27 txn_id: TransactionId,
28 high_water_mark: TransactionId,
29 in_progress: HashSet<TransactionId>,
30 ) -> Self {
31 let is_clean = in_progress.is_empty();
32 Snapshot {
33 txn_id,
34 high_water_mark,
35 in_progress_at_start: in_progress,
36 visible_ancestors: HashSet::new(),
37 is_clean,
38 }
39 }
40
41 pub fn with_ancestors(
46 txn_id: TransactionId,
47 high_water_mark: TransactionId,
48 in_progress: HashSet<TransactionId>,
49 ancestors: HashSet<TransactionId>,
50 ) -> Self {
51 let is_clean = in_progress.is_empty() && ancestors.is_empty();
52 Snapshot {
53 txn_id,
54 high_water_mark,
55 in_progress_at_start: in_progress,
56 visible_ancestors: ancestors,
57 is_clean,
58 }
59 }
60
61 pub fn add_ancestor(&mut self, ancestor: TransactionId) {
63 self.visible_ancestors.insert(ancestor);
64 self.is_clean = false;
65 }
66
67 pub fn is_ancestor(&self, txn_id: TransactionId) -> bool {
69 self.visible_ancestors.contains(&txn_id)
70 }
71
72 pub fn txn_id(&self) -> TransactionId {
74 self.txn_id
75 }
76
77 #[inline]
80 pub fn sees_all_committed(&self) -> bool {
81 self.is_clean
82 }
83
84 #[inline]
86 pub fn high_water_mark(&self) -> TransactionId {
87 self.high_water_mark
88 }
89
90 #[inline]
92 pub fn can_see(&self, created_by: TransactionId) -> bool {
93 if created_by == self.txn_id {
95 return true;
96 }
97
98 if self.is_clean {
101 return created_by < self.high_water_mark;
102 }
103
104 if self.visible_ancestors.contains(&created_by) {
106 return true;
107 }
108
109 if created_by >= self.high_water_mark {
111 return false;
112 }
113
114 !self.in_progress_at_start.contains(&created_by)
116 }
117
118 #[inline]
120 pub fn is_visible(&self, created_by: TransactionId, deleted_by: Option<TransactionId>) -> bool {
121 if !self.can_see(created_by) {
123 return false;
124 }
125
126 if let Some(deleted_by) = deleted_by {
128 if self.can_see(deleted_by) {
129 return false;
130 }
131 }
132
133 true
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn test_snapshot_own_changes_visible() {
143 let snapshot = Snapshot::new(TransactionId(5), TransactionId(10), HashSet::new());
144
145 assert!(snapshot.can_see(TransactionId(5)));
146 }
147
148 #[test]
149 fn test_snapshot_old_committed_visible() {
150 let snapshot = Snapshot::new(TransactionId(5), TransactionId(10), HashSet::new());
151
152 assert!(snapshot.can_see(TransactionId(3)));
154 }
155
156 #[test]
157 fn test_snapshot_future_not_visible() {
158 let snapshot = Snapshot::new(TransactionId(5), TransactionId(10), HashSet::new());
159
160 assert!(!snapshot.can_see(TransactionId(15)));
162 }
163
164 #[test]
165 fn test_snapshot_in_progress_not_visible() {
166 let mut in_progress = HashSet::new();
167 in_progress.insert(TransactionId(3));
168
169 let snapshot = Snapshot::new(TransactionId(5), TransactionId(10), in_progress);
170
171 assert!(!snapshot.can_see(TransactionId(3)));
173 }
174
175 #[test]
176 fn test_snapshot_deleted_visibility() {
177 let snapshot = Snapshot::new(TransactionId(5), TransactionId(10), HashSet::new());
178
179 assert!(snapshot.is_visible(TransactionId(2), None));
181
182 assert!(!snapshot.is_visible(TransactionId(2), Some(TransactionId(3))));
184
185 assert!(snapshot.is_visible(TransactionId(2), Some(TransactionId(15))));
187 }
188}