1use futures::future::{BoxFuture, FutureExt as _, Shared};
4
5pub mod paged;
6mod read;
7mod tip;
8mod write;
9
10pub use read::Read;
11pub use write::Write;
12
13#[derive(Clone)]
19struct Completion(Shared<BoxFuture<'static, Result<(), crate::Error>>>);
20
21impl Completion {
22 fn handle(&self) -> crate::Handle<()> {
24 crate::Handle::from_future(self.0.clone())
25 }
26
27 async fn wait(&self) -> Result<(), crate::Error> {
29 self.0.clone().await
30 }
31}
32
33impl From<crate::Handle<()>> for Completion {
34 fn from(handle: crate::Handle<()>) -> Self {
35 Self(handle.boxed().shared())
36 }
37}
38
39enum SyncState {
51 Clean,
53 Dirty,
55 Pending(Completion),
57}
58
59impl SyncState {
60 fn mark_dirty(&mut self) {
62 assert!(
63 !matches!(self, Self::Pending(_)),
64 "pending sync must be joined before marking dirty"
65 );
66 *self = Self::Dirty;
67 }
68
69 async fn wait_for_pending(&mut self) -> Result<(), crate::Error> {
71 let Self::Pending(pending) = self else {
72 return Ok(());
73 };
74 match pending.wait().await {
75 Ok(()) => {
76 *self = Self::Clean;
77 Ok(())
78 }
79 Err(err) => {
80 *self = Self::Dirty;
82 Err(err)
83 }
84 }
85 }
86
87 async fn write_at(
89 &mut self,
90 blob: &impl crate::Blob,
91 offset: u64,
92 bufs: impl Into<crate::IoBufs> + Send,
93 ) -> Result<(), crate::Error> {
94 self.wait_for_pending().await?;
95 blob.write_at(offset, bufs).await?;
96 self.mark_dirty();
97 Ok(())
98 }
99
100 async fn write_at_sync(
102 &mut self,
103 blob: &impl crate::Blob,
104 offset: u64,
105 bufs: impl Into<crate::IoBufs> + Send,
106 ) -> Result<(), crate::Error> {
107 self.wait_for_pending().await?;
108 match self {
109 Self::Dirty => {
110 blob.write_at(offset, bufs).await?;
112 blob.sync().await?;
113 *self = Self::Clean;
114 Ok(())
115 }
116 Self::Clean => {
117 self.mark_dirty();
119 blob.write_at_sync(offset, bufs).await?;
120 *self = Self::Clean;
121 Ok(())
122 }
123 Self::Pending(_) => unreachable!("pending sync waited above"),
124 }
125 }
126
127 async fn resize(&mut self, blob: &impl crate::Blob, len: u64) -> Result<(), crate::Error> {
129 self.wait_for_pending().await?;
130 blob.resize(len).await?;
131 self.mark_dirty();
132 Ok(())
133 }
134
135 async fn sync(&mut self, blob: &impl crate::Blob) -> Result<(), crate::Error> {
137 self.wait_for_pending().await?;
138 if matches!(self, Self::Clean) {
139 return Ok(());
140 }
141 blob.sync().await?;
142 *self = Self::Clean;
143 Ok(())
144 }
145
146 async fn start_sync(&mut self, blob: &impl crate::Blob) -> crate::Handle<()> {
148 match self {
149 Self::Clean => crate::Handle::ready(Ok(())),
150 Self::Dirty => {
151 let pending = Completion::from(blob.start_sync().await);
153 let handle = pending.handle();
154 *self = Self::Pending(pending);
155 handle
156 }
157 Self::Pending(pending) => pending.handle(),
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165 use crate::{
166 deterministic,
167 mocks::{next_pending_sync, DelayedSyncBlob},
168 Blob as _, BufMut, Error, Handle, IoBufMut, IoBufs, IoBufsMut, Runner, Storage,
169 };
170 use commonware_macros::test_traced;
171 use commonware_utils::{sync::Mutex, NZUsize};
172 use std::sync::Arc;
173
174 #[derive(Default)]
175 struct RangeSyncState {
176 data: Vec<u8>,
181
182 durable: Vec<u8>,
184
185 writes: usize,
187
188 full_syncs: usize,
190
191 range_syncs: usize,
193 }
194
195 #[derive(Clone)]
202 pub struct SyncTrackingBlob {
203 state: Arc<Mutex<RangeSyncState>>,
204 }
205
206 impl SyncTrackingBlob {
207 pub fn new() -> Self {
208 Self {
209 state: Arc::new(Mutex::new(RangeSyncState::default())),
210 }
211 }
212
213 pub fn snapshot(&self) -> (Vec<u8>, usize, usize, usize) {
214 let state = self.state.lock();
215 (
216 state.durable.clone(),
217 state.writes,
218 state.full_syncs,
219 state.range_syncs,
220 )
221 }
222
223 pub fn size(&self) -> u64 {
224 self.state.lock().data.len() as u64
225 }
226
227 fn write(data: &mut Vec<u8>, offset: u64, buf: &[u8]) -> Result<(), Error> {
228 let start = usize::try_from(offset).map_err(|_| Error::OffsetOverflow)?;
229 let end = start.checked_add(buf.len()).ok_or(Error::OffsetOverflow)?;
230 if end > data.len() {
231 data.resize(end, 0);
232 }
233 data[start..end].copy_from_slice(buf);
234 Ok(())
235 }
236 }
237
238 impl crate::Blob for SyncTrackingBlob {
239 async fn read_at(&self, offset: u64, len: usize) -> Result<IoBufsMut, Error> {
240 self.read_at_buf(offset, len, IoBufMut::default()).await
241 }
242
243 async fn read_at_buf(
244 &self,
245 offset: u64,
246 len: usize,
247 buf: impl Into<IoBufsMut> + Send,
248 ) -> Result<IoBufsMut, Error> {
249 let start = usize::try_from(offset).map_err(|_| Error::OffsetOverflow)?;
250 let end = start.checked_add(len).ok_or(Error::OffsetOverflow)?;
251 let state = self.state.lock();
252 if end > state.data.len() {
253 return Err(Error::BlobInsufficientLength);
254 }
255
256 let mut out = buf.into();
257 out.put_slice(&state.data[start..end]);
258 Ok(out)
259 }
260
261 async fn write_at(&self, offset: u64, buf: impl Into<IoBufs> + Send) -> Result<(), Error> {
262 let buf = buf.into().coalesce();
263 let mut state = self.state.lock();
264 Self::write(&mut state.data, offset, buf.as_ref())?;
265 state.writes += 1;
266 Ok(())
267 }
268
269 async fn write_at_sync(
270 &self,
271 offset: u64,
272 buf: impl Into<IoBufs> + Send,
273 ) -> Result<(), Error> {
274 let buf = buf.into().coalesce();
275 let mut state = self.state.lock();
276 Self::write(&mut state.data, offset, buf.as_ref())?;
277 Self::write(&mut state.durable, offset, buf.as_ref())?;
278 state.writes += 1;
279 state.range_syncs += 1;
280 Ok(())
281 }
282
283 async fn resize(&self, len: u64) -> Result<(), Error> {
284 let len = usize::try_from(len).map_err(|_| Error::OffsetOverflow)?;
285 self.state.lock().data.resize(len, 0);
286 Ok(())
287 }
288
289 async fn sync(&self) -> Result<(), Error> {
290 let mut state = self.state.lock();
291 state.durable = state.data.clone();
292 state.full_syncs += 1;
293 Ok(())
294 }
295
296 async fn start_sync(&self) -> Handle<()> {
297 Handle::ready(self.sync().await)
298 }
299 }
300
301 #[test_traced]
302 fn test_read_basic() {
303 let executor = deterministic::Runner::default();
304 executor.start(|context| async move {
305 let data = b"Hello, world! This is a test.";
307 let (blob, size) = context.open("partition", b"test").await.unwrap();
308 assert_eq!(size, 0);
309 blob.write_at(0, data).await.unwrap();
310 let size = data.len() as u64;
311
312 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(10));
314
315 let read = reader.read(5).await.unwrap().coalesce();
317 assert_eq!(read.as_ref(), b"Hello");
318
319 let read = reader.read(14).await.unwrap().coalesce();
321 assert_eq!(read.as_ref(), b", world! This ");
322
323 assert_eq!(reader.position(), 19);
325
326 let read = reader.read(7).await.unwrap().coalesce();
328 assert_eq!(read.as_ref(), b"is a te");
329
330 let result = reader.read(5).await;
332 assert!(matches!(result, Err(Error::BlobInsufficientLength)));
333 });
334 }
335
336 #[test_traced]
337 fn test_read_cross_boundary() {
338 let executor = deterministic::Runner::default();
339 executor.start(|context| async move {
340 let data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
342 let (blob, size) = context.open("partition", b"test").await.unwrap();
343 assert_eq!(size, 0);
344 blob.write_at(0, data).await.unwrap();
345 let size = data.len() as u64;
346
347 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(10));
349
350 let read = reader.read(15).await.unwrap().coalesce();
352 assert_eq!(read.as_ref(), b"ABCDEFGHIJKLMNO");
353
354 assert_eq!(reader.position(), 15);
356
357 let read = reader.read(11).await.unwrap().coalesce();
359 assert_eq!(read.as_ref(), b"PQRSTUVWXYZ");
360
361 assert_eq!(reader.position(), 26);
363 assert_eq!(reader.blob_remaining(), 0);
364 });
365 }
366
367 #[test_traced]
369 fn test_read_to_end_then_rewind_and_read_again() {
370 let executor = deterministic::Runner::default();
371 executor.start(|context| async move {
372 let data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
373 let (blob, size) = context.open("partition", b"test").await.unwrap();
374 assert_eq!(size, 0);
375 blob.write_at(0, data).await.unwrap();
376 let size = data.len() as u64;
377
378 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(20));
379
380 let read = reader.read(21).await.unwrap().coalesce();
382 assert_eq!(read.as_ref(), b"ABCDEFGHIJKLMNOPQRSTU");
383
384 assert_eq!(reader.position(), 21);
386
387 let read = reader.read(5).await.unwrap().coalesce();
389 assert_eq!(read.as_ref(), b"VWXYZ");
390
391 reader.seek_to(0).unwrap();
393 let read = reader.read(21).await.unwrap().coalesce();
394 assert_eq!(read.as_ref(), b"ABCDEFGHIJKLMNOPQRSTU");
395 });
396 }
397
398 #[test_traced]
399 fn test_read_with_known_size() {
400 let executor = deterministic::Runner::default();
401 executor.start(|context| async move {
402 let data = b"This is a test with known size limitations.";
404 let (blob, size) = context.open("partition", b"test").await.unwrap();
405 assert_eq!(size, 0);
406 blob.write_at(0, data).await.unwrap();
407 let size = data.len() as u64;
408
409 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(10));
411
412 assert_eq!(reader.blob_remaining(), size);
414
415 let read = reader.read(5).await.unwrap().coalesce();
417 assert_eq!(read.as_ref(), b"This ");
418
419 assert_eq!(reader.blob_remaining(), size - 5);
421
422 let read = reader.read((size - 5) as usize).await.unwrap().coalesce();
424 assert_eq!(read.as_ref(), b"is a test with known size limitations.");
425
426 assert_eq!(reader.blob_remaining(), 0);
428
429 let result = reader.read(1).await;
431 assert!(matches!(result, Err(Error::BlobInsufficientLength)));
432 });
433 }
434
435 #[test_traced]
436 fn test_read_oversized_request_does_not_consume_buffered_bytes() {
437 let executor = deterministic::Runner::default();
438 executor.start(|context| async move {
439 let data = b"abcdefghij";
440 let (blob, size) = context
441 .open("partition", b"double-count-regression")
442 .await
443 .unwrap();
444 assert_eq!(size, 0);
445 blob.write_at(0, data).await.unwrap();
446
447 let mut reader = Read::from_pooler(&context, blob, data.len() as u64, NZUsize!(8));
448
449 let first = reader.read(6).await.unwrap().coalesce();
451 assert_eq!(first.as_ref(), b"abcdef");
452 assert_eq!(reader.position(), 6);
453
454 let err = reader.read(5).await.unwrap_err();
456 assert!(matches!(err, Error::BlobInsufficientLength));
457 assert_eq!(reader.position(), 6);
458
459 let tail = reader.read(4).await.unwrap().coalesce();
461 assert_eq!(tail.as_ref(), b"ghij");
462 assert_eq!(reader.position(), 10);
463 });
464 }
465
466 #[test_traced]
467 fn test_read_large_data() {
468 let executor = deterministic::Runner::default();
469 executor.start(|context| async move {
470 let data_size = 1024 * 256; let data = vec![0x42; data_size];
473 let (blob, size) = context.open("partition", b"test").await.unwrap();
474 assert_eq!(size, 0);
475 blob.write_at(0, data.clone()).await.unwrap();
476 let size = data.len() as u64;
477
478 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(64 * 1024));
480
481 let mut total_read = 0;
483 let chunk_size = 8 * 1024; while total_read < data_size {
486 let to_read = std::cmp::min(chunk_size, data_size - total_read);
487 let read = reader.read(to_read).await.unwrap().coalesce();
488
489 assert!(
491 read.as_ref().iter().all(|&b| b == 0x42),
492 "Data at position {total_read} is not correct"
493 );
494
495 total_read += to_read;
496 }
497
498 assert_eq!(total_read, data_size);
500
501 let result = reader.read(1).await;
503 assert!(matches!(result, Err(Error::BlobInsufficientLength)));
504 });
505 }
506
507 #[test_traced]
508 fn test_read_exact_size_reads() {
509 let executor = deterministic::Runner::default();
510 executor.start(|context| async move {
511 let buffer_size = 1024;
513 let data_size = buffer_size * 5 / 2; let data = vec![0x37; data_size];
515
516 let (blob, size) = context.open("partition", b"test").await.unwrap();
517 assert_eq!(size, 0);
518 blob.write_at(0, data.clone()).await.unwrap();
519 let size = data.len() as u64;
520
521 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(buffer_size));
522
523 let read = reader.read(buffer_size).await.unwrap().coalesce();
525 assert!(read.as_ref().iter().all(|&b| b == 0x37));
526
527 let read = reader.read(buffer_size).await.unwrap().coalesce();
529 assert!(read.as_ref().iter().all(|&b| b == 0x37));
530
531 let half_buffer = buffer_size / 2;
533 let read = reader.read(half_buffer).await.unwrap().coalesce();
534 assert!(read.as_ref().iter().all(|&b| b == 0x37));
535
536 assert_eq!(reader.blob_remaining(), 0);
538 assert_eq!(reader.position(), size);
539 });
540 }
541
542 #[test_traced]
543 fn test_read_structure_single_vs_chunked() {
544 let executor = deterministic::Runner::default();
545 executor.start(|context| async move {
546 let data = b"ABCDEFGHIJKL";
547 let (blob, size) = context.open("partition", b"structural").await.unwrap();
548 assert_eq!(size, 0);
549 blob.write_at(0, data).await.unwrap();
550
551 let mut reader = Read::from_pooler(&context, blob, data.len() as u64, NZUsize!(5));
552
553 let first = reader.read(3).await.unwrap();
555 assert!(first.is_single());
556 assert_eq!(first.coalesce().as_ref(), b"ABC");
557
558 let second = reader.read(7).await.unwrap();
560 assert!(!second.is_single());
561 assert_eq!(second.coalesce().as_ref(), b"DEFGHIJ");
562 });
563 }
564
565 #[test_traced]
566 fn test_read_seek_to() {
567 let executor = deterministic::Runner::default();
568 executor.start(|context| async move {
569 let data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
571 let (blob, size) = context.open("partition", b"test").await.unwrap();
572 assert_eq!(size, 0);
573 blob.write_at(0, data).await.unwrap();
574 let size = data.len() as u64;
575
576 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(10));
578
579 let read = reader.read(5).await.unwrap().coalesce();
581 assert_eq!(read.as_ref(), b"ABCDE");
582 assert_eq!(reader.position(), 5);
583
584 reader.seek_to(10).unwrap();
586 assert_eq!(reader.position(), 10);
587
588 let read = reader.read(5).await.unwrap().coalesce();
590 assert_eq!(read.as_ref(), b"KLMNO");
591
592 reader.seek_to(0).unwrap();
594 assert_eq!(reader.position(), 0);
595
596 let read = reader.read(5).await.unwrap().coalesce();
597 assert_eq!(read.as_ref(), b"ABCDE");
598
599 reader.seek_to(size).unwrap();
601 assert_eq!(reader.position(), size);
602
603 let result = reader.read(1).await;
605 assert!(matches!(result, Err(Error::BlobInsufficientLength)));
606
607 let result = reader.seek_to(size + 10);
609 assert!(matches!(result, Err(Error::BlobInsufficientLength)));
610 });
611 }
612
613 #[test_traced]
614 fn test_read_seek_with_refill() {
615 let executor = deterministic::Runner::default();
616 executor.start(|context| async move {
617 let data = vec![0x41; 1000]; let (blob, size) = context.open("partition", b"test").await.unwrap();
620 assert_eq!(size, 0);
621 blob.write_at(0, data.clone()).await.unwrap();
622 let size = data.len() as u64;
623
624 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(10));
626
627 let _ = reader.read(5).await.unwrap().coalesce();
629
630 reader.seek_to(500).unwrap();
632
633 let read = reader.read(5).await.unwrap().coalesce();
635 assert_eq!(read.as_ref(), b"AAAAA"); assert_eq!(reader.position(), 505);
637
638 reader.seek_to(100).unwrap();
640
641 let _ = reader.read(5).await.unwrap().coalesce();
643 assert_eq!(reader.position(), 105);
644 });
645 }
646
647 #[test_traced]
648 fn test_read_seek_within_buffered_range() {
649 let executor = deterministic::Runner::default();
650 executor.start(|context| async move {
651 let data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
652 let (blob, size) = context.open("partition", b"test").await.unwrap();
653 assert_eq!(size, 0);
654 blob.write_at(0, data).await.unwrap();
655
656 let mut reader = Read::from_pooler(&context, blob, data.len() as u64, NZUsize!(10));
657
658 let read = reader.read(6).await.unwrap().coalesce();
660 assert_eq!(read.as_ref(), b"ABCDEF");
661 assert_eq!(reader.position(), 6);
662 assert_eq!(reader.buffer_remaining(), 4);
663
664 reader.seek_to(3).unwrap();
666 assert_eq!(reader.position(), 3);
667 assert_eq!(reader.buffer_remaining(), 7);
668
669 let read = reader.read(5).await.unwrap().coalesce();
670 assert_eq!(read.as_ref(), b"DEFGH");
671 assert_eq!(reader.position(), 8);
672 assert_eq!(reader.buffer_remaining(), 2);
673 });
674 }
675
676 #[test_traced]
677 fn test_read_seek_within_unread_buffer_does_not_refill() {
678 let executor = deterministic::Runner::default();
679 executor.start(|context| async move {
680 let data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
681 let (blob, size) = context
682 .open("partition", b"seek_unread_no_refill")
683 .await
684 .unwrap();
685 assert_eq!(size, 0);
686 blob.write_at(0, data).await.unwrap();
687
688 let mut reader = Read::from_pooler(&context, blob, data.len() as u64, NZUsize!(10));
689
690 let first = reader.read(6).await.unwrap();
692 assert_eq!(first.coalesce().as_ref(), b"ABCDEF");
693 assert_eq!(reader.position(), 6);
694 assert_eq!(reader.buffer_remaining(), 4);
695
696 reader.seek_to(7).unwrap();
698 assert_eq!(reader.position(), 7);
699 assert_eq!(reader.buffer_remaining(), 3);
700
701 let second = reader.read(3).await.unwrap();
703 assert_eq!(second.coalesce().as_ref(), b"HIJ");
704 assert_eq!(reader.position(), 10);
705 assert_eq!(reader.buffer_remaining(), 0);
706
707 let third = reader.read(1).await.unwrap();
709 assert_eq!(third.coalesce().as_ref(), b"K");
710 assert_eq!(reader.position(), 11);
711 assert_eq!(reader.buffer_remaining(), 9);
712 });
713 }
714
715 #[test_traced]
716 fn test_read_resize() {
717 let executor = deterministic::Runner::default();
718 executor.start(|context| async move {
719 let data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
721 let (blob, size) = context.open("partition", b"test").await.unwrap();
722 assert_eq!(size, 0);
723 blob.write_at(0, data).await.unwrap();
724 let data_len = data.len() as u64;
725
726 let reader = Read::from_pooler(&context, blob.clone(), data_len, NZUsize!(10));
728
729 let resize_len = data_len / 2;
731 reader.resize(resize_len).await.unwrap();
732
733 let (blob, size) = context.open("partition", b"test").await.unwrap();
735 assert_eq!(size, resize_len, "Blob should be resized to half size");
736
737 let mut new_reader = Read::from_pooler(&context, blob, size, NZUsize!(10));
739
740 let read = new_reader.read(size as usize).await.unwrap().coalesce();
742 assert_eq!(
743 read.as_ref(),
744 b"ABCDEFGHIJKLM",
745 "Resized content should match"
746 );
747
748 let result = new_reader.read(1).await;
750 assert!(matches!(result, Err(Error::BlobInsufficientLength)));
751
752 new_reader.resize(data_len * 2).await.unwrap();
754
755 let (blob, new_size) = context.open("partition", b"test").await.unwrap();
757 assert_eq!(new_size, data_len * 2);
758
759 let mut new_reader = Read::from_pooler(&context, blob, new_size, NZUsize!(10));
761 let read = new_reader.read(new_size as usize).await.unwrap().coalesce();
762 assert_eq!(&read.as_ref()[..size as usize], b"ABCDEFGHIJKLM");
763 assert_eq!(
764 &read.as_ref()[size as usize..],
765 vec![0u8; new_size as usize - size as usize]
766 );
767 });
768 }
769
770 #[test_traced]
771 fn test_read_resize_to_zero() {
772 let executor = deterministic::Runner::default();
773 executor.start(|context| async move {
774 let data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
776 let data_len = data.len() as u64;
777 let (blob, size) = context.open("partition", b"test").await.unwrap();
778 assert_eq!(size, 0);
779 blob.write_at(0, data).await.unwrap();
780
781 let reader = Read::from_pooler(&context, blob.clone(), data_len, NZUsize!(10));
783
784 reader.resize(0).await.unwrap();
786
787 let (blob, size) = context.open("partition", b"test").await.unwrap();
789 assert_eq!(size, 0, "Blob should be resized to zero");
790
791 let mut new_reader = Read::from_pooler(&context, blob, size, NZUsize!(10));
793
794 let result = new_reader.read(1).await;
796 assert!(matches!(result, Err(Error::BlobInsufficientLength)));
797 });
798 }
799
800 #[test_traced]
801 fn test_write_basic() {
802 let executor = deterministic::Runner::default();
803 executor.start(|context| async move {
804 let (blob, size) = context.open("partition", b"write_basic").await.unwrap();
806 assert_eq!(size, 0);
807
808 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(8));
809 writer.write_at(0, b"hello").await.unwrap();
810 assert_eq!(writer.size(), 5);
811 writer.sync().await.unwrap();
812 assert_eq!(writer.size(), 5);
813
814 let (blob, size) = context.open("partition", b"write_basic").await.unwrap();
816 assert_eq!(size, 5);
817 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(8));
818 let read = reader.read(5).await.unwrap().coalesce();
819 assert_eq!(read.as_ref(), b"hello");
820 });
821 }
822
823 #[test_traced]
824 fn test_write_multiple_flushes() {
825 let executor = deterministic::Runner::default();
826 executor.start(|context| async move {
827 let (blob, size) = context.open("partition", b"write_multi").await.unwrap();
829 assert_eq!(size, 0);
830
831 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(4));
832 writer.write_at(0, b"abc").await.unwrap();
833 assert_eq!(writer.size(), 3);
834 writer.write_at(3, b"defg").await.unwrap();
835 assert_eq!(writer.size(), 7);
836 writer.sync().await.unwrap();
837
838 let (blob, size) = context.open("partition", b"write_multi").await.unwrap();
840 assert_eq!(size, 7);
841 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(4));
842 let read = reader.read(7).await.unwrap().coalesce();
843 assert_eq!(read.as_ref(), b"abcdefg");
844 });
845 }
846
847 #[test_traced]
848 fn test_write_large_data() {
849 let executor = deterministic::Runner::default();
850 executor.start(|context| async move {
851 let (blob, size) = context.open("partition", b"write_large").await.unwrap();
853 assert_eq!(size, 0);
854
855 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(4));
856 writer.write_at(0, b"abc").await.unwrap();
857 assert_eq!(writer.size(), 3);
858 writer
859 .write_at(3, b"defghijklmnopqrstuvwxyz")
860 .await
861 .unwrap();
862 assert_eq!(writer.size(), 26);
863 writer.sync().await.unwrap();
864 assert_eq!(writer.size(), 26);
865
866 let (blob, size) = context.open("partition", b"write_large").await.unwrap();
868 assert_eq!(size, 26);
869 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(4));
870 let read = reader.read(26).await.unwrap().coalesce();
871 assert_eq!(read.as_ref(), b"abcdefghijklmnopqrstuvwxyz");
872 });
873 }
874
875 #[test_traced]
876 fn test_write_append_to_buffer() {
877 let executor = deterministic::Runner::default();
878 executor.start(|context| async move {
879 let (blob, size) = context.open("partition", b"append_buf").await.unwrap();
881 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(10));
882
883 writer.write_at(0, b"hello").await.unwrap();
885 assert_eq!(writer.size(), 5);
886
887 writer.write_at(5, b" world").await.unwrap();
889 writer.sync().await.unwrap();
890 assert_eq!(writer.size(), 11);
891
892 let (blob, size) = context.open("partition", b"append_buf").await.unwrap();
894 assert_eq!(size, 11);
895 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(10));
896 let read = reader.read(11).await.unwrap().coalesce();
897 assert_eq!(read.as_ref(), b"hello world");
898 });
899 }
900
901 #[test_traced]
902 fn test_write_into_middle_of_buffer() {
903 let executor = deterministic::Runner::default();
904 executor.start(|context| async move {
905 let (blob, size) = context.open("partition", b"middle_buf").await.unwrap();
907 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(20));
908
909 writer.write_at(0, b"abcdefghij").await.unwrap();
911 assert_eq!(writer.size(), 10);
912
913 writer.write_at(2, b"01234").await.unwrap();
915 assert_eq!(writer.size(), 10);
916 writer.sync().await.unwrap();
917
918 let (blob, size) = context.open("partition", b"middle_buf").await.unwrap();
920 assert_eq!(size, 10);
921 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(10));
922 let read = reader.read(10).await.unwrap().coalesce();
923 assert_eq!(read.as_ref(), b"ab01234hij");
924
925 writer.write_at(10, b"klmnopqrst").await.unwrap();
927 assert_eq!(writer.size(), 20);
928 writer.write_at(9, b"wxyz").await.unwrap();
929 assert_eq!(writer.size(), 20);
930 writer.sync().await.unwrap();
931
932 let (blob, size) = context.open("partition", b"middle_buf").await.unwrap();
934 assert_eq!(size, 20);
935 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(20));
936 let read = reader.read(20).await.unwrap().coalesce();
937 assert_eq!(read.as_ref(), b"ab01234hiwxyznopqrst");
938 });
939 }
940
941 #[test_traced]
942 fn test_write_before_buffer() {
943 let executor = deterministic::Runner::default();
944 executor.start(|context| async move {
945 let (blob, size) = context.open("partition", b"before_buf").await.unwrap();
947 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(10));
948
949 writer.write_at(10, b"0123456789").await.unwrap();
951 assert_eq!(writer.size(), 20);
952
953 writer.write_at(0, b"abcde").await.unwrap();
955 assert_eq!(writer.size(), 20);
956 writer.sync().await.unwrap();
957
958 let (blob, size) = context.open("partition", b"before_buf").await.unwrap();
960 assert_eq!(size, 20);
961 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(20));
962 let read = reader.read(20).await.unwrap().coalesce();
963 let mut expected = vec![0u8; 20];
964 expected[0..5].copy_from_slice("abcde".as_bytes());
965 expected[10..20].copy_from_slice("0123456789".as_bytes());
966 assert_eq!(read.as_ref(), expected.as_slice());
967
968 writer.write_at(5, b"fghij").await.unwrap();
970 assert_eq!(writer.size(), 20);
971 writer.sync().await.unwrap();
972 assert_eq!(writer.size(), 20);
973
974 let (blob, size) = context.open("partition", b"before_buf").await.unwrap();
976 assert_eq!(size, 20);
977 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(20));
978 let read = reader.read(20).await.unwrap().coalesce();
979 expected[0..10].copy_from_slice("abcdefghij".as_bytes());
980 assert_eq!(read.as_ref(), expected.as_slice());
981 });
982 }
983
984 #[test_traced]
985 fn test_write_resize() {
986 let executor = deterministic::Runner::default();
987 executor.start(|context| async move {
988 let (blob, size) = context.open("partition", b"resize_write").await.unwrap();
990 let mut writer = Write::from_pooler(&context, blob, size, NZUsize!(10));
991
992 writer.write_at(0, b"hello world").await.unwrap();
994 assert_eq!(writer.size(), 11);
995 writer.sync().await.unwrap();
996 assert_eq!(writer.size(), 11);
997
998 let (blob_check, size_check) =
999 context.open("partition", b"resize_write").await.unwrap();
1000 assert_eq!(size_check, 11);
1001 drop(blob_check);
1002
1003 writer.resize(5).await.unwrap();
1005 assert_eq!(writer.size(), 5);
1006 writer.sync().await.unwrap();
1007
1008 let (blob, size) = context.open("partition", b"resize_write").await.unwrap();
1010 assert_eq!(size, 5);
1011 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(5));
1012 let read = reader.read(5).await.unwrap().coalesce();
1013 assert_eq!(read.as_ref(), b"hello");
1014
1015 writer.write_at(0, b"X").await.unwrap();
1017 assert_eq!(writer.size(), 5);
1018 writer.sync().await.unwrap();
1019
1020 let (blob, size) = context.open("partition", b"resize_write").await.unwrap();
1022 assert_eq!(size, 5);
1023 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(5));
1024 let read = reader.read(5).await.unwrap().coalesce();
1025 assert_eq!(read.as_ref(), b"Xello");
1026
1027 writer.resize(10).await.unwrap();
1029 assert_eq!(writer.size(), 10);
1030 writer.sync().await.unwrap();
1031
1032 let (blob, size) = context.open("partition", b"resize_write").await.unwrap();
1034 assert_eq!(size, 10);
1035 let mut reader = Read::from_pooler(&context, blob, size, NZUsize!(10));
1036 let read = reader.read(10).await.unwrap().coalesce();
1037 assert_eq!(&read.as_ref()[0..5], b"Xello");
1038 assert_eq!(&read.as_ref()[5..10], [0u8; 5]);
1039
1040 let (blob_zero, size) = context.open("partition", b"resize_zero").await.unwrap();
1042 let mut writer_zero =
1043 Write::from_pooler(&context, blob_zero.clone(), size, NZUsize!(10));
1044 writer_zero.write_at(0, b"some data").await.unwrap();
1045 assert_eq!(writer_zero.size(), 9);
1046 writer_zero.sync().await.unwrap();
1047 assert_eq!(writer_zero.size(), 9);
1048 writer_zero.resize(0).await.unwrap();
1049 assert_eq!(writer_zero.size(), 0);
1050 writer_zero.sync().await.unwrap();
1051 assert_eq!(writer_zero.size(), 0);
1052
1053 let (_, size_z) = context.open("partition", b"resize_zero").await.unwrap();
1055 assert_eq!(size_z, 0);
1056 });
1057 }
1058
1059 #[test_traced]
1060 fn test_write_read_at_on_writer() {
1061 let executor = deterministic::Runner::default();
1062 executor.start(|context| async move {
1063 let (blob, size) = context.open("partition", b"read_at_writer").await.unwrap();
1065 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(10));
1066
1067 writer.write_at(0, b"buffered").await.unwrap();
1069 assert_eq!(writer.size(), 8);
1070
1071 let read_buf_vec = writer.read_at(0, 4).await.unwrap().coalesce();
1073 assert_eq!(read_buf_vec, b"buff");
1074
1075 let read_buf_vec = writer.read_at(4, 4).await.unwrap().coalesce();
1076 assert_eq!(read_buf_vec, b"ered");
1077
1078 assert!(writer.read_at(8, 1).await.is_err());
1080
1081 writer.write_at(8, b" and flushed").await.unwrap();
1083 assert_eq!(writer.size(), 20);
1084 writer.sync().await.unwrap();
1085 assert_eq!(writer.size(), 20);
1086
1087 let read_buf_vec_2 = writer.read_at(0, 4).await.unwrap().coalesce();
1089 assert_eq!(read_buf_vec_2, b"buff");
1090
1091 let read_buf_7_vec = writer.read_at(13, 7).await.unwrap().coalesce();
1092 assert_eq!(read_buf_7_vec, b"flushed");
1093
1094 writer.write_at(20, b" more data").await.unwrap();
1096 assert_eq!(writer.size(), 30);
1097
1098 let read_buf_vec_3 = writer.read_at(20, 5).await.unwrap().coalesce();
1100 assert_eq!(read_buf_vec_3, b" more");
1101
1102 let combo_read_buf_vec = writer.read_at(16, 12).await.unwrap();
1104 assert_eq!(combo_read_buf_vec.coalesce(), b"shed more da");
1105
1106 writer.sync().await.unwrap();
1108 assert_eq!(writer.size(), 30);
1109 let (final_blob, final_size) =
1110 context.open("partition", b"read_at_writer").await.unwrap();
1111 assert_eq!(final_size, 30);
1112 let mut final_reader =
1113 Read::from_pooler(&context, final_blob, final_size, NZUsize!(30));
1114 let read = final_reader.read(30).await.unwrap().coalesce();
1115 assert_eq!(read.as_ref(), b"buffered and flushed more data");
1116 });
1117 }
1118
1119 #[test_traced]
1120 fn test_write_zero_length_read_past_eof_errors() {
1121 let executor = deterministic::Runner::default();
1122 executor.start(|context| async move {
1123 let (blob, size) = context.open("partition", b"zero_len_probe").await.unwrap();
1124 let mut writer = Write::from_pooler(&context, blob, size, NZUsize!(8));
1125 writer.write_at(0, b"abc").await.unwrap();
1126
1127 let empty = writer.read_at(3, 0).await.unwrap();
1128 assert!(empty.is_empty());
1129
1130 let err = writer.read_at(4, 0).await.unwrap_err();
1131 assert!(matches!(err, Error::BlobInsufficientLength));
1132 });
1133 }
1134
1135 #[test_traced]
1136 fn test_write_straddling_non_mergeable() {
1137 let executor = deterministic::Runner::default();
1138 executor.start(|context| async move {
1139 let (blob, size) = context.open("partition", b"write_straddle").await.unwrap();
1141 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(10));
1142
1143 writer.write_at(0, b"0123456789").await.unwrap();
1145 assert_eq!(writer.size(), 10);
1146
1147 writer.write_at(15, b"abc").await.unwrap();
1149 assert_eq!(writer.size(), 18);
1150 writer.sync().await.unwrap();
1151 assert_eq!(writer.size(), 18);
1152
1153 let (blob_check, size_check) =
1155 context.open("partition", b"write_straddle").await.unwrap();
1156 assert_eq!(size_check, 18);
1157 let mut reader = Read::from_pooler(&context, blob_check, size_check, NZUsize!(20));
1158 let read = reader.read(18).await.unwrap().coalesce();
1159
1160 let mut expected = vec![0u8; 18];
1161 expected[0..10].copy_from_slice(b"0123456789");
1162 expected[15..18].copy_from_slice(b"abc");
1163 assert_eq!(read.as_ref(), expected.as_slice());
1164
1165 let (blob2, size) = context.open("partition", b"write_straddle2").await.unwrap();
1167 let mut writer2 = Write::from_pooler(&context, blob2.clone(), size, NZUsize!(10));
1168 writer2.write_at(0, b"0123456789").await.unwrap();
1169 assert_eq!(writer2.size(), 10);
1170
1171 writer2.write_at(5, b"ABCDEFGHIJKL").await.unwrap();
1173 assert_eq!(writer2.size(), 17);
1174 writer2.sync().await.unwrap();
1175 assert_eq!(writer2.size(), 17);
1176
1177 let (blob_check2, size_check2) =
1179 context.open("partition", b"write_straddle2").await.unwrap();
1180 assert_eq!(size_check2, 17);
1181 let mut reader2 = Read::from_pooler(&context, blob_check2, size_check2, NZUsize!(20));
1182 let read = reader2.read(17).await.unwrap().coalesce();
1183 assert_eq!(read.as_ref(), b"01234ABCDEFGHIJKL");
1184 });
1185 }
1186
1187 #[test_traced]
1188 fn test_write_close() {
1189 let executor = deterministic::Runner::default();
1190 executor.start(|context| async move {
1191 let (blob_orig, size) = context.open("partition", b"write_close").await.unwrap();
1193 let mut writer = Write::from_pooler(&context, blob_orig.clone(), size, NZUsize!(8));
1194 writer.write_at(0, b"pending").await.unwrap();
1195 assert_eq!(writer.size(), 7);
1196
1197 writer.sync().await.unwrap();
1199
1200 let (blob_check, size_check) = context.open("partition", b"write_close").await.unwrap();
1202 assert_eq!(size_check, 7);
1203 let mut reader = Read::from_pooler(&context, blob_check, size_check, NZUsize!(8));
1204 let read = reader.read(7).await.unwrap().coalesce();
1205 assert_eq!(read.as_ref(), b"pending");
1206 });
1207 }
1208
1209 #[test_traced]
1210 fn test_write_direct_due_to_size() {
1211 let executor = deterministic::Runner::default();
1212 executor.start(|context| async move {
1213 let (blob, size) = context
1215 .open("partition", b"write_direct_size")
1216 .await
1217 .unwrap();
1218 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(5));
1219
1220 let data_large = b"0123456789";
1222 writer.write_at(0, data_large).await.unwrap();
1223 assert_eq!(writer.size(), 10);
1224
1225 writer.sync().await.unwrap();
1227
1228 let (blob_check, size_check) = context
1230 .open("partition", b"write_direct_size")
1231 .await
1232 .unwrap();
1233 assert_eq!(size_check, 10);
1234 let mut reader = Read::from_pooler(&context, blob_check, size_check, NZUsize!(10));
1235 let read = reader.read(10).await.unwrap().coalesce();
1236 assert_eq!(read.as_ref(), data_large.as_slice());
1237
1238 writer.write_at(10, b"abc").await.unwrap();
1240 assert_eq!(writer.size(), 13);
1241
1242 let read_small_buf_vec = writer.read_at(10, 3).await.unwrap().coalesce();
1244 assert_eq!(read_small_buf_vec, b"abc");
1245
1246 writer.sync().await.unwrap();
1247
1248 let (blob_check2, size_check2) = context
1250 .open("partition", b"write_direct_size")
1251 .await
1252 .unwrap();
1253 assert_eq!(size_check2, 13);
1254 let mut reader2 = Read::from_pooler(&context, blob_check2, size_check2, NZUsize!(13));
1255 let read = reader2.read(13).await.unwrap().coalesce();
1256 assert_eq!(&read.as_ref()[10..], b"abc".as_slice());
1257 });
1258 }
1259
1260 #[test_traced]
1261 fn test_write_overwrite_and_extend_in_buffer() {
1262 let executor = deterministic::Runner::default();
1263 executor.start(|context| async move {
1264 let (blob, size) = context
1266 .open("partition", b"overwrite_extend_buf")
1267 .await
1268 .unwrap();
1269 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(15));
1270
1271 writer.write_at(0, b"0123456789").await.unwrap();
1273 assert_eq!(writer.size(), 10);
1274
1275 writer.write_at(5, b"ABCDEFGHIJ").await.unwrap();
1277 assert_eq!(writer.size(), 15);
1278
1279 let read_buf_vec = writer.read_at(0, 15).await.unwrap().coalesce();
1281 assert_eq!(read_buf_vec, b"01234ABCDEFGHIJ");
1282
1283 writer.sync().await.unwrap();
1284
1285 let (blob_check, size_check) = context
1287 .open("partition", b"overwrite_extend_buf")
1288 .await
1289 .unwrap();
1290 assert_eq!(size_check, 15);
1291 let mut reader = Read::from_pooler(&context, blob_check, size_check, NZUsize!(15));
1292 let read = reader.read(15).await.unwrap().coalesce();
1293 assert_eq!(read.as_ref(), b"01234ABCDEFGHIJ".as_slice());
1294 });
1295 }
1296
1297 #[test_traced]
1298 fn test_write_at_size() {
1299 let executor = deterministic::Runner::default();
1300 executor.start(|context| async move {
1301 let (blob, size) = context.open("partition", b"write_end").await.unwrap();
1303 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(20));
1304
1305 writer.write_at(0, b"0123456789").await.unwrap();
1307 assert_eq!(writer.size(), 10);
1308 writer.sync().await.unwrap();
1309
1310 writer.write_at(writer.size(), b"abc").await.unwrap();
1312 assert_eq!(writer.size(), 13);
1313 writer.sync().await.unwrap();
1314
1315 let (blob_check, size_check) = context.open("partition", b"write_end").await.unwrap();
1317 assert_eq!(size_check, 13);
1318 let mut reader = Read::from_pooler(&context, blob_check, size_check, NZUsize!(13));
1319 let read = reader.read(13).await.unwrap().coalesce();
1320 assert_eq!(read.as_ref(), b"0123456789abc");
1321 });
1322 }
1323
1324 #[test_traced]
1325 fn test_write_at_size_multiple_appends() {
1326 let executor = deterministic::Runner::default();
1327 executor.start(|context| async move {
1328 let (blob, size) = context
1330 .open("partition", b"write_multiple_appends_at_size")
1331 .await
1332 .unwrap();
1333 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(5));
1334
1335 writer.write_at(0, b"AAA").await.unwrap();
1337 assert_eq!(writer.size(), 3);
1338 writer.sync().await.unwrap();
1339 assert_eq!(writer.size(), 3);
1340
1341 writer.write_at(writer.size(), b"BBB").await.unwrap();
1343 assert_eq!(writer.size(), 6); writer.sync().await.unwrap();
1345 assert_eq!(writer.size(), 6);
1346
1347 writer.write_at(writer.size(), b"CCC").await.unwrap();
1349 assert_eq!(writer.size(), 9); writer.sync().await.unwrap();
1351 assert_eq!(writer.size(), 9);
1352
1353 let (blob_check, size_check) = context
1355 .open("partition", b"write_multiple_appends_at_size")
1356 .await
1357 .unwrap();
1358 assert_eq!(size_check, 9);
1359 let mut reader = Read::from_pooler(&context, blob_check, size_check, NZUsize!(9));
1360 let read = reader.read(9).await.unwrap().coalesce();
1361 assert_eq!(read.as_ref(), b"AAABBBCCC");
1362 });
1363 }
1364
1365 #[test_traced]
1366 fn test_write_non_contiguous_then_append_at_size() {
1367 let executor = deterministic::Runner::default();
1368 executor.start(|context| async move {
1369 let (blob, size) = context
1371 .open("partition", b"write_non_contiguous_then_append")
1372 .await
1373 .unwrap();
1374 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(10));
1375
1376 writer.write_at(0, b"INITIAL").await.unwrap(); assert_eq!(writer.size(), 7);
1379 writer.write_at(20, b"NONCONTIG").await.unwrap();
1383 assert_eq!(writer.size(), 29);
1384 writer.sync().await.unwrap();
1385 assert_eq!(writer.size(), 29);
1386
1387 writer.write_at(writer.size(), b"APPEND").await.unwrap();
1389 assert_eq!(writer.size(), 35); writer.sync().await.unwrap();
1391 assert_eq!(writer.size(), 35);
1392
1393 let (blob_check, size_check) = context
1395 .open("partition", b"write_non_contiguous_then_append")
1396 .await
1397 .unwrap();
1398 assert_eq!(size_check, 35);
1399 let mut reader = Read::from_pooler(&context, blob_check, size_check, NZUsize!(35));
1400 let read = reader.read(35).await.unwrap().coalesce();
1401
1402 let mut expected = vec![0u8; 35];
1403 expected[0..7].copy_from_slice(b"INITIAL");
1404 expected[20..29].copy_from_slice(b"NONCONTIG");
1405 expected[29..35].copy_from_slice(b"APPEND");
1406 assert_eq!(read.as_ref(), expected.as_slice());
1407 });
1408 }
1409
1410 #[test_traced]
1411 fn test_write_resize_then_append_at_size() {
1412 let executor = deterministic::Runner::default();
1413 executor.start(|context| async move {
1414 let (blob, size) = context
1416 .open("partition", b"resize_then_append_at_size")
1417 .await
1418 .unwrap();
1419 let mut writer = Write::from_pooler(&context, blob.clone(), size, NZUsize!(10));
1420
1421 writer.write_at(0, b"0123456789ABCDEF").await.unwrap(); assert_eq!(writer.size(), 16);
1424 writer.sync().await.unwrap(); assert_eq!(writer.size(), 16);
1426
1427 let resize_to = 5;
1429 writer.resize(resize_to).await.unwrap();
1430 assert_eq!(writer.size(), resize_to);
1433 writer.sync().await.unwrap(); assert_eq!(writer.size(), resize_to);
1435
1436 writer.write_at(writer.size(), b"XXXXX").await.unwrap(); assert_eq!(writer.size(), 10); writer.sync().await.unwrap();
1441 assert_eq!(writer.size(), 10);
1442
1443 let (blob_check, size_check) = context
1445 .open("partition", b"resize_then_append_at_size")
1446 .await
1447 .unwrap();
1448 assert_eq!(size_check, 10);
1449 let mut reader = Read::from_pooler(&context, blob_check, size_check, NZUsize!(10));
1450 let read = reader.read(10).await.unwrap().coalesce();
1451 assert_eq!(read.as_ref(), b"01234XXXXX");
1452 });
1453 }
1454
1455 #[test_traced]
1457 fn test_write_start_sync_persists_and_marks_clean() {
1458 let executor = deterministic::Runner::default();
1459 executor.start(|context| async move {
1460 let blob = SyncTrackingBlob::new();
1461 let mut writer = Write::from_pooler(&context, blob.clone(), 0, NZUsize!(8));
1462
1463 writer.write_at(0, b"abc").await.unwrap();
1465 let handle = writer.start_sync().await;
1466 handle.await.unwrap();
1467
1468 let (durable, writes, full_syncs, range_syncs) = blob.snapshot();
1470 assert_eq!(durable.as_slice(), b"abc");
1471 assert_eq!(writes, 1);
1472 assert_eq!(full_syncs, 1);
1473 assert_eq!(range_syncs, 0);
1474
1475 writer.write_at(3, b"d").await.unwrap();
1478 writer.sync().await.unwrap();
1479 let (durable, writes, full_syncs, range_syncs) = blob.snapshot();
1480 assert_eq!(durable.as_slice(), b"abcd");
1481 assert_eq!(writes, 2);
1482 assert_eq!(full_syncs, 1);
1483 assert_eq!(range_syncs, 1);
1484
1485 let handle = writer.start_sync().await;
1487 handle.await.unwrap();
1488 let (_, _, full_syncs, range_syncs) = blob.snapshot();
1489 assert_eq!(full_syncs, 1);
1490 assert_eq!(range_syncs, 1);
1491 });
1492 }
1493
1494 #[test_traced]
1496 fn test_write_sync_waits_for_outstanding_start_sync() {
1497 let executor = deterministic::Runner::default();
1498 executor.start(|context| async move {
1499 let inner = SyncTrackingBlob::new();
1500 let (blob, pending) = DelayedSyncBlob::new(inner.clone());
1501 let mut writer = Write::from_pooler(&context, blob, 0, NZUsize!(8));
1502
1503 let handle = writer.start_sync().await;
1505 let deferred = next_pending_sync(&pending);
1506
1507 let mut sync = Box::pin(writer.sync());
1509 assert!(
1510 sync.as_mut().now_or_never().is_none(),
1511 "sync must wait for the outstanding start_sync handle"
1512 );
1513 deferred
1514 .blocked
1515 .await
1516 .expect("sync never waited on start_sync");
1517
1518 let (_, _, full_syncs, range_syncs) = inner.snapshot();
1519 assert_eq!(full_syncs, 0);
1520 assert_eq!(range_syncs, 0);
1521
1522 deferred.release.send(Ok(())).unwrap();
1524 sync.await.unwrap();
1525 handle.await.unwrap();
1526 let (_, _, full_syncs, range_syncs) = inner.snapshot();
1527 assert_eq!(full_syncs, 1);
1528 assert_eq!(range_syncs, 0);
1529 });
1530 }
1531
1532 #[test_traced]
1534 fn test_write_sync_after_start_sync_and_small_write_waits_before_range_sync() {
1535 let executor = deterministic::Runner::default();
1536 executor.start(|context| async move {
1537 let inner = SyncTrackingBlob::new();
1538 let (blob, pending) = DelayedSyncBlob::new(inner.clone());
1539 let mut writer = Write::from_pooler(&context, blob, 0, NZUsize!(8));
1540
1541 let handle = writer.start_sync().await;
1543 let deferred = next_pending_sync(&pending);
1544
1545 writer.write_at(0, b"abc").await.unwrap();
1547 let mut sync = Box::pin(writer.sync());
1548 assert!(
1549 sync.as_mut().now_or_never().is_none(),
1550 "sync must wait for the outstanding start_sync before flushing the small write"
1551 );
1552 deferred
1553 .blocked
1554 .await
1555 .expect("sync never waited on start_sync");
1556
1557 let (_, writes, full_syncs, range_syncs) = inner.snapshot();
1558 assert_eq!(writes, 0);
1559 assert_eq!(full_syncs, 0);
1560 assert_eq!(range_syncs, 0);
1561
1562 deferred.release.send(Ok(())).unwrap();
1564 sync.await.unwrap();
1565 handle.await.unwrap();
1566
1567 let (durable, writes, full_syncs, range_syncs) = inner.snapshot();
1568 assert_eq!(durable.as_slice(), b"abc");
1569 assert_eq!(writes, 1);
1570 assert_eq!(full_syncs, 1);
1571 assert_eq!(range_syncs, 1);
1572 });
1573 }
1574
1575 #[test_traced]
1577 fn test_write_at_overlap_flush_waits_for_outstanding_start_sync() {
1578 let executor = deterministic::Runner::default();
1579 executor.start(|context| async move {
1580 let inner = SyncTrackingBlob::new();
1581 inner.write_at(0, b"xxx").await.unwrap();
1582
1583 let (blob, pending) = DelayedSyncBlob::new(inner.clone());
1584 let mut writer = Write::from_pooler(&context, blob, inner.size(), NZUsize!(8));
1585
1586 let handle = writer.start_sync().await;
1587 let deferred = next_pending_sync(&pending);
1588
1589 writer.write_at(3, b"abc").await.unwrap();
1591
1592 let mut write = Box::pin(writer.write_at(2, b"ZZ"));
1594 assert!(
1595 write.as_mut().now_or_never().is_none(),
1596 "overlapping write must wait for the outstanding start_sync before flushing"
1597 );
1598 deferred
1599 .blocked
1600 .await
1601 .expect("write never waited on start_sync");
1602
1603 let (_, writes, full_syncs, range_syncs) = inner.snapshot();
1604 assert_eq!(writes, 1);
1605 assert_eq!(full_syncs, 0);
1606 assert_eq!(range_syncs, 0);
1607
1608 deferred.release.send(Ok(())).unwrap();
1610 write.await.unwrap();
1611 handle.await.unwrap();
1612
1613 let (_, writes, full_syncs, range_syncs) = inner.snapshot();
1614 assert_eq!(writes, 3);
1615 assert_eq!(full_syncs, 1);
1616 assert_eq!(range_syncs, 0);
1617 });
1618 }
1619
1620 #[test_traced]
1622 fn test_write_resize_waits_for_outstanding_start_sync_before_resizing() {
1623 let executor = deterministic::Runner::default();
1624 executor.start(|context| async move {
1625 let inner = SyncTrackingBlob::new();
1626 inner.write_at(0, b"abcdef").await.unwrap();
1627
1628 let (blob, pending) = DelayedSyncBlob::new(inner.clone());
1629 let mut writer = Write::from_pooler(&context, blob, inner.size(), NZUsize!(8));
1630
1631 let handle = writer.start_sync().await;
1632 let deferred = next_pending_sync(&pending);
1633 let original_size = inner.size();
1634
1635 let mut resize = Box::pin(writer.resize(3));
1637 assert!(
1638 resize.as_mut().now_or_never().is_none(),
1639 "resize must wait for the outstanding start_sync handle"
1640 );
1641 deferred
1642 .blocked
1643 .await
1644 .expect("resize never waited on start_sync");
1645 assert_eq!(
1646 inner.size(),
1647 original_size,
1648 "resize must not mutate the blob before the pending sync finishes"
1649 );
1650
1651 deferred.release.send(Ok(())).unwrap();
1653 resize.await.unwrap();
1654 handle.await.unwrap();
1655 assert_eq!(writer.size(), 3);
1656 assert_eq!(inner.size(), 3);
1657 });
1658 }
1659
1660 #[test_traced]
1661 fn test_write_sync_uses_range_sync_for_buffer_only_write() {
1662 let executor = deterministic::Runner::default();
1663 executor.start(|context| async move {
1664 let blob = SyncTrackingBlob::new();
1665 let mut writer = Write::from_pooler(&context, blob.clone(), 0, NZUsize!(8));
1666
1667 writer.sync().await.unwrap();
1669 let (durable, writes, full_syncs, range_syncs) = blob.snapshot();
1670 assert!(durable.is_empty());
1671 assert_eq!(writes, 0);
1672 assert_eq!(full_syncs, 1);
1673 assert_eq!(range_syncs, 0);
1674
1675 writer.write_at(0, b"abc").await.unwrap();
1677 writer.sync().await.unwrap();
1678
1679 let (durable, writes, full_syncs, range_syncs) = blob.snapshot();
1681 assert_eq!(durable.as_slice(), b"abc");
1682 assert_eq!(writes, 1);
1683 assert_eq!(full_syncs, 1);
1684 assert_eq!(range_syncs, 1);
1685
1686 writer.sync().await.unwrap();
1688 let (durable, writes, full_syncs, range_syncs) = blob.snapshot();
1689 assert_eq!(durable.as_slice(), b"abc");
1690 assert_eq!(writes, 1);
1691 assert_eq!(full_syncs, 1);
1692 assert_eq!(range_syncs, 1);
1693 });
1694 }
1695
1696 #[test_traced]
1697 fn test_write_sync_persists_pre_wrapped_blob_mutation() {
1698 let executor = deterministic::Runner::default();
1699 executor.start(|context| async move {
1700 let blob = SyncTrackingBlob::new();
1701
1702 blob.write_at(0, b"abc").await.unwrap();
1704
1705 let mut writer = Write::from_pooler(&context, blob.clone(), 3, NZUsize!(8));
1706 writer.sync().await.unwrap();
1707
1708 let (durable, writes, full_syncs, range_syncs) = blob.snapshot();
1710 assert_eq!(durable.as_slice(), b"abc");
1711 assert_eq!(writes, 1);
1712 assert_eq!(full_syncs, 1);
1713 assert_eq!(range_syncs, 0);
1714
1715 writer.write_at(3, b"d").await.unwrap();
1717 writer.sync().await.unwrap();
1718
1719 let (durable, writes, full_syncs, range_syncs) = blob.snapshot();
1720 assert_eq!(durable.as_slice(), b"abcd");
1721 assert_eq!(writes, 2);
1722 assert_eq!(full_syncs, 1);
1723 assert_eq!(range_syncs, 1);
1724 });
1725 }
1726
1727 #[test_traced]
1728 fn test_write_sync_failed_range_sync_does_not_mark_clean() {
1729 let executor = deterministic::Runner::default();
1730 executor.start(|context| async move {
1731 let name = b"failed_range_sync";
1732 let (blob, size) = context.open("partition", name).await.unwrap();
1733 let mut writer = Write::from_pooler(&context, blob, size, NZUsize!(8));
1734 writer.sync().await.unwrap();
1735
1736 writer.write_at(0, b"abc").await.unwrap();
1738
1739 context.remove("partition", Some(name)).await.unwrap();
1741 assert!(writer.sync().await.is_err());
1742
1743 assert!(writer.sync().await.is_err());
1746 });
1747 }
1748
1749 #[test_traced]
1750 fn test_write_sync_persists_prior_direct_flushes_with_buffered_tip() {
1751 let executor = deterministic::Runner::default();
1752 executor.start(|context| async move {
1753 let blob = SyncTrackingBlob::new();
1754 let mut writer = Write::from_pooler(&context, blob.clone(), 0, NZUsize!(4));
1755
1756 writer.write_at(0, b"abcdef").await.unwrap();
1758 writer.write_at(6, b"g").await.unwrap();
1759 writer.sync().await.unwrap();
1760
1761 let (durable, writes, full_syncs, range_syncs) = blob.snapshot();
1763 assert_eq!(durable.as_slice(), b"abcdefg");
1764 assert_eq!(writes, 2);
1765 assert_eq!(full_syncs, 1);
1766 assert_eq!(range_syncs, 0);
1767
1768 writer.sync().await.unwrap();
1770 let (durable, writes, full_syncs, range_syncs) = blob.snapshot();
1771 assert_eq!(durable.as_slice(), b"abcdefg");
1772 assert_eq!(writes, 2);
1773 assert_eq!(full_syncs, 1);
1774 assert_eq!(range_syncs, 0);
1775
1776 writer.write_at(7, b"h").await.unwrap();
1778 writer.sync().await.unwrap();
1779
1780 let (durable, writes, full_syncs, range_syncs) = blob.snapshot();
1781 assert_eq!(durable.as_slice(), b"abcdefgh");
1782 assert_eq!(writes, 3);
1783 assert_eq!(full_syncs, 1);
1784 assert_eq!(range_syncs, 1);
1785 });
1786 }
1787
1788 #[test_traced]
1789 fn test_write_sync_uses_full_sync_after_resize() {
1790 let executor = deterministic::Runner::default();
1791 executor.start(|context| async move {
1792 let blob = SyncTrackingBlob::new();
1793 let mut writer = Write::from_pooler(&context, blob.clone(), 0, NZUsize!(8));
1794 writer.sync().await.unwrap();
1795
1796 writer.write_at(0, b"abcdef").await.unwrap();
1798 writer.sync().await.unwrap();
1799
1800 writer.resize(4).await.unwrap();
1802 writer.sync().await.unwrap();
1803
1804 let (durable, writes, full_syncs, range_syncs) = blob.snapshot();
1806 assert_eq!(durable.as_slice(), b"abcd");
1807 assert_eq!(writes, 1);
1808 assert_eq!(full_syncs, 2);
1809 assert_eq!(range_syncs, 1);
1810 });
1811 }
1812}