linkrs_sync/batch/
transaction_buffer.rs1use dashmap::DashMap;
2
3use super::trait_def::BatchBuffer;
4use crate::batch::error::BatchResult;
5use crate::types::{IndexOpKey, IndexOperation};
6use linkrs_core::types::TransactionId;
7
8#[derive(Debug, Default)]
9pub struct TransactionBufferEntry {
10 pub operations: Vec<SequencedIndexOperation>,
11}
12
13#[derive(Debug, Clone)]
14pub struct SequencedIndexOperation {
15 pub sequence: u64,
16 pub operation: IndexOperation,
17}
18
19pub struct TransactionBatchBuffer {
20 pending: DashMap<TransactionId, DashMap<IndexOpKey, TransactionBufferEntry>>,
21}
22
23impl std::fmt::Debug for TransactionBatchBuffer {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 f.debug_struct("TransactionBatchBuffer")
26 .field("pending_count", &self.pending.len())
27 .finish_non_exhaustive()
28 }
29}
30
31impl Default for TransactionBatchBuffer {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37impl TransactionBatchBuffer {
38 pub fn new() -> Self {
39 Self {
40 pending: DashMap::new(),
41 }
42 }
43
44 pub fn take_operations(
45 &self,
46 txn_id: TransactionId,
47 ) -> BatchResult<Vec<(IndexOpKey, Vec<IndexOperation>)>> {
48 if let Some((_, txn_buffer)) = self.pending.remove(&txn_id) {
49 let mut result = Vec::new();
50 for entry in txn_buffer.iter() {
51 let key = entry.key().clone();
52 let ops = entry
53 .value()
54 .operations
55 .iter()
56 .cloned()
57 .map(|op| op.operation)
58 .collect::<Vec<_>>();
59 if !ops.is_empty() {
60 result.push((key, ops));
61 }
62 }
63 Ok(result)
64 } else {
65 Ok(Vec::new())
66 }
67 }
68
69 pub fn prepare(&self, txn_id: TransactionId, operation: IndexOperation) -> BatchResult<()> {
70 self.prepare_with_sequence(txn_id, 0, operation)
71 }
72
73 pub fn prepare_with_sequence(
74 &self,
75 txn_id: TransactionId,
76 sequence: u64,
77 operation: IndexOperation,
78 ) -> BatchResult<()> {
79 let txn_buffer = self.pending.entry(txn_id).or_default();
80
81 let key = operation.key.clone();
82
83 let mut entry = txn_buffer.entry(key).or_default();
84 entry.operations.push(SequencedIndexOperation {
85 sequence,
86 operation,
87 });
88 Ok(())
89 }
90
91 pub fn peek_operations(
92 &self,
93 txn_id: TransactionId,
94 ) -> BatchResult<Vec<(IndexOpKey, Vec<IndexOperation>)>> {
95 if let Some(txn_buffer) = self.pending.get(&txn_id) {
96 let mut result = Vec::new();
97 for entry in txn_buffer.iter() {
98 let key = entry.key().clone();
99 let ops = entry
100 .value()
101 .operations
102 .iter()
103 .cloned()
104 .map(|op| op.operation)
105 .collect::<Vec<_>>();
106 if !ops.is_empty() {
107 result.push((key, ops));
108 }
109 }
110 Ok(result)
111 } else {
112 Ok(Vec::new())
113 }
114 }
115
116 pub fn rollback(&self, txn_id: TransactionId) -> BatchResult<()> {
117 self.pending.remove(&txn_id);
118 Ok(())
119 }
120
121 pub fn truncate_operations(&self, txn_id: TransactionId, sequence: u64) -> BatchResult<()> {
122 if let Some(txn_buffer) = self.pending.get_mut(&txn_id) {
123 let keys: Vec<IndexOpKey> =
124 txn_buffer.iter().map(|entry| entry.key().clone()).collect();
125
126 for key in keys {
127 if let Some(mut entry) = txn_buffer.get_mut(&key) {
128 entry.operations.retain(|op| op.sequence <= sequence);
129 }
130 }
131 }
132 Ok(())
133 }
134
135 pub fn pending_sequence(&self, txn_id: TransactionId) -> u64 {
136 self.pending
137 .get(&txn_id)
138 .and_then(|txn_buffer| {
139 txn_buffer
140 .iter()
141 .flat_map(|entry| {
142 let ops = entry.value().operations.clone();
143 ops.into_iter().map(|op| op.sequence)
144 })
145 .max()
146 })
147 .unwrap_or(0)
148 }
149
150 pub fn pending_count(&self, txn_id: TransactionId) -> usize {
151 self.pending
152 .get(&txn_id)
153 .map(|txn_buffer| txn_buffer.iter().map(|e| e.value().operations.len()).sum())
154 .unwrap_or(0)
155 }
156}
157
158impl TransactionBatchBuffer {
159 pub fn add_operation(&self, txn_id: TransactionId, key: &IndexOpKey, value: IndexOperation) {
160 let txn_buffer = self.pending.entry(txn_id).or_default();
161 let mut entry = txn_buffer.entry(key.clone()).or_default();
162 entry.operations.push(SequencedIndexOperation {
163 sequence: 0,
164 operation: value,
165 });
166 }
167}
168
169impl BatchBuffer<IndexOpKey, IndexOperation> for TransactionBatchBuffer {
170 fn add(&self, key: &IndexOpKey, value: IndexOperation) {
171 self.add_operation(TransactionId::new(0), key, value);
172 }
173
174 fn drain(&self, key: &IndexOpKey) -> Vec<IndexOperation> {
175 if let Some(txn_buffer) = self.pending.get(&TransactionId::new(0)) {
176 if let Some(entry) = txn_buffer.get(key) {
177 return entry
178 .operations
179 .iter()
180 .map(|op| op.operation.clone())
181 .collect();
182 }
183 }
184 Vec::new()
185 }
186
187 fn peek(&self, key: &IndexOpKey) -> Vec<IndexOperation> {
188 if let Some(txn_buffer) = self.pending.get(&TransactionId::new(0)) {
189 if let Some(entry) = txn_buffer.get(key) {
190 return entry
191 .operations
192 .iter()
193 .map(|op| op.operation.clone())
194 .collect();
195 }
196 }
197 Vec::new()
198 }
199
200 fn count(&self, key: &IndexOpKey) -> usize {
201 if let Some(txn_buffer) = self.pending.get(&TransactionId::new(0)) {
202 if let Some(entry) = txn_buffer.get(key) {
203 return entry.operations.len();
204 }
205 }
206 0
207 }
208
209 fn is_empty(&self, key: &IndexOpKey) -> bool {
210 self.count(key) == 0
211 }
212
213 fn keys(&self) -> Vec<IndexOpKey> {
214 if let Some(txn_buffer) = self.pending.get(&TransactionId::new(0)) {
215 return txn_buffer.iter().map(|e| e.key().clone()).collect();
216 }
217 Vec::new()
218 }
219
220 fn clear(&self) {
221 self.pending.clear();
222 }
223
224 fn total_count(&self) -> usize {
225 self.pending
226 .iter()
227 .map(|txn_buffer| {
228 txn_buffer
229 .value()
230 .iter()
231 .map(|e| e.value().operations.len())
232 .sum::<usize>()
233 })
234 .sum()
235 }
236}