1use crate::manager::Inner;
18use std::ops::Range;
19use std::sync::{
20 Arc,
21 atomic::{AtomicBool, AtomicUsize, Ordering},
22};
23use crate::MemorySize;
26
27pub type FileChunk = StorageBatch<u8>;
34
35#[derive(Debug)]
38pub struct StorageBatch<T> {
39 values: Arc<Vec<T>>,
40 range: Range<usize>,
41 charges: Arc<Vec<Arc<WriteCharge>>>,
42 memory: Arc<Vec<crate::arrow::RetainedMemory>>,
43}
44
45#[derive(Debug)]
46pub(crate) struct WriteCharge {
47 bytes: usize,
48 retired: AtomicBool,
49 held: Arc<AtomicUsize>,
50 changed: tokio::sync::watch::Sender<u64>,
51}
52
53impl<T> StorageBatch<T> {
60 pub fn new(values: Vec<T>) -> Self {
61 let range = 0..values.len();
62 Self { values: Arc::new(values), range, charges: Arc::new(Vec::new()), memory: Arc::new(Vec::new()) }
63 }
64
65 pub(crate) fn retained(mut self, charge: Arc<WriteCharge>) -> Self {
66 let mut charges = self.charges.as_ref().clone();
67 charges.push(charge);
68 self.charges = Arc::new(charges);
69 self
70 }
71
72 pub(crate) fn retain_memory(mut self, memory: crate::arrow::RetainedMemory) -> Self {
73 Arc::make_mut(&mut self.memory).push(memory);
74 self
75 }
76
77 pub(crate) fn charged(&self) -> bool {
78 self.charges.iter().any(|charge| charge.retired.load(Ordering::SeqCst))
79 }
80
81 pub(crate) fn retire(&self) {
82 for charge in self.charges.iter() {
83 charge.retire();
84 }
85 }
86
87 pub(crate) fn merge(batches: &mut [&mut Self]) -> Option<Self> {
88 let first = batches.first()?;
89 let mut range = first.range.clone();
90 let adjoining = batches.iter().skip(1).all(|batch| {
91 let joined = Arc::ptr_eq(&first.values, &batch.values) && range.end == batch.range.start;
92 range.end = batch.range.end;
93 joined
94 });
95 if adjoining {
96 return Some(Self {
97 values: Arc::clone(&first.values),
98 range,
99 charges: Arc::clone(&first.charges),
100 memory: Arc::clone(&first.memory),
101 });
102 }
103 if batches.iter().any(|batch| Arc::strong_count(&batch.values) != 1 || batch.range != (0..batch.values.len())) {
104 return None;
105 }
106 let count = batches.iter().map(|batch| batch.len()).sum();
107 let lengths: Vec<_> = batches.iter().map(|batch| batch.len()).collect();
108 let charges = batches.iter().flat_map(|batch| batch.charges.iter().cloned()).collect();
109 let memory = batches.iter().flat_map(|batch| batch.memory.iter().cloned()).collect();
110 let mut values = Vec::with_capacity(count);
111 for batch in batches.iter_mut() {
112 let source = std::mem::take(Arc::get_mut(&mut batch.values).expect("unique queued backing"));
113 values.extend(source);
114 }
115 let merged = Self { values: Arc::new(values), range: 0..count, charges: Arc::new(charges), memory: Arc::new(memory) };
116 let mut start = 0;
117 for (batch, length) in batches.iter_mut().zip(lengths) {
118 **batch = merged.slice(start..start + length);
119 start += length;
120 }
121 Some(merged)
122 }
123
124 pub fn as_slice(&self) -> &[T] {
125 &self.values[self.range.clone()]
126 }
127
128 pub fn slice(&self, range: Range<usize>) -> Self {
129 assert!(range.start <= range.end && range.end <= self.len(), "batch slice is outside its span");
130 let range = self.range.start + range.start..self.range.start + range.end;
131 Self { values: Arc::clone(&self.values), range, charges: Arc::clone(&self.charges), memory: Arc::clone(&self.memory) }
132 }
133
134 pub fn len(&self) -> usize {
135 self.range.len()
136 }
137
138 pub fn is_empty(&self) -> bool {
139 self.range.is_empty()
140 }
141
142 pub fn capacity(&self) -> usize {
143 self.values.capacity()
144 }
145
146 pub fn allocation_bytes(&self) -> usize {
147 self.values.capacity().saturating_mul(std::mem::size_of::<T>())
148 }
149}
150
151impl WriteCharge {
152 pub(crate) fn new(inner: &Arc<Inner>, bytes: usize) -> Arc<Self> {
153 Arc::new(Self {
154 bytes,
155 retired: AtomicBool::new(false),
156 held: Arc::clone(&inner.retained_write_bytes),
157 changed: inner.changed.clone(),
158 })
159 }
160
161 pub(crate) fn read(inner: &Arc<Inner>, bytes: usize) -> Arc<Self> {
162 Arc::new(Self {
163 bytes,
164 retired: AtomicBool::new(false),
165 held: Arc::clone(&inner.retained_read_bytes),
166 changed: inner.changed.clone(),
167 })
168 }
169
170 pub(crate) fn retire(&self) {
171 if !self.retired.swap(true, Ordering::SeqCst) {
172 self.held.fetch_add(self.bytes, Ordering::SeqCst);
173 }
174 }
175}
176
177impl Drop for WriteCharge {
178 fn drop(&mut self) {
179 if self.retired.load(Ordering::SeqCst) {
180 self.held.fetch_sub(self.bytes, Ordering::SeqCst);
181 self.changed.send_modify(|revision| *revision = revision.wrapping_add(1));
182 }
183 }
184}
185
186impl From<Vec<u8>> for StorageBatch<u8> {
187 fn from(values: Vec<u8>) -> Self {
188 Self::new(values)
189 }
190}
191
192impl<T> Clone for StorageBatch<T> {
193 fn clone(&self) -> Self {
194 Self {
195 values: Arc::clone(&self.values),
196 range: self.range.clone(),
197 charges: Arc::clone(&self.charges),
198 memory: Arc::clone(&self.memory),
199 }
200 }
201}
202
203impl<T: MemorySize> MemorySize for StorageBatch<T> {
204 fn memory_size(&self) -> usize {
205 std::mem::size_of::<Self>().saturating_add(self.values.as_ref().memory_size()).saturating_add(2 * std::mem::size_of::<usize>())
206 }
207}
208
209#[cfg(test)]
216mod tests {
217 use super::{FileChunk, StorageBatch};
218 use crate::MemorySize;
219
220 #[test]
221 fn chunk_allocation() {
222 let mut bytes = Vec::with_capacity(1024);
223 bytes.extend_from_slice(&[1, 2, 3, 4]);
224 let pointer = bytes.as_ptr();
225 let capacity = bytes.capacity();
226 let batch = FileChunk::from(bytes);
227 assert_eq!(batch.as_slice().as_ptr(), pointer);
228 let retained = batch.slice(1..3);
229 let empty = batch.slice(4..4);
230 assert_eq!(retained.allocation_bytes(), capacity);
231 assert_eq!(empty.memory_size(), batch.memory_size());
232 drop(batch);
233 assert_eq!(retained.as_slice(), &[2, 3]);
234 assert_eq!(retained.as_slice().as_ptr(), pointer.wrapping_add(1));
235 }
236
237 #[test]
238 fn slice_accounting() {
239 let mut payload = Vec::with_capacity(4096);
240 payload.push(7u8);
241 let batch = StorageBatch::new(vec![payload, vec![8u8]]);
242 let slice = batch.slice(1..2);
243 assert_eq!(slice.memory_size(), batch.memory_size());
244 assert!(slice.memory_size() >= 4096);
245 }
246
247 fn storage() -> crate::StorageManager {
248 use crate::test_backend::{Data, Recovery, history, storage_settings};
249 use std::sync::Arc;
250 crate::StorageManager::new(
251 crate::StorageBackends { primary: Arc::new(Data::<()>::new(history())), state: Arc::new(Recovery::new(history())) },
252 storage_settings(),
253 )
254 .unwrap()
255 }
256
257 fn pending(storage: &crate::StorageManager) -> std::sync::Arc<crate::manager::Pending> {
258 use crate::{Identifier, PayloadEncoding, SchemaKey, StoredRecord};
259 let record = StoredRecord {
260 position: storage.position(1),
261 schema: SchemaKey {
262 id: Identifier::new("record").unwrap(),
263 version: std::num::NonZeroU32::MIN,
264 payload_encoding: PayloadEncoding::PostcardV1,
265 },
266 scope: None,
267 item: Identifier::new("item").unwrap(),
268 source: None,
269 source_time: None,
270 payload: vec![7; 32],
271 };
272 std::sync::Arc::new(crate::manager::Pending::new(
273 &storage.inner,
274 None,
275 1,
276 4096,
277 crate::manager::Operation::Records(StorageBatch::new(vec![record])),
278 ))
279 }
280
281 #[test]
282 fn pending_retention() {
283 let storage = storage();
284 let pending = pending(&storage);
285 let crate::manager::Operation::Records(values) = &pending.operation else { unreachable!() };
286 let detached = values.clone();
287 let empty = detached.slice(1..1);
288 {
289 let mut state = storage.inner.state.lock().unwrap();
290 state.queue.push_back(std::sync::Arc::clone(&pending));
291 state.collect();
292 assert_eq!(state.status.pending_bytes, 4096);
293 assert_eq!(state.retained_write_bytes.load(std::sync::atomic::Ordering::SeqCst), 0);
294 state.queue.retire_data(1, 1);
295 state.collect();
296 assert_eq!(state.status.pending_bytes, 4096);
297 }
298 drop(pending);
299 drop(detached);
300 assert_eq!(storage.status().pending_bytes, 4096);
301 drop(empty);
302 assert_eq!(storage.status().pending_bytes, 0);
303 }
304
305 #[test]
306 fn failure_retention() {
307 let storage = storage();
308 let pending = pending(&storage);
309 let crate::manager::Operation::Records(values) = &pending.operation else { unreachable!() };
310 let detached = values.clone();
311 {
312 let mut state = storage.inner.state.lock().unwrap();
313 state.queue.push_back(std::sync::Arc::clone(&pending));
314 state.status.failure = Some(std::sync::Arc::new(crate::Error::NotReady { domain: "injected backend" }));
315 state.status.closing = true;
316 state.collect();
317 assert_eq!(state.status.pending_bytes, 4096);
318 assert_eq!(state.retained_write_bytes.load(std::sync::atomic::Ordering::SeqCst), 0);
319 state.queue.retire_data(1, 1);
320 state.status.closed = true;
321 }
322 drop(pending);
323 assert_eq!(storage.status().pending_bytes, 4096);
324 drop(detached);
325 assert_eq!(storage.status().pending_bytes, 0);
326 }
327
328 #[test]
329 fn payload_merge() {
330 let left = vec![7u8; 32];
331 let right = vec![8u8; 32];
332 let pointers = [left.as_ptr(), right.as_ptr()];
333 let mut first = StorageBatch::new(vec![left]);
334 let mut second = StorageBatch::new(vec![right]);
335 let merged = StorageBatch::merge(&mut [&mut first, &mut second]).unwrap();
336 assert_eq!(merged.as_slice()[0].as_ptr(), pointers[0]);
337 assert_eq!(merged.as_slice()[1].as_ptr(), pointers[1]);
338 let detached = merged.clone();
339 let retry = StorageBatch::merge(&mut [&mut first, &mut second]).unwrap();
340 assert_eq!(retry.len(), 2);
341 assert_eq!(retry.as_slice()[1].as_ptr(), pointers[1]);
342 drop(detached);
343 }
344
345 #[test]
346 fn shared_merge_refused() {
347 let mut first = StorageBatch::new(vec![vec![7u8; 32]]);
348 let mut second = StorageBatch::new(vec![vec![8u8; 32]]);
349 let detached = first.clone();
350 assert!(StorageBatch::merge(&mut [&mut first, &mut second]).is_none());
351 assert_eq!(first.as_slice(), detached.as_slice());
352 assert_eq!(second.as_slice()[0], vec![8; 32]);
353 }
354}