1use crate::error::{Error, Result};
5#[cfg(feature = "test-hooks")]
6use crate::kv::hooks::{CrashOperation, CrashSimulator, CrashTiming, IoHooks};
7use crate::kv::{KVStore, KVTransaction, OwnedKVScan, OwnedKVStore, OwnedKVTransaction};
8use crate::log::wal::{WalReader, WalRecord, WalWriter};
9use crate::storage::flush::write_empty_vector_segment;
10use crate::storage::sstable::{SstableReader, SstableWriter};
11use crate::txn::TxnManager;
12use crate::types::{Key, TxnId, TxnMode, TxnState, Value};
13use std::collections::{BTreeMap, HashMap};
14use std::ops::Bound::{Excluded, Included, Unbounded};
15use std::path::{Path, PathBuf};
16use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
17use std::sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard};
18use tracing::warn;
19
20#[derive(Debug, Clone, Default, PartialEq)]
22pub struct MemoryStats {
23 pub total_bytes: usize,
25 pub kv_bytes: usize,
27 pub index_bytes: usize,
29}
30
31#[derive(Clone)]
33pub struct MemoryKV {
34 manager: Arc<MemoryTxnManager>,
35}
36
37impl MemoryKV {
38 pub fn new() -> Self {
40 Self {
41 manager: Arc::new(MemoryTxnManager::new(None, None, None)),
42 }
43 }
44
45 pub fn memory_stats(&self) -> MemoryStats {
47 self.manager.memory_stats()
48 }
49
50 pub fn new_with_limit(limit: Option<usize>) -> Self {
52 Self {
53 manager: Arc::new(MemoryTxnManager::new_with_limit(limit)),
54 }
55 }
56
57 pub fn open(path: &Path) -> Result<Self> {
59 let wal_writer = WalWriter::new(path)?;
60 let sstable_path = path.with_extension("sst");
61 let manager = Arc::new(MemoryTxnManager::new(
62 Some(wal_writer),
63 Some(path.to_path_buf()),
64 Some(sstable_path),
65 ));
66 manager.recover()?;
67 Ok(Self { manager })
68 }
69
70 #[cfg(feature = "test-hooks")]
72 pub fn open_with_io_hooks(path: &Path, hooks: Arc<dyn IoHooks>) -> Result<Self> {
73 let wal_writer = WalWriter::new(path)?;
74 let sstable_path = path.with_extension("sst");
75 let manager = Arc::new(MemoryTxnManager::new(
76 Some(wal_writer),
77 Some(path.to_path_buf()),
78 Some(sstable_path),
79 ));
80 manager.set_io_hooks(Some(hooks));
81 manager.recover()?;
82 Ok(Self { manager })
83 }
84
85 #[cfg(feature = "test-hooks")]
87 pub fn open_with_crash_hooks(path: &Path, crash_sim: Arc<CrashSimulator>) -> Result<Self> {
88 let wal_writer = WalWriter::new(path)?;
89 let sstable_path = path.with_extension("sst");
90 let manager = Arc::new(MemoryTxnManager::new(
91 Some(wal_writer),
92 Some(path.to_path_buf()),
93 Some(sstable_path),
94 ));
95 manager.set_crash_sim(Some(crash_sim));
96 manager.recover()?;
97 Ok(Self { manager })
98 }
99
100 pub fn flush(&self) -> Result<()> {
102 self.manager.flush()
103 }
104}
105
106impl Default for MemoryKV {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112impl KVStore for MemoryKV {
113 type Transaction<'a> = MemoryTransaction<'a>;
114 type Manager<'a> = &'a MemoryTxnManager;
115
116 fn txn_manager(&self) -> Self::Manager<'_> {
117 &self.manager
118 }
119
120 fn begin(&self, mode: TxnMode) -> Result<Self::Transaction<'_>> {
121 self.manager.begin_internal(mode)
122 }
123
124 fn runtime_stats(&self) -> Option<crate::kv::RuntimeStats> {
125 Some(crate::kv::RuntimeStats::Memory(self.memory_stats()))
126 }
127
128 fn set_memory_limit_bytes(&self, limit: Option<usize>) -> Result<()> {
129 self.manager.set_memory_limit(limit);
130 Ok(())
131 }
132}
133
134impl OwnedKVStore for MemoryKV {
135 fn begin_owned_kv_transaction(
136 self: Arc<Self>,
137 mode: TxnMode,
138 ) -> Result<Box<dyn OwnedKVTransaction>> {
139 Ok(Box::new(OwnedMemoryTransaction::new(
140 self.manager.clone(),
141 mode,
142 )))
143 }
144}
145
146type VersionedValue = (Value, u64);
148
149struct MemorySharedState {
151 data: RwLock<BTreeMap<Key, VersionedValue>>,
153 next_txn_id: AtomicU64,
155 commit_version: AtomicU64,
157 wal_writer: Option<RwLock<WalWriter>>,
159 wal_path: Option<PathBuf>,
161 sstable: RwLock<Option<SstableReader>>,
163 sstable_path: Option<PathBuf>,
165 memory_limit: RwLock<Option<usize>>,
167 current_memory: AtomicUsize,
169 owned_snapshot_gate: Arc<OwnedSnapshotGate>,
172 #[cfg(feature = "test-hooks")]
173 io_hooks: RwLock<Option<Arc<dyn IoHooks>>>,
175 #[cfg(feature = "test-hooks")]
176 crash_sim: RwLock<Option<Arc<CrashSimulator>>>,
178}
179
180#[derive(Default)]
181struct OwnedSnapshotGate {
182 state: Mutex<OwnedSnapshotGateState>,
183 changed: Condvar,
184}
185
186#[derive(Default)]
187struct OwnedSnapshotGateState {
188 readers: usize,
189 writer: bool,
190}
191
192impl OwnedSnapshotGate {
193 fn acquire_reader(self: &Arc<Self>) -> OwnedSnapshotReader {
194 let mut state = self
195 .state
196 .lock()
197 .expect("owned snapshot gate mutex poisoned");
198 while state.writer {
199 state = self
200 .changed
201 .wait(state)
202 .expect("owned snapshot gate mutex poisoned");
203 }
204 state.readers = state.readers.saturating_add(1);
205 OwnedSnapshotReader {
206 gate: self.clone(),
207 released: false,
208 }
209 }
210
211 fn acquire_writer(self: &Arc<Self>) -> OwnedSnapshotWriter {
212 let mut state = self
213 .state
214 .lock()
215 .expect("owned snapshot gate mutex poisoned");
216 while state.writer || state.readers != 0 {
217 state = self
218 .changed
219 .wait(state)
220 .expect("owned snapshot gate mutex poisoned");
221 }
222 state.writer = true;
223 OwnedSnapshotWriter {
224 gate: self.clone(),
225 released: false,
226 }
227 }
228}
229
230struct OwnedSnapshotReader {
231 gate: Arc<OwnedSnapshotGate>,
232 released: bool,
233}
234
235impl Drop for OwnedSnapshotReader {
236 fn drop(&mut self) {
237 if !self.released {
238 let mut state = self
239 .gate
240 .state
241 .lock()
242 .expect("owned snapshot gate mutex poisoned");
243 state.readers = state.readers.saturating_sub(1);
244 self.released = true;
245 self.gate.changed.notify_all();
246 }
247 }
248}
249
250struct OwnedSnapshotWriter {
251 gate: Arc<OwnedSnapshotGate>,
252 released: bool,
253}
254
255impl Drop for OwnedSnapshotWriter {
256 fn drop(&mut self) {
257 if !self.released {
258 let mut state = self
259 .gate
260 .state
261 .lock()
262 .expect("owned snapshot gate mutex poisoned");
263 state.writer = false;
264 self.released = true;
265 self.gate.changed.notify_all();
266 }
267 }
268}
269
270impl MemorySharedState {
271 fn check_memory_limit(&self, additional: usize) -> Result<()> {
273 if let Some(limit) = *self.memory_limit.read().unwrap() {
274 let current = self.current_memory.load(Ordering::Relaxed);
275 let requested = current.saturating_add(additional);
276 if requested > limit {
277 return Err(Error::MemoryLimitExceeded { limit, requested });
278 }
279 }
280 Ok(())
281 }
282
283 fn memory_stats(&self) -> MemoryStats {
285 let kv_bytes = self.current_memory.load(Ordering::Relaxed);
286 MemoryStats {
287 total_bytes: kv_bytes,
288 kv_bytes,
289 index_bytes: 0,
290 }
291 }
292
293 fn recompute_current_memory(&self) {
295 let data = self.data.read().unwrap();
296 let mut total = 0usize;
297 for (k, (v, _)) in data.iter() {
298 total = total.saturating_add(k.len() + v.len());
299 }
300 self.current_memory.store(total, Ordering::Relaxed);
301 }
302}
303
304pub struct MemoryTxnManager {
306 state: Arc<MemorySharedState>,
307}
308
309impl MemoryTxnManager {
310 fn new_with_params(
311 wal_writer: Option<WalWriter>,
312 wal_path: Option<PathBuf>,
313 sstable_path: Option<PathBuf>,
314 memory_limit: Option<usize>,
315 ) -> Self {
316 Self {
317 state: Arc::new(MemorySharedState {
318 data: RwLock::new(BTreeMap::new()),
319 next_txn_id: AtomicU64::new(1),
320 commit_version: AtomicU64::new(0),
321 wal_writer: wal_writer.map(RwLock::new),
322 wal_path,
323 sstable: RwLock::new(None),
324 sstable_path,
325 memory_limit: RwLock::new(memory_limit),
326 current_memory: AtomicUsize::new(0),
327 owned_snapshot_gate: Arc::new(OwnedSnapshotGate::default()),
328 #[cfg(feature = "test-hooks")]
329 io_hooks: RwLock::new(None),
330 #[cfg(feature = "test-hooks")]
331 crash_sim: RwLock::new(None),
332 }),
333 }
334 }
335
336 fn new(
337 wal_writer: Option<WalWriter>,
338 wal_path: Option<PathBuf>,
339 sstable_path: Option<PathBuf>,
340 ) -> Self {
341 Self::new_with_params(wal_writer, wal_path, sstable_path, None)
342 }
343
344 pub fn new_with_limit(limit: Option<usize>) -> Self {
346 Self::new_with_params(None, None, None, limit)
347 }
348
349 #[cfg(feature = "test-hooks")]
350 fn set_io_hooks(&self, hooks: Option<Arc<dyn IoHooks>>) {
351 let mut guard = self.state.io_hooks.write().unwrap();
352 *guard = hooks;
353 }
354
355 #[cfg(feature = "test-hooks")]
356 fn set_crash_sim(&self, crash_sim: Option<Arc<CrashSimulator>>) {
357 let mut guard = self.state.crash_sim.write().unwrap();
358 *guard = crash_sim;
359 }
360
361 #[cfg(feature = "test-hooks")]
362 fn io_hooks(&self) -> Option<Arc<dyn IoHooks>> {
363 self.state.io_hooks.read().unwrap().clone()
364 }
365
366 #[cfg(feature = "test-hooks")]
367 fn crash_sim(&self) -> Option<Arc<CrashSimulator>> {
368 self.state.crash_sim.read().unwrap().clone()
369 }
370
371 pub fn memory_stats(&self) -> MemoryStats {
373 self.state.memory_stats()
374 }
375
376 pub fn set_memory_limit(&self, limit: Option<usize>) {
378 let mut guard = self.state.memory_limit.write().unwrap();
379 *guard = limit;
380 }
381
382 pub fn snapshot(&self) -> Vec<(Key, Value)> {
384 let data = self.state.data.read().unwrap();
385 data.iter()
386 .map(|(k, (v, _))| (k.clone(), v.clone()))
387 .collect()
388 }
389
390 pub fn clear_all(&self) {
392 let _snapshot_writer = self.state.owned_snapshot_gate.acquire_writer();
393 let mut data = self.state.data.write().unwrap();
394 data.clear();
395 drop(data);
396 self.state.current_memory.store(0, Ordering::Relaxed);
397 self.state.commit_version.store(0, Ordering::Relaxed);
398 }
399
400 pub fn compact_with_limit<F>(
403 &self,
404 input_bytes: usize,
405 output_bytes: usize,
406 run: F,
407 ) -> Result<bool>
408 where
409 F: FnOnce() -> Result<()>,
410 {
411 if let Some(limit) = *self.state.memory_limit.read().unwrap() {
412 let current = self.state.current_memory.load(Ordering::Relaxed);
413 let prospective = current
415 .saturating_sub(input_bytes)
416 .saturating_add(output_bytes);
417 if prospective > limit {
418 warn!(
419 limit,
420 requested = prospective,
421 "compaction skipped due to memory limit"
422 );
423 return Ok(false);
424 }
425 }
426
427 run()?;
428
429 let current = self.state.current_memory.load(Ordering::Relaxed);
431 let new_usage = current
432 .saturating_sub(input_bytes)
433 .saturating_add(output_bytes);
434 self.state
435 .current_memory
436 .store(new_usage, Ordering::Relaxed);
437 Ok(true)
438 }
439
440 #[cfg(feature = "test-hooks")]
441 fn trigger_crash(&self, operation: CrashOperation, timing: CrashTiming) {
442 if let Some(sim) = self.crash_sim() {
443 sim.check_crash(operation, timing);
444 }
445 }
446
447 #[cfg(feature = "test-hooks")]
448 fn notify_wal_hooks(&self, data: &[u8], timing: CrashTiming) -> Result<()> {
449 match timing {
450 CrashTiming::Before => {
451 self.trigger_crash(CrashOperation::WalWrite, CrashTiming::Before);
452 if let Some(hooks) = self.io_hooks() {
453 hooks.before_wal_write(data).map_err(Error::Io)?;
454 hooks.before_fsync().map_err(Error::Io)?;
455 }
456 self.trigger_crash(CrashOperation::WalFsync, CrashTiming::Before);
457 }
458 CrashTiming::During => {
459 self.trigger_crash(CrashOperation::WalWrite, CrashTiming::During);
460 self.trigger_crash(CrashOperation::WalFsync, CrashTiming::During);
461 }
462 CrashTiming::After => {
463 self.trigger_crash(CrashOperation::WalWrite, CrashTiming::After);
464 self.trigger_crash(CrashOperation::WalFsync, CrashTiming::After);
465 if let Some(hooks) = self.io_hooks() {
466 hooks.after_wal_write(data).map_err(Error::Io)?;
467 hooks.after_fsync().map_err(Error::Io)?;
468 }
469 }
470 }
471 Ok(())
472 }
473
474 #[cfg(feature = "test-hooks")]
475 fn notify_compaction(&self, timing: CrashTiming) {
476 self.trigger_crash(CrashOperation::Compaction, timing);
477 if let Some(hooks) = self.io_hooks() {
478 match timing {
479 CrashTiming::Before => hooks.on_compaction_start(),
480 CrashTiming::After => hooks.on_compaction_end(),
481 CrashTiming::During => {}
482 }
483 }
484 }
485
486 fn append_wal_record(&self, wal: &mut WalWriter, record: &WalRecord) -> Result<()> {
487 #[cfg(feature = "test-hooks")]
488 {
489 let data =
490 bincode::serialize(record).map_err(|e| Error::Io(std::io::Error::other(e)))?;
491 self.notify_wal_hooks(&data, CrashTiming::Before)?;
492 self.notify_wal_hooks(&data, CrashTiming::During)?;
493 wal.append(record)?;
494 self.notify_wal_hooks(&data, CrashTiming::After)?;
495 Ok(())
496 }
497
498 #[cfg(not(feature = "test-hooks"))]
499 {
500 wal.append(record)
501 }
502 }
503
504 fn write_wal(&self, txn_id: TxnId, writes: &BTreeMap<Key, Option<Value>>) -> Result<()> {
505 if let Some(wal_lock) = &self.state.wal_writer {
506 let mut wal = wal_lock.write().unwrap();
507 self.append_wal_record(&mut wal, &WalRecord::Begin(txn_id))?;
508 for (key, value) in writes {
509 let record = match value {
510 Some(v) => WalRecord::Put(txn_id, key.clone(), v.clone()),
511 None => WalRecord::Delete(txn_id, key.clone()),
512 };
513 self.append_wal_record(&mut wal, &record)?;
514 }
515 self.append_wal_record(&mut wal, &WalRecord::Commit(txn_id))?;
516 wal.sync()?;
520 }
521 Ok(())
522 }
523
524 pub fn compact_in_memory(&self) -> Result<bool> {
526 #[cfg(feature = "test-hooks")]
527 self.notify_compaction(CrashTiming::Before);
528
529 let snapshot_bytes = {
530 let data = self.state.data.read().unwrap();
531 let mut bytes = 0usize;
532 for (k, (v, _)) in data.iter() {
533 bytes = bytes.saturating_add(k.len() + v.len());
534 }
535 bytes
536 };
537
538 let executed = self.compact_with_limit(snapshot_bytes, snapshot_bytes, || {
539 let data = self.state.data.read().unwrap();
540 let mut rebuilt = BTreeMap::new();
541 for (k, (v, version)) in data.iter() {
542 rebuilt.insert(k.clone(), (v.clone(), *version));
543 }
544 drop(data);
545
546 #[cfg(feature = "test-hooks")]
547 self.notify_compaction(CrashTiming::During);
548
549 let _snapshot_writer = self.state.owned_snapshot_gate.acquire_writer();
550 let mut write_guard = self.state.data.write().unwrap();
551 *write_guard = rebuilt;
552 Ok(())
553 })?;
554
555 #[cfg(feature = "test-hooks")]
556 self.notify_compaction(CrashTiming::After);
557
558 Ok(executed)
559 }
560
561 pub fn flush(&self) -> Result<()> {
563 let Some(path) = self.state.sstable_path.as_ref() else {
564 return Ok(());
565 };
566
567 #[cfg(feature = "test-hooks")]
568 self.notify_compaction(CrashTiming::Before);
569
570 let data = self.state.data.read().unwrap();
571 let mut writer = SstableWriter::create(path)?;
572 for (key, (value, _version)) in data.iter() {
573 #[cfg(feature = "test-hooks")]
574 {
575 let mut record = Vec::with_capacity(key.len() + value.len());
576 record.extend_from_slice(key);
577 record.extend_from_slice(value);
578 self.trigger_crash(CrashOperation::SstWrite, CrashTiming::Before);
579 if let Some(hooks) = self.io_hooks() {
580 hooks.before_sst_write(&record).map_err(Error::Io)?;
581 }
582 self.trigger_crash(CrashOperation::SstWrite, CrashTiming::During);
583 }
584
585 writer.append(key, value)?;
586
587 #[cfg(feature = "test-hooks")]
588 self.trigger_crash(CrashOperation::SstWrite, CrashTiming::After);
589 }
590 drop(data);
591
592 #[cfg(feature = "test-hooks")]
593 self.trigger_crash(CrashOperation::SstFinalize, CrashTiming::Before);
594 let _footer = writer.finish()?;
595 #[cfg(feature = "test-hooks")]
596 self.trigger_crash(CrashOperation::SstFinalize, CrashTiming::After);
597 let reader = SstableReader::open(path)?;
598 let vec_path = path.with_extension("vec");
600 write_empty_vector_segment(&vec_path)?;
601
602 let mut slot = self.state.sstable.write().unwrap();
603 *slot = Some(reader);
604
605 #[cfg(feature = "test-hooks")]
606 self.notify_compaction(CrashTiming::After);
607 Ok(())
608 }
609
610 fn replay(&self) -> Result<()> {
612 let path = match &self.state.wal_path {
613 Some(p) => p,
614 None => return Ok(()),
615 };
616 if !path.exists() || std::fs::metadata(path)?.len() == 0 {
617 return Ok(());
618 }
619
620 let _snapshot_writer = self.state.owned_snapshot_gate.acquire_writer();
621 let mut data = self.state.data.write().unwrap();
622 let mut max_txn_id = 0;
623 let mut max_version = self.state.commit_version.load(Ordering::Acquire);
624 let reader = WalReader::new(path)?;
625 let mut pending_txns: HashMap<TxnId, Vec<(Key, Option<Value>)>> = HashMap::new();
626
627 for record_result in reader {
628 match record_result? {
629 WalRecord::Begin(txn_id) => {
630 max_txn_id = max_txn_id.max(txn_id.0);
631 pending_txns.entry(txn_id).or_default();
632 }
633 WalRecord::Put(txn_id, key, value) => {
634 max_txn_id = max_txn_id.max(txn_id.0);
635 pending_txns
636 .entry(txn_id)
637 .or_default()
638 .push((key, Some(value)));
639 }
640 WalRecord::Delete(txn_id, key) => {
641 max_txn_id = max_txn_id.max(txn_id.0);
642 pending_txns.entry(txn_id).or_default().push((key, None));
643 }
644 WalRecord::Commit(txn_id) => {
645 if let Some(writes) = pending_txns.remove(&txn_id) {
646 max_version += 1;
647 for (key, value) in writes {
648 if let Some(v) = value {
649 data.insert(key, (v, max_version));
650 } else {
651 data.remove(&key);
652 }
653 }
654 }
655 }
656 }
657 }
658
659 self.state
660 .next_txn_id
661 .store(max_txn_id + 1, Ordering::SeqCst);
662 self.state
663 .commit_version
664 .store(max_version, Ordering::SeqCst);
665 Ok(())
666 }
667
668 fn load_sstable(&self) -> Result<()> {
669 let path = match &self.state.sstable_path {
670 Some(p) => p,
671 None => return Ok(()),
672 };
673 if !path.exists() {
674 return Ok(());
675 }
676
677 let mut reader = match SstableReader::open(path) {
678 Ok(reader) => reader,
679 Err(e @ (Error::InvalidFormat(_) | Error::ChecksumMismatch)) => {
686 warn!(
687 path = %path.display(),
688 error = %e,
689 "discarding unreadable SSTable during recovery; replaying WAL only"
690 );
691 return Ok(());
692 }
693 Err(Error::Io(io)) if io.kind() == std::io::ErrorKind::UnexpectedEof => {
694 warn!(
695 path = %path.display(),
696 "discarding truncated SSTable during recovery; replaying WAL only"
697 );
698 return Ok(());
699 }
700 Err(e) => return Err(e),
701 };
702 let mut data = self.state.data.write().unwrap();
703 let mut version = self.state.commit_version.load(Ordering::Acquire);
704
705 let keys: Vec<Key> = reader
706 .index()
707 .iter()
708 .map(|entry| entry.key.clone())
709 .collect();
710
711 for key in keys {
712 if let Some(value) = reader.get(&key)? {
713 version += 1;
714 data.insert(key, (value, version));
715 }
716 }
717
718 self.state.commit_version.store(version, Ordering::SeqCst);
719 let mut slot = self.state.sstable.write().unwrap();
720 *slot = Some(reader);
721 Ok(())
722 }
723
724 fn recover(&self) -> Result<()> {
726 self.load_sstable()?;
727 self.replay()?;
728 self.state.recompute_current_memory();
729 Ok(())
730 }
731
732 fn sstable_get(&self, key: &Key) -> Result<Option<Value>> {
733 let mut guard = self.state.sstable.write().unwrap();
734 if let Some(reader) = guard.as_mut() {
735 return reader.get(key);
736 }
737 Ok(None)
738 }
739
740 fn begin_internal(&self, mode: TxnMode) -> Result<MemoryTransaction<'_>> {
741 let txn_id = self.state.next_txn_id.fetch_add(1, Ordering::SeqCst);
742 let start_version = self.state.commit_version.load(Ordering::Acquire);
743 Ok(MemoryTransaction::new(
744 self,
745 TxnId(txn_id),
746 mode,
747 start_version,
748 ))
749 }
750}
751
752impl<'a> TxnManager<'a, MemoryTransaction<'a>> for &'a MemoryTxnManager {
753 fn begin(&'a self, mode: TxnMode) -> Result<MemoryTransaction<'a>> {
754 self.begin_internal(mode)
755 }
756
757 fn commit(&'a self, mut txn: MemoryTransaction<'a>) -> Result<()> {
758 if txn.state != TxnState::Active {
759 return Err(Error::TxnClosed);
760 }
761 if txn.mode == TxnMode::ReadOnly || txn.writes.is_empty() {
762 txn.state = TxnState::Committed;
763 return Ok(());
764 }
765
766 let _snapshot_writer = self.state.owned_snapshot_gate.acquire_writer();
767 let mut data = self.state.data.write().unwrap();
768
769 for key in txn.read_set.keys() {
770 let current_version = data.get(key).map(|(_, v)| *v).unwrap_or(0);
771 if current_version > txn.start_version {
772 return Err(Error::TxnConflict);
773 }
774 }
775
776 for key in txn.writes.keys() {
778 let current_version = data.get(key).map(|(_, v)| *v).unwrap_or(0);
779 if current_version > txn.start_version {
780 return Err(Error::TxnConflict);
781 }
782 }
783
784 let mut delta: isize = 0;
786 for (key, value) in &txn.writes {
787 let current_size = data.get(key).map(|(v, _)| key.len() + v.len()).unwrap_or(0);
788 let new_size = match value {
789 Some(v) => key.len() + v.len(),
790 None => 0,
791 };
792 delta += new_size as isize - current_size as isize;
793 }
794
795 let current_mem = self.state.current_memory.load(Ordering::Relaxed);
796 let prospective = if delta >= 0 {
797 current_mem.saturating_add(delta as usize)
798 } else {
799 current_mem.saturating_sub(delta.unsigned_abs())
800 };
801
802 if delta > 0 {
803 self.state.check_memory_limit(delta as usize)?;
804 }
805
806 let commit_version = self.state.commit_version.fetch_add(1, Ordering::AcqRel) + 1;
807
808 self.write_wal(txn.id, &txn.writes)?;
809
810 for (key, value) in std::mem::take(&mut txn.writes) {
811 if let Some(v) = value {
812 data.insert(key, (v, commit_version));
813 } else {
814 data.remove(&key);
815 }
816 }
817
818 self.state
819 .current_memory
820 .store(prospective, Ordering::Relaxed);
821
822 txn.state = TxnState::Committed;
823 Ok(())
824 }
825
826 fn rollback(&'a self, mut txn: MemoryTransaction<'a>) -> Result<()> {
827 if txn.state != TxnState::Active {
828 return Err(Error::TxnClosed);
829 }
830 txn.state = TxnState::RolledBack;
831 Ok(())
832 }
833}
834
835pub struct MemoryTransaction<'a> {
837 manager: &'a MemoryTxnManager,
838 id: TxnId,
839 mode: TxnMode,
840 state: TxnState,
841 start_version: u64,
842 writes: BTreeMap<Key, Option<Value>>,
843 read_set: HashMap<Key, u64>,
844}
845
846impl<'a> MemoryTransaction<'a> {
847 fn new(manager: &'a MemoryTxnManager, id: TxnId, mode: TxnMode, start_version: u64) -> Self {
848 Self {
849 manager,
850 id,
851 mode,
852 state: TxnState::Active,
853 start_version,
854 writes: BTreeMap::new(),
855 read_set: HashMap::new(),
856 }
857 }
858
859 fn ensure_active(&self) -> Result<()> {
860 if self.state != TxnState::Active {
861 return Err(Error::TxnClosed);
862 }
863 Ok(())
864 }
865
866 pub(crate) fn rollback_in_place(&mut self) -> Result<()> {
868 if self.state != TxnState::Active {
869 return Err(Error::TxnClosed);
870 }
871 self.state = TxnState::RolledBack;
872 Ok(())
873 }
874
875 fn scan_range_internal(&mut self, start: &[u8], end: &[u8]) -> MergedScanIter<'_> {
876 let start_vec = start.to_vec();
877 let end_vec = end.to_vec();
878 let data_guard = self.manager.state.data.read().unwrap();
879 let data_ptr: *const BTreeMap<Key, VersionedValue> = &*data_guard;
880 let data_iter = unsafe {
881 (&*data_ptr).range((Included(start_vec.clone()), Excluded(end_vec.clone())))
883 };
884 let write_iter = self
885 .writes
886 .range((Included(start_vec.clone()), Excluded(end_vec.clone())));
887
888 MergedScanIter::new(
889 data_guard,
890 data_iter,
891 write_iter,
892 None,
893 Some(end_vec),
894 self.start_version,
895 &mut self.read_set,
896 )
897 }
898
899 fn scan_prefix_internal(&mut self, prefix: &[u8]) -> MergedScanIter<'_> {
900 let prefix_vec = prefix.to_vec();
901 let data_guard = self.manager.state.data.read().unwrap();
902 let data_ptr: *const BTreeMap<Key, VersionedValue> = &*data_guard;
903 let data_iter = unsafe {
904 (&*data_ptr).range(prefix_vec.clone()..)
906 };
907 let write_iter = self.writes.range(prefix_vec.clone()..);
908 MergedScanIter::new(
909 data_guard,
910 data_iter,
911 write_iter,
912 Some(prefix_vec),
913 None,
914 self.start_version,
915 &mut self.read_set,
916 )
917 }
918
919 fn scan_from_internal(&mut self, start: &[u8]) -> MergedScanIter<'_> {
920 let start_vec = start.to_vec();
921 let data_guard = self.manager.state.data.read().unwrap();
922 let data_ptr: *const BTreeMap<Key, VersionedValue> = &*data_guard;
923 let data_iter = unsafe {
924 (&*data_ptr).range(start_vec.clone()..)
926 };
927 let write_iter = self.writes.range(start_vec..);
928 MergedScanIter::new(
929 data_guard,
930 data_iter,
931 write_iter,
932 None,
933 None,
934 self.start_version,
935 &mut self.read_set,
936 )
937 }
938}
939
940impl<'a> KVTransaction<'a> for MemoryTransaction<'a> {
941 fn id(&self) -> TxnId {
942 self.id
943 }
944
945 fn mode(&self) -> TxnMode {
946 self.mode
947 }
948
949 fn get(&mut self, key: &Key) -> Result<Option<Value>> {
950 if self.state != TxnState::Active {
951 return Err(Error::TxnClosed);
952 }
953
954 if let Some(value) = self.writes.get(key) {
955 return Ok(value.clone());
956 }
957
958 let result = {
959 let data = self.manager.state.data.read().unwrap();
960 data.get(key).cloned()
961 };
962
963 if let Some((v, version)) = result {
964 self.read_set.insert(key.clone(), version);
965 return Ok(Some(v));
966 }
967
968 if let Some(value) = self.manager.sstable_get(key)? {
970 let version = self.manager.state.commit_version.load(Ordering::Acquire);
971 self.read_set.insert(key.clone(), version);
972 return Ok(Some(value));
973 }
974
975 Ok(None)
976 }
977
978 fn put(&mut self, key: Key, value: Value) -> Result<()> {
979 if self.state != TxnState::Active {
980 return Err(Error::TxnClosed);
981 }
982 if self.mode == TxnMode::ReadOnly {
983 return Err(Error::TxnReadOnly);
984 }
985 self.writes.insert(key, Some(value));
986 Ok(())
987 }
988
989 fn delete(&mut self, key: Key) -> Result<()> {
990 if self.state != TxnState::Active {
991 return Err(Error::TxnClosed);
992 }
993 if self.mode == TxnMode::ReadOnly {
994 return Err(Error::TxnReadOnly);
995 }
996 self.writes.insert(key, None);
997 Ok(())
998 }
999
1000 fn scan_prefix(
1001 &mut self,
1002 prefix: &[u8],
1003 ) -> Result<Box<dyn Iterator<Item = (Key, Value)> + '_>> {
1004 self.ensure_active()?;
1005 let iter = self
1006 .scan_prefix_internal(prefix)
1007 .filter_map(|(k, v)| v.map(|val| (k, val)));
1008 Ok(Box::new(iter))
1009 }
1010
1011 fn scan_range(
1012 &mut self,
1013 start: &[u8],
1014 end: &[u8],
1015 ) -> Result<Box<dyn Iterator<Item = (Key, Value)> + '_>> {
1016 self.ensure_active()?;
1017 let iter = self
1018 .scan_range_internal(start, end)
1019 .filter_map(|(k, v)| v.map(|val| (k, val)));
1020 Ok(Box::new(iter))
1021 }
1022
1023 fn scan_from(&mut self, start: &[u8]) -> Result<Box<dyn Iterator<Item = (Key, Value)> + '_>> {
1024 self.ensure_active()?;
1025 let iter = self
1026 .scan_from_internal(start)
1027 .filter_map(|(key, value)| value.map(|value| (key, value)));
1028 Ok(Box::new(iter))
1029 }
1030
1031 fn commit_self(mut self) -> Result<()> {
1032 if self.state != TxnState::Active {
1033 return Err(Error::TxnClosed);
1034 }
1035 if self.mode == TxnMode::ReadOnly || self.writes.is_empty() {
1036 self.state = TxnState::Committed;
1037 return Ok(());
1038 }
1039
1040 let _snapshot_writer = self.manager.state.owned_snapshot_gate.acquire_writer();
1041 let mut data = self.manager.state.data.write().unwrap();
1042
1043 for key in self.read_set.keys() {
1045 let current_version = data.get(key).map(|(_, v)| *v).unwrap_or(0);
1046 if current_version > self.start_version {
1047 return Err(Error::TxnConflict);
1048 }
1049 }
1050
1051 for key in self.writes.keys() {
1053 let current_version = data.get(key).map(|(_, v)| *v).unwrap_or(0);
1054 if current_version > self.start_version {
1055 return Err(Error::TxnConflict);
1056 }
1057 }
1058
1059 let mut delta: isize = 0;
1061 for (key, value) in &self.writes {
1062 let current_size = data.get(key).map(|(v, _)| key.len() + v.len()).unwrap_or(0);
1063 let new_size = match value {
1064 Some(v) => key.len() + v.len(),
1065 None => 0,
1066 };
1067 delta += new_size as isize - current_size as isize;
1068 }
1069
1070 let current_mem = self.manager.state.current_memory.load(Ordering::Relaxed);
1071 let prospective = if delta >= 0 {
1072 current_mem.saturating_add(delta as usize)
1073 } else {
1074 current_mem.saturating_sub(delta.unsigned_abs())
1075 };
1076
1077 if delta > 0 {
1078 self.manager.state.check_memory_limit(delta as usize)?;
1079 }
1080
1081 let commit_version = self
1082 .manager
1083 .state
1084 .commit_version
1085 .fetch_add(1, Ordering::AcqRel)
1086 + 1;
1087
1088 self.manager.write_wal(self.id, &self.writes)?;
1090
1091 for (key, value) in std::mem::take(&mut self.writes) {
1093 if let Some(v) = value {
1094 data.insert(key, (v, commit_version));
1095 } else {
1096 data.remove(&key);
1097 }
1098 }
1099
1100 self.manager
1101 .state
1102 .current_memory
1103 .store(prospective, Ordering::Relaxed);
1104
1105 self.state = TxnState::Committed;
1106 Ok(())
1107 }
1108
1109 fn rollback_self(mut self) -> Result<()> {
1110 if self.state != TxnState::Active {
1111 return Err(Error::TxnClosed);
1112 }
1113 self.state = TxnState::RolledBack;
1114 Ok(())
1115 }
1116}
1117
1118struct OwnedMemoryTransaction {
1120 manager: Arc<MemoryTxnManager>,
1121 state: Arc<Mutex<OwnedMemoryTransactionState>>,
1122}
1123
1124struct OwnedMemoryTransactionState {
1125 id: TxnId,
1126 mode: TxnMode,
1127 state: TxnState,
1128 start_version: u64,
1129 writes: BTreeMap<Key, Option<Value>>,
1130 read_set: HashMap<Key, u64>,
1131 cursor_open: bool,
1132}
1133
1134impl OwnedMemoryTransaction {
1135 fn new(manager: Arc<MemoryTxnManager>, mode: TxnMode) -> Self {
1136 let id = TxnId(manager.state.next_txn_id.fetch_add(1, Ordering::SeqCst));
1137 let start_version = manager.state.commit_version.load(Ordering::Acquire);
1138 Self {
1139 manager,
1140 state: Arc::new(Mutex::new(OwnedMemoryTransactionState {
1141 id,
1142 mode,
1143 state: TxnState::Active,
1144 start_version,
1145 writes: BTreeMap::new(),
1146 read_set: HashMap::new(),
1147 cursor_open: false,
1148 })),
1149 }
1150 }
1151
1152 fn open_cursor(
1153 &mut self,
1154 start: Option<Key>,
1155 prefix: Option<Vec<u8>>,
1156 end: Option<Key>,
1157 ) -> Result<Box<dyn OwnedKVScan>> {
1158 let snapshot = self.manager.state.owned_snapshot_gate.acquire_reader();
1159 let mut state = self
1160 .state
1161 .lock()
1162 .expect("owned memory transaction mutex poisoned");
1163 if state.state != TxnState::Active || state.cursor_open {
1164 return Err(Error::TxnClosed);
1165 }
1166 state.cursor_open = true;
1167 drop(state);
1168 Ok(Box::new(OwnedMemoryCursor {
1169 manager: self.manager.clone(),
1170 transaction: self.state.clone(),
1171 snapshot: Some(snapshot),
1172 last_key: None,
1173 start,
1174 prefix,
1175 end,
1176 }))
1177 }
1178
1179 fn ensure_active(state: &OwnedMemoryTransactionState) -> Result<()> {
1180 if state.state != TxnState::Active {
1181 return Err(Error::TxnClosed);
1182 }
1183 Ok(())
1184 }
1185}
1186
1187impl OwnedKVTransaction for OwnedMemoryTransaction {
1188 fn id(&self) -> TxnId {
1189 self.state
1190 .lock()
1191 .expect("owned memory transaction mutex poisoned")
1192 .id
1193 }
1194
1195 fn mode(&self) -> TxnMode {
1196 self.state
1197 .lock()
1198 .expect("owned memory transaction mutex poisoned")
1199 .mode
1200 }
1201
1202 fn get(&mut self, key: &Key) -> Result<Option<Value>> {
1203 let mut state = self
1204 .state
1205 .lock()
1206 .expect("owned memory transaction mutex poisoned");
1207 Self::ensure_active(&state)?;
1208 if let Some(value) = state.writes.get(key) {
1209 return Ok(value.clone());
1210 }
1211
1212 let result = {
1213 let data = self.manager.state.data.read().unwrap();
1214 data.get(key).cloned()
1215 };
1216 if let Some((value, version)) = result {
1217 if version <= state.start_version {
1218 state.read_set.insert(key.clone(), version);
1219 return Ok(Some(value));
1220 }
1221 return Ok(None);
1222 }
1223
1224 if let Some(value) = self.manager.sstable_get(key)? {
1225 let start_version = state.start_version;
1226 state.read_set.insert(key.clone(), start_version);
1227 return Ok(Some(value));
1228 }
1229 Ok(None)
1230 }
1231
1232 fn put(&mut self, key: Key, value: Value) -> Result<()> {
1233 let mut state = self
1234 .state
1235 .lock()
1236 .expect("owned memory transaction mutex poisoned");
1237 Self::ensure_active(&state)?;
1238 if state.mode == TxnMode::ReadOnly {
1239 return Err(Error::TxnReadOnly);
1240 }
1241 if state.cursor_open {
1242 return Err(Error::TxnClosed);
1243 }
1244 state.writes.insert(key, Some(value));
1245 Ok(())
1246 }
1247
1248 fn delete(&mut self, key: Key) -> Result<()> {
1249 let mut state = self
1250 .state
1251 .lock()
1252 .expect("owned memory transaction mutex poisoned");
1253 Self::ensure_active(&state)?;
1254 if state.mode == TxnMode::ReadOnly {
1255 return Err(Error::TxnReadOnly);
1256 }
1257 if state.cursor_open {
1258 return Err(Error::TxnClosed);
1259 }
1260 state.writes.insert(key, None);
1261 Ok(())
1262 }
1263
1264 fn scan_prefix(&mut self, prefix: &[u8]) -> Result<Box<dyn OwnedKVScan>> {
1265 self.open_cursor(Some(prefix.to_vec()), Some(prefix.to_vec()), None)
1270 }
1271
1272 fn scan_range(&mut self, start: &[u8], end: &[u8]) -> Result<Box<dyn OwnedKVScan>> {
1273 self.open_cursor(Some(start.to_vec()), None, Some(end.to_vec()))
1274 }
1275
1276 fn scan_from(&mut self, start: &[u8]) -> Result<Box<dyn OwnedKVScan>> {
1277 self.open_cursor(Some(start.to_vec()), None, None)
1278 }
1279
1280 fn commit(self: Box<Self>) -> Result<()> {
1281 let (id, mode, start_version, writes, read_set) = {
1282 let mut state = self
1283 .state
1284 .lock()
1285 .expect("owned memory transaction mutex poisoned");
1286 Self::ensure_active(&state)?;
1287 if state.cursor_open {
1288 return Err(Error::TxnClosed);
1289 }
1290 state.state = TxnState::Committed;
1291 (
1292 state.id,
1293 state.mode,
1294 state.start_version,
1295 std::mem::take(&mut state.writes),
1296 std::mem::take(&mut state.read_set),
1297 )
1298 };
1299 if mode == TxnMode::ReadOnly || writes.is_empty() {
1300 return Ok(());
1301 }
1302
1303 let _snapshot_writer = self.manager.state.owned_snapshot_gate.acquire_writer();
1304 let mut data = self.manager.state.data.write().unwrap();
1305 for key in read_set.keys().chain(writes.keys()) {
1306 let current_version = data.get(key).map(|(_, version)| *version).unwrap_or(0);
1307 if current_version > start_version {
1308 return Err(Error::TxnConflict);
1309 }
1310 }
1311
1312 let mut delta: isize = 0;
1313 for (key, value) in &writes {
1314 let current_size = data
1315 .get(key)
1316 .map(|(value, _)| key.len() + value.len())
1317 .unwrap_or(0);
1318 let new_size = value.as_ref().map_or(0, |value| key.len() + value.len());
1319 delta += new_size as isize - current_size as isize;
1320 }
1321 let current_memory = self.manager.state.current_memory.load(Ordering::Relaxed);
1322 let prospective = if delta >= 0 {
1323 current_memory.saturating_add(delta as usize)
1324 } else {
1325 current_memory.saturating_sub(delta.unsigned_abs())
1326 };
1327 if delta > 0 {
1328 self.manager.state.check_memory_limit(delta as usize)?;
1329 }
1330
1331 let commit_version = self
1332 .manager
1333 .state
1334 .commit_version
1335 .fetch_add(1, Ordering::AcqRel)
1336 + 1;
1337 self.manager.write_wal(id, &writes)?;
1338 for (key, value) in writes {
1339 if let Some(value) = value {
1340 data.insert(key, (value, commit_version));
1341 } else {
1342 data.remove(&key);
1343 }
1344 }
1345 self.manager
1346 .state
1347 .current_memory
1348 .store(prospective, Ordering::Relaxed);
1349 Ok(())
1350 }
1351
1352 fn rollback(self: Box<Self>) -> Result<()> {
1353 let mut state = self
1354 .state
1355 .lock()
1356 .expect("owned memory transaction mutex poisoned");
1357 Self::ensure_active(&state)?;
1358 if state.cursor_open {
1359 return Err(Error::TxnClosed);
1360 }
1361 state.writes.clear();
1362 state.state = TxnState::RolledBack;
1363 Ok(())
1364 }
1365}
1366
1367struct OwnedMemoryCursor {
1368 manager: Arc<MemoryTxnManager>,
1369 transaction: Arc<Mutex<OwnedMemoryTransactionState>>,
1370 snapshot: Option<OwnedSnapshotReader>,
1371 last_key: Option<Key>,
1372 start: Option<Key>,
1373 prefix: Option<Vec<u8>>,
1374 end: Option<Key>,
1375}
1376
1377impl OwnedMemoryCursor {
1378 fn key_is_in_scope(&self, key: &Key) -> bool {
1379 self.prefix
1380 .as_ref()
1381 .is_none_or(|prefix| key.starts_with(prefix))
1382 && self.end.as_ref().is_none_or(|end| key < end)
1383 }
1384
1385 fn data_candidate(&self, start_version: u64) -> Option<(Key, Value, u64)> {
1386 let data = self.manager.state.data.read().unwrap();
1387 let entries: Box<dyn Iterator<Item = (&Key, &(Value, u64))>> = match &self.last_key {
1388 Some(last_key) => Box::new(data.range::<Key, _>((Excluded(last_key), Unbounded))),
1389 None => match &self.start {
1390 Some(start) => Box::new(data.range::<Key, _>((Included(start), Unbounded))),
1391 None => Box::new(data.iter()),
1392 },
1393 };
1394 for (key, (value, version)) in entries {
1395 if !self.key_is_in_scope(key) {
1396 return None;
1397 }
1398 if *version <= start_version {
1399 return Some((key.clone(), value.clone(), *version));
1400 }
1401 }
1402 None
1403 }
1404
1405 fn write_candidate(&self) -> Option<(Key, Option<Value>)> {
1406 let transaction = self
1407 .transaction
1408 .lock()
1409 .expect("owned memory transaction mutex poisoned");
1410 let entry = match &self.last_key {
1411 Some(last_key) => transaction
1412 .writes
1413 .range::<Key, _>((Excluded(last_key), Unbounded))
1414 .next(),
1415 None => match &self.start {
1416 Some(start) => transaction
1417 .writes
1418 .range::<Key, _>((Included(start), Unbounded))
1419 .next(),
1420 None => transaction.writes.iter().next(),
1421 },
1422 }?;
1423 self.key_is_in_scope(entry.0)
1424 .then(|| (entry.0.clone(), entry.1.clone()))
1425 }
1426
1427 fn record_read(&self, key: Key, version: u64) -> Result<()> {
1428 let mut transaction = self
1429 .transaction
1430 .lock()
1431 .expect("owned memory transaction mutex poisoned");
1432 if transaction.state != TxnState::Active || !transaction.cursor_open {
1433 return Err(Error::TxnClosed);
1434 }
1435 transaction.read_set.insert(key, version);
1436 Ok(())
1437 }
1438
1439 fn finish(&mut self) {
1440 self.snapshot.take();
1441 let mut transaction = self
1442 .transaction
1443 .lock()
1444 .expect("owned memory transaction mutex poisoned");
1445 transaction.cursor_open = false;
1446 }
1447}
1448
1449impl OwnedKVScan for OwnedMemoryCursor {
1450 fn next_entry(&mut self) -> Result<Option<(Key, Value)>> {
1451 if self.snapshot.is_none() {
1452 return Ok(None);
1453 }
1454 loop {
1455 let start_version = self
1456 .transaction
1457 .lock()
1458 .expect("owned memory transaction mutex poisoned")
1459 .start_version;
1460 let data = self.data_candidate(start_version);
1461 let write = self.write_candidate();
1462 let next = match (data, write) {
1463 (Some((data_key, data_value, data_version)), Some((write_key, write_value))) => {
1464 if data_key == write_key {
1465 self.record_read(data_key.clone(), data_version)?;
1466 (data_key, write_value)
1467 } else if data_key < write_key {
1468 self.record_read(data_key.clone(), data_version)?;
1469 (data_key, Some(data_value))
1470 } else {
1471 (write_key, write_value)
1472 }
1473 }
1474 (Some((data_key, data_value, data_version)), None) => {
1475 self.record_read(data_key.clone(), data_version)?;
1476 (data_key, Some(data_value))
1477 }
1478 (None, Some((write_key, write_value))) => (write_key, write_value),
1479 (None, None) => {
1480 self.finish();
1481 return Ok(None);
1482 }
1483 };
1484 self.last_key = Some(next.0.clone());
1485 if let Some(value) = next.1 {
1486 return Ok(Some((next.0, value)));
1487 }
1488 }
1489 }
1490
1491 fn close(&mut self) -> Result<()> {
1492 self.finish();
1493 Ok(())
1494 }
1495}
1496
1497impl Drop for OwnedMemoryCursor {
1498 fn drop(&mut self) {
1499 self.finish();
1500 }
1501}
1502
1503struct MergedScanIter<'a> {
1505 _data_guard: RwLockReadGuard<'a, BTreeMap<Key, VersionedValue>>,
1506 data_iter: std::collections::btree_map::Range<'a, Key, VersionedValue>,
1507 write_iter: std::collections::btree_map::Range<'a, Key, Option<Value>>,
1508 data_peek: Option<(Key, (Value, u64))>,
1509 write_peek: Option<(Key, Option<Value>)>,
1510 prefix: Option<Vec<u8>>,
1511 end: Option<Key>,
1512 start_version: u64,
1513 read_set: &'a mut HashMap<Key, u64>,
1514}
1515
1516impl<'a> MergedScanIter<'a> {
1517 #[allow(clippy::too_many_arguments)]
1518 fn new(
1519 data_guard: std::sync::RwLockReadGuard<'a, BTreeMap<Key, VersionedValue>>,
1520 data_iter: std::collections::btree_map::Range<'a, Key, VersionedValue>,
1521 write_iter: std::collections::btree_map::Range<'a, Key, Option<Value>>,
1522 prefix: Option<Vec<u8>>,
1523 end: Option<Key>,
1524 start_version: u64,
1525 read_set: &'a mut HashMap<Key, u64>,
1526 ) -> Self {
1527 let mut iter = Self {
1528 _data_guard: data_guard,
1529 data_iter,
1530 write_iter,
1531 data_peek: None,
1532 write_peek: None,
1533 prefix,
1534 end,
1535 start_version,
1536 read_set,
1537 };
1538 iter.advance_data();
1539 iter.advance_write();
1540 iter
1541 }
1542
1543 fn advance_data(&mut self) {
1544 self.data_peek = None;
1545 while let Some((k, (v, ver))) = self.data_iter.next().map(|(k, v)| (k.clone(), v.clone())) {
1546 if let Some(end) = &self.end {
1547 if k >= *end {
1548 return;
1549 }
1550 }
1551 if let Some(prefix) = &self.prefix {
1552 if !k.starts_with(prefix) {
1553 return;
1554 }
1555 }
1556 if ver > self.start_version {
1557 continue;
1558 }
1559 self.data_peek = Some((k, (v, ver)));
1560 return;
1561 }
1562 }
1563
1564 fn advance_write(&mut self) {
1565 self.write_peek = None;
1566 if let Some((k, v)) = self.write_iter.next().map(|(k, v)| (k.clone(), v.clone())) {
1567 if let Some(end) = &self.end {
1568 if k >= *end {
1569 return;
1570 }
1571 }
1572 if let Some(prefix) = &self.prefix {
1573 if !k.starts_with(prefix) {
1574 return;
1575 }
1576 }
1577 self.write_peek = Some((k, v));
1578 }
1579 }
1580}
1581
1582impl<'a> Iterator for MergedScanIter<'a> {
1583 type Item = (Key, Option<Value>);
1584
1585 fn next(&mut self) -> Option<Self::Item> {
1586 let data_key = self.data_peek.as_ref().map(|(k, _)| k.clone());
1587 let write_key = self.write_peek.as_ref().map(|(k, _)| k.clone());
1588
1589 match (data_key, write_key) {
1590 (Some(dk), Some(wk)) => {
1591 if dk == wk {
1592 let (_, (_, ver)) = self.data_peek.take().unwrap();
1593 let (_, write_val) = self.write_peek.take().unwrap();
1594 self.read_set.insert(dk.clone(), ver);
1595 self.advance_data();
1596 self.advance_write();
1597 Some((dk, write_val))
1598 } else if dk < wk {
1599 let (k, (v, ver)) = self.data_peek.take().unwrap();
1600 self.read_set.insert(k.clone(), ver);
1601 self.advance_data();
1602 Some((k, Some(v)))
1603 } else {
1604 let (k, write_val) = self.write_peek.take().unwrap();
1605 self.advance_write();
1606 Some((k, write_val))
1607 }
1608 }
1609 (Some(_), None) => {
1610 let (k, (v, ver)) = self.data_peek.take().unwrap();
1611 self.read_set.insert(k.clone(), ver);
1612 self.advance_data();
1613 Some((k, Some(v)))
1614 }
1615 (None, Some(_)) => {
1616 let (k, write_val) = self.write_peek.take().unwrap();
1617 self.advance_write();
1618 Some((k, write_val))
1619 }
1620 (None, None) => None,
1621 }
1622 }
1623}
1624
1625impl<'a> Drop for MemoryTransaction<'a> {
1626 fn drop(&mut self) {
1627 if self.state == TxnState::Active {
1628 self.state = TxnState::RolledBack;
1629 }
1630 }
1631}
1632
1633#[cfg(all(test, not(target_arch = "wasm32")))]
1634mod tests {
1635 use super::*;
1636 use crate::{KVTransaction, TxnManager};
1637 use tempfile::tempdir;
1638 use tracing::Level;
1639
1640 fn key(s: &str) -> Key {
1641 s.as_bytes().to_vec()
1642 }
1643
1644 fn value(s: &str) -> Value {
1645 s.as_bytes().to_vec()
1646 }
1647
1648 fn committed_value_after_reopen(wal_path: &Path, key: Key) -> Option<Value> {
1649 let reopened = MemoryKV::open(wal_path).unwrap();
1650 let manager = reopened.txn_manager();
1651 let mut txn = manager.begin(TxnMode::ReadOnly).unwrap();
1652 txn.get(&key).unwrap()
1653 }
1654
1655 fn write_flush_and_corrupt_sstable<F>(corrupt: F) -> (tempfile::TempDir, PathBuf)
1656 where
1657 F: FnOnce(&Path),
1658 {
1659 let dir = tempdir().unwrap();
1660 let wal_path = dir.path().join("wal.log");
1661 {
1662 let store = MemoryKV::open(&wal_path).unwrap();
1663 let manager = store.txn_manager();
1664 let mut txn = manager.begin(TxnMode::ReadWrite).unwrap();
1665 txn.put(key("k1"), value("v1")).unwrap();
1666 manager.commit(txn).unwrap();
1667 store.flush().unwrap();
1668 }
1669
1670 corrupt(&wal_path.with_extension("sst"));
1671 (dir, wal_path)
1672 }
1673
1674 #[cfg(feature = "test-hooks")]
1675 struct FailsBeforeFsync;
1676
1677 #[cfg(feature = "test-hooks")]
1678 impl IoHooks for FailsBeforeFsync {
1679 fn before_fsync(&self) -> std::io::Result<()> {
1680 Err(std::io::Error::other("injected WAL fsync failure"))
1681 }
1682 }
1683
1684 #[cfg(feature = "test-hooks")]
1685 #[test]
1686 fn commit_self_wal_fsync_failure_does_not_ack_or_apply() {
1687 let dir = tempdir().unwrap();
1688 let wal_path = dir.path().join("wal.log");
1689 let store = MemoryKV::open_with_io_hooks(&wal_path, Arc::new(FailsBeforeFsync)).unwrap();
1690 let manager = store.txn_manager();
1691
1692 let mut txn = manager.begin(TxnMode::ReadWrite).unwrap();
1693 txn.put(key("not-acked"), value("value")).unwrap();
1694 let result = txn.commit_self();
1695 assert!(matches!(result, Err(Error::Io(_))));
1696
1697 let mut read_txn = manager.begin(TxnMode::ReadOnly).unwrap();
1698 assert_eq!(read_txn.get(&key("not-acked")).unwrap(), None);
1699 }
1700
1701 #[test]
1702 fn test_put_and_get_transient() {
1703 let store = MemoryKV::new();
1704 let manager = store.txn_manager();
1705 let mut txn = manager.begin(TxnMode::ReadWrite).unwrap();
1706 txn.put(key("hello"), value("world")).unwrap();
1707 let val = txn.get(&key("hello")).unwrap();
1708 assert_eq!(val, Some(value("world")));
1709 manager.commit(txn).unwrap();
1710
1711 let mut txn2 = manager.begin(TxnMode::ReadOnly).unwrap();
1712 let val2 = txn2.get(&key("hello")).unwrap();
1713 assert_eq!(val2, Some(value("world")));
1714 }
1715
1716 #[test]
1717 fn test_occ_conflict() {
1718 let store = MemoryKV::new();
1719 let manager = store.txn_manager();
1720
1721 let mut t1 = manager.begin(TxnMode::ReadWrite).unwrap();
1722 t1.get(&key("k1")).unwrap();
1723
1724 let mut t2 = manager.begin(TxnMode::ReadWrite).unwrap();
1725 t2.put(key("k1"), value("v2")).unwrap();
1726 assert!(manager.commit(t2).is_ok());
1727
1728 t1.put(key("k1"), value("v1")).unwrap();
1729 let result = manager.commit(t1);
1730 assert!(matches!(result, Err(Error::TxnConflict)));
1731 }
1732
1733 #[test]
1734 fn test_blind_write_conflict() {
1735 let store = MemoryKV::new();
1736 let manager = store.txn_manager();
1737
1738 let mut t1 = manager.begin(TxnMode::ReadWrite).unwrap();
1739 t1.put(key("k1"), value("v1")).unwrap();
1740
1741 let mut t2 = manager.begin(TxnMode::ReadWrite).unwrap();
1742 t2.put(key("k1"), value("v2")).unwrap();
1743 assert!(manager.commit(t2).is_ok());
1744
1745 let result = manager.commit(t1);
1746 assert!(matches!(result, Err(Error::TxnConflict)));
1747 }
1748
1749 #[test]
1750 fn test_read_only_write_fails() {
1751 let store = MemoryKV::new();
1752 let manager = store.txn_manager();
1753 let mut txn = manager.begin(TxnMode::ReadOnly).unwrap();
1754 assert!(matches!(
1755 txn.put(key("k1"), value("v1")),
1756 Err(Error::TxnReadOnly)
1757 ));
1758 assert!(matches!(txn.delete(key("k1")), Err(Error::TxnReadOnly)));
1759 }
1760
1761 #[test]
1762 fn test_txn_closed_error() {
1763 let store = MemoryKV::new();
1764 let manager = store.txn_manager();
1765 let txn = manager.begin(TxnMode::ReadWrite).unwrap();
1766 manager.commit(txn).unwrap();
1767
1768 let mut closed_txn = manager.begin(TxnMode::ReadWrite).unwrap();
1771 closed_txn.state = TxnState::Committed;
1772 assert!(matches!(closed_txn.get(&key("k1")), Err(Error::TxnClosed)));
1773 assert!(matches!(
1774 closed_txn.put(key("k1"), value("v1")),
1775 Err(Error::TxnClosed)
1776 ));
1777 }
1778
1779 #[test]
1780 fn test_get_not_found() {
1781 let store = MemoryKV::new();
1782 let manager = store.txn_manager();
1783 let mut txn = manager.begin(TxnMode::ReadOnly).unwrap();
1784 let res = txn.get(&key("non-existent"));
1785 assert!(res.is_ok());
1786 assert!(res.unwrap().is_none());
1787 }
1788
1789 #[test]
1790 fn flush_and_reopen_reads_from_sstable() {
1791 let dir = tempdir().unwrap();
1792 let wal_path = dir.path().join("wal.log");
1793 {
1794 let store = MemoryKV::open(&wal_path).unwrap();
1795 let manager = store.txn_manager();
1796 let mut txn = manager.begin(TxnMode::ReadWrite).unwrap();
1797 txn.put(key("k1"), value("v1")).unwrap();
1798 manager.commit(txn).unwrap();
1799 store.flush().unwrap();
1800 }
1801
1802 let reopened = MemoryKV::open(&wal_path).unwrap();
1803 let manager = reopened.txn_manager();
1804 let mut txn = manager.begin(TxnMode::ReadOnly).unwrap();
1805 assert_eq!(txn.get(&key("k1")).unwrap(), Some(value("v1")));
1806 }
1807
1808 #[test]
1809 fn corrupt_sstable_header_is_discarded_and_wal_recovers() {
1810 let (_dir, wal_path) = write_flush_and_corrupt_sstable(|sst_path| {
1811 let mut file = std::fs::OpenOptions::new()
1812 .write(true)
1813 .open(sst_path)
1814 .unwrap();
1815 use std::io::{Seek, Write};
1816 file.seek(std::io::SeekFrom::Start(0)).unwrap();
1817 file.write_all(b"BAD!").unwrap();
1818 file.sync_all().unwrap();
1819 });
1820 let err = SstableReader::open(&wal_path.with_extension("sst")).unwrap_err();
1821 assert!(matches!(err, Error::InvalidFormat(_)));
1822
1823 assert_eq!(
1824 committed_value_after_reopen(&wal_path, key("k1")),
1825 Some(value("v1"))
1826 );
1827 }
1828
1829 #[test]
1830 fn corrupt_sstable_payload_checksum_is_discarded_and_wal_recovers() {
1831 let (_dir, wal_path) = write_flush_and_corrupt_sstable(|sst_path| {
1832 let mut file = std::fs::OpenOptions::new()
1833 .read(true)
1834 .write(true)
1835 .open(sst_path)
1836 .unwrap();
1837 use std::io::{Read, Seek, Write};
1838 file.seek(std::io::SeekFrom::Start(16 + 8 + key("k1").len() as u64))
1839 .unwrap();
1840 let mut byte = [0u8; 1];
1841 file.read_exact(&mut byte).unwrap();
1842 file.seek(std::io::SeekFrom::Current(-1)).unwrap();
1843 file.write_all(&[byte[0] ^ 0xFF]).unwrap();
1844 file.sync_all().unwrap();
1845 });
1846 let err = SstableReader::open(&wal_path.with_extension("sst")).unwrap_err();
1847 assert!(matches!(err, Error::ChecksumMismatch));
1848
1849 assert_eq!(
1850 committed_value_after_reopen(&wal_path, key("k1")),
1851 Some(value("v1"))
1852 );
1853 }
1854
1855 #[test]
1856 fn truncated_sstable_is_discarded_and_wal_recovers() {
1857 let (_dir, wal_path) = write_flush_and_corrupt_sstable(|sst_path| {
1858 let file = std::fs::OpenOptions::new()
1859 .write(true)
1860 .open(sst_path)
1861 .unwrap();
1862 file.set_len(16).unwrap();
1863 file.sync_all().unwrap();
1864 });
1865 let err = SstableReader::open(&wal_path.with_extension("sst")).unwrap_err();
1866 assert!(matches!(err, Error::InvalidFormat(_)));
1867
1868 assert_eq!(
1869 committed_value_after_reopen(&wal_path, key("k1")),
1870 Some(value("v1"))
1871 );
1872 }
1873
1874 #[test]
1875 fn wal_recovers_committed_tombstone_on_reopen() {
1876 let dir = tempdir().unwrap();
1877 let wal_path = dir.path().join("wal.log");
1878 {
1879 let store = MemoryKV::open(&wal_path).unwrap();
1880 let manager = store.txn_manager();
1881 let mut put_txn = manager.begin(TxnMode::ReadWrite).unwrap();
1882 put_txn.put(key("deleted"), value("value")).unwrap();
1883 manager.commit(put_txn).unwrap();
1884
1885 let mut delete_txn = manager.begin(TxnMode::ReadWrite).unwrap();
1886 delete_txn.delete(key("deleted")).unwrap();
1887 manager.commit(delete_txn).unwrap();
1888 }
1889
1890 assert_eq!(
1891 committed_value_after_reopen(&wal_path, key("deleted")),
1892 None
1893 );
1894 }
1895
1896 #[test]
1897 fn wal_overlays_sstable_on_reopen() {
1898 let dir = tempdir().unwrap();
1899 let wal_path = dir.path().join("wal.log");
1900 {
1901 let store = MemoryKV::open(&wal_path).unwrap();
1902 let manager = store.txn_manager();
1903 let mut txn = manager.begin(TxnMode::ReadWrite).unwrap();
1904 txn.put(key("k1"), value("v1")).unwrap();
1905 manager.commit(txn).unwrap();
1906 store.flush().unwrap();
1907
1908 let mut txn2 = manager.begin(TxnMode::ReadWrite).unwrap();
1909 txn2.put(key("k1"), value("v2")).unwrap();
1910 manager.commit(txn2).unwrap();
1911 }
1912
1913 let reopened = MemoryKV::open(&wal_path).unwrap();
1914 let manager = reopened.txn_manager();
1915 let mut txn = manager.begin(TxnMode::ReadOnly).unwrap();
1916 assert_eq!(txn.get(&key("k1")).unwrap(), Some(value("v2")));
1917 }
1918
1919 #[test]
1920 fn scan_prefix_merges_snapshot_and_writes() {
1921 let store = MemoryKV::new();
1922 let manager = store.txn_manager();
1923
1924 let mut seed = manager.begin(TxnMode::ReadWrite).unwrap();
1925 seed.put(key("p:1"), value("old1")).unwrap();
1926 seed.put(key("p:2"), value("old2")).unwrap();
1927 seed.put(key("q:1"), value("other")).unwrap();
1928 manager.commit(seed).unwrap();
1929
1930 let mut txn = manager.begin(TxnMode::ReadWrite).unwrap();
1931 txn.put(key("p:1"), value("new1")).unwrap();
1932 txn.delete(key("p:2")).unwrap();
1933 txn.put(key("p:3"), value("new3")).unwrap();
1934
1935 let results: Vec<_> = txn.scan_prefix(b"p:").unwrap().collect();
1936 assert_eq!(
1937 results,
1938 vec![(key("p:1"), value("new1")), (key("p:3"), value("new3"))]
1939 );
1940 }
1941
1942 #[test]
1943 fn scan_range_skips_newer_versions() {
1944 let store = MemoryKV::new();
1945 let manager = store.txn_manager();
1946
1947 let mut seed = manager.begin(TxnMode::ReadWrite).unwrap();
1948 seed.put(key("b"), value("v1")).unwrap();
1949 manager.commit(seed).unwrap();
1950
1951 let mut txn1 = manager.begin(TxnMode::ReadWrite).unwrap();
1952
1953 let mut txn2 = manager.begin(TxnMode::ReadWrite).unwrap();
1954 txn2.put(key("ba"), value("v2")).unwrap();
1955 manager.commit(txn2).unwrap();
1956
1957 let results: Vec<_> = txn1.scan_range(b"b", b"c").unwrap().collect();
1958 assert_eq!(results, vec![(key("b"), value("v1"))]);
1959 }
1960
1961 #[test]
1962 fn scan_range_records_reads_for_conflict_detection() {
1963 let store = MemoryKV::new();
1964 let manager = store.txn_manager();
1965
1966 let mut seed = manager.begin(TxnMode::ReadWrite).unwrap();
1967 seed.put(key("k1"), value("v1")).unwrap();
1968 manager.commit(seed).unwrap();
1969
1970 let mut t1 = manager.begin(TxnMode::ReadWrite).unwrap();
1971 let results: Vec<_> = t1.scan_range(b"k0", b"kz").unwrap().collect();
1972 assert_eq!(results, vec![(key("k1"), value("v1"))]);
1973 t1.put(key("k_new"), value("v_new")).unwrap();
1974
1975 let mut t2 = manager.begin(TxnMode::ReadWrite).unwrap();
1976 t2.put(key("k1"), value("v2")).unwrap();
1977 manager.commit(t2).unwrap();
1978
1979 let result = manager.commit(t1);
1980 assert!(matches!(result, Err(Error::TxnConflict)));
1981 }
1982
1983 #[test]
1984 fn owned_memory_transaction_merges_incremental_cursor_and_commits_once() {
1985 use crate::kv::OwnedSessionFactory;
1986
1987 let store = Arc::new(MemoryKV::new());
1988 let session = store
1989 .clone()
1990 .begin_owned_transaction(TxnMode::ReadWrite)
1991 .unwrap();
1992 let lease = session.acquire_lease().unwrap();
1993 lease
1994 .with_transaction(|transaction| {
1995 transaction.put(key("p:1"), value("one"))?;
1996 transaction.put(key("p:2"), value("two"))?;
1997 transaction.put(key("q:1"), value("other"))?;
1998 Ok(())
1999 })
2000 .unwrap();
2001 let mut cursor = lease
2002 .with_transaction(|transaction| transaction.scan_prefix(b"p:"))
2003 .unwrap();
2004 assert_eq!(
2005 cursor.next_entry().unwrap(),
2006 Some((key("p:1"), value("one")))
2007 );
2008 assert_eq!(
2009 cursor.next_entry().unwrap(),
2010 Some((key("p:2"), value("two")))
2011 );
2012 assert_eq!(cursor.next_entry().unwrap(), None);
2013 cursor.close().unwrap();
2014 drop(cursor);
2015 lease
2016 .finish(crate::txn::OwnedLeaseOutcome::Exhausted)
2017 .unwrap();
2018 session.commit().unwrap();
2019
2020 let read = store
2021 .clone()
2022 .begin_owned_read(crate::kv::OwnedReadOptions::default())
2023 .unwrap();
2024 let lease = read.acquire_lease().unwrap();
2025 assert_eq!(
2026 lease
2027 .with_transaction(|transaction| transaction.get(&key("p:2")))
2028 .unwrap(),
2029 Some(value("two"))
2030 );
2031 lease
2032 .finish(crate::txn::OwnedLeaseOutcome::Exhausted)
2033 .unwrap();
2034
2035 let range = store
2036 .clone()
2037 .begin_owned_read(crate::kv::OwnedReadOptions::default())
2038 .unwrap();
2039 let lease = range.acquire_lease().unwrap();
2040 let mut cursor = lease
2041 .with_transaction(|transaction| transaction.scan_range(b"p:1", b"q:"))
2042 .unwrap();
2043 assert_eq!(
2044 cursor.next_entry().unwrap(),
2045 Some((key("p:1"), value("one")))
2046 );
2047 assert_eq!(
2048 cursor.next_entry().unwrap(),
2049 Some((key("p:2"), value("two")))
2050 );
2051 assert_eq!(cursor.next_entry().unwrap(), None);
2052 drop(cursor);
2053 lease
2054 .finish(crate::txn::OwnedLeaseOutcome::Exhausted)
2055 .unwrap();
2056 }
2057
2058 #[test]
2059 fn owned_prefix_cursor_skips_keys_before_its_prefix() {
2060 use crate::kv::OwnedSessionFactory;
2061
2062 let store = Arc::new(MemoryKV::new());
2063 let writer = store
2064 .clone()
2065 .begin_owned_transaction(TxnMode::ReadWrite)
2066 .unwrap();
2067 let writer_lease = writer.acquire_lease().unwrap();
2068 writer_lease
2069 .with_transaction(|transaction| {
2070 transaction.put(key("catalog:before"), value("metadata"))?;
2071 transaction.put(key("row:1"), value("one"))?;
2072 Ok(())
2073 })
2074 .unwrap();
2075 writer_lease
2076 .finish(crate::txn::OwnedLeaseOutcome::Exhausted)
2077 .unwrap();
2078 writer.commit().unwrap();
2079
2080 let reader = store.clone().begin_owned_read(Default::default()).unwrap();
2081 let reader_lease = reader.acquire_lease().unwrap();
2082 let mut cursor = reader_lease
2083 .with_transaction(|transaction| transaction.scan_prefix(b"row:"))
2084 .unwrap();
2085 assert_eq!(
2086 cursor.next_entry().unwrap(),
2087 Some((key("row:1"), value("one")))
2088 );
2089 assert_eq!(cursor.next_entry().unwrap(), None);
2090 cursor.close().unwrap();
2091 drop(cursor);
2092 reader_lease
2093 .finish(crate::txn::OwnedLeaseOutcome::Exhausted)
2094 .unwrap();
2095 }
2096
2097 #[test]
2098 fn any_kv_memory_dispatches_to_owned_memory_session_without_borrowed_transaction() {
2099 use crate::kv::{AnyKV, OwnedSessionFactory};
2100
2101 let store = Arc::new(AnyKV::Memory(MemoryKV::new()));
2102 let session = store
2103 .clone()
2104 .begin_owned_transaction(TxnMode::ReadWrite)
2105 .unwrap();
2106 let lease = session.acquire_lease().unwrap();
2107 lease
2108 .with_transaction(|transaction| transaction.put(key("owned"), value("value")))
2109 .unwrap();
2110 lease
2111 .finish(crate::txn::OwnedLeaseOutcome::Exhausted)
2112 .unwrap();
2113 session.commit().unwrap();
2114
2115 let read = store
2116 .clone()
2117 .begin_owned_read(crate::kv::OwnedReadOptions::default())
2118 .unwrap();
2119 let lease = read.acquire_lease().unwrap();
2120 assert_eq!(
2121 lease
2122 .with_transaction(|transaction| transaction.get(&key("owned")))
2123 .unwrap(),
2124 Some(value("value"))
2125 );
2126 lease
2127 .finish(crate::txn::OwnedLeaseOutcome::Exhausted)
2128 .unwrap();
2129 }
2130
2131 #[test]
2132 fn memory_stats_tracks_put_and_delete() {
2133 let store = MemoryKV::new();
2134 let manager = store.txn_manager();
2135
2136 let stats = manager.memory_stats();
2137 assert_eq!(stats.total_bytes, 0);
2138 assert_eq!(stats.kv_bytes, 0);
2139 assert_eq!(stats.index_bytes, 0);
2140
2141 let mut txn = manager.begin(TxnMode::ReadWrite).unwrap();
2143 txn.put(key("a"), value("1234")).unwrap(); manager.commit(txn).unwrap();
2145
2146 let stats = manager.memory_stats();
2147 assert_eq!(stats.total_bytes, 5);
2148 assert_eq!(stats.kv_bytes, 5);
2149 assert_eq!(stats.index_bytes, 0);
2150
2151 let mut txn = manager.begin(TxnMode::ReadWrite).unwrap();
2153 txn.delete(key("a")).unwrap();
2154 manager.commit(txn).unwrap();
2155
2156 let stats = manager.memory_stats();
2157 assert_eq!(stats.total_bytes, 0);
2158 assert_eq!(stats.kv_bytes, 0);
2159 }
2160
2161 #[test]
2162 fn memory_limit_error_does_not_break_reads() {
2163 let store = MemoryKV::new_with_limit(Some(10));
2164 let manager = store.txn_manager();
2165
2166 let mut txn = manager.begin_internal(TxnMode::ReadWrite).unwrap();
2168 txn.put(key("k1"), value("vvvv")).unwrap();
2169 manager.commit(txn).unwrap();
2170
2171 let mut txn2 = manager.begin_internal(TxnMode::ReadWrite).unwrap();
2173 txn2.put(key("k2"), value("vvvvvv")).unwrap();
2174 let result = manager.commit(txn2);
2175 assert!(matches!(result, Err(Error::MemoryLimitExceeded { .. })));
2176
2177 let mut read_txn = manager.begin_internal(TxnMode::ReadOnly).unwrap();
2179 let got = read_txn.get(&key("k1")).unwrap();
2180 assert_eq!(got, Some(value("vvvv")));
2181
2182 let stats = manager.memory_stats();
2184 assert_eq!(stats.total_bytes, 6);
2185 }
2186
2187 struct VecWriter(std::sync::Arc<std::sync::Mutex<Vec<u8>>>);
2188
2189 impl std::io::Write for VecWriter {
2190 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
2191 let mut guard = self.0.lock().unwrap();
2192 guard.extend_from_slice(buf);
2193 Ok(buf.len())
2194 }
2195
2196 fn flush(&mut self) -> std::io::Result<()> {
2197 Ok(())
2198 }
2199 }
2200
2201 #[test]
2202 fn compaction_skips_when_over_limit_and_logs_warning() {
2203 let store = MemoryKV::new_with_limit(Some(12));
2204 let manager = store.txn_manager();
2205
2206 let mut txn = manager.begin_internal(TxnMode::ReadWrite).unwrap();
2208 txn.put(key("k1"), value("123456")).unwrap();
2209 manager.commit(txn).unwrap();
2210
2211 let buffer = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
2213 let make_writer = {
2214 let buf = buffer.clone();
2215 move || VecWriter(buf.clone())
2216 };
2217 let subscriber = tracing_subscriber::fmt()
2218 .with_max_level(Level::WARN)
2219 .with_writer(make_writer)
2220 .without_time()
2221 .finish();
2222 let _guard = tracing::subscriber::set_default(subscriber);
2223
2224 let ran = manager.compact_with_limit(2, 10, || Ok(())).unwrap();
2226 assert!(!ran);
2227
2228 assert_eq!(manager.memory_stats().total_bytes, 8);
2230
2231 let log = String::from_utf8(buffer.lock().unwrap().clone()).unwrap();
2233 assert!(
2234 log.contains("compaction skipped due to memory limit"),
2235 "expected warning log, got: {}",
2236 log
2237 );
2238 }
2239}