mkit_server/memory/mod.rs
1//! In-memory reference backends: the template third-party backends copy,
2//! and the stores under the core's own tests. Not durable.
3
4mod blob;
5mod kv;
6
7use std::sync::{Mutex, MutexGuard, PoisonError};
8
9use crate::store::StoreError;
10
11#[cfg(test)]
12pub(crate) use blob::MemoryPartSink;
13pub use blob::{MemoryBlobStore, MemoryPackSink};
14pub use kv::MemoryKv;
15
16/// A one-shot failure a memory store injects, for tests of the layers
17/// above. Each store fires only the faults that apply to it.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[non_exhaustive]
20pub enum MemoryFault {
21 /// The next `apply` fails before it checks anything; nothing is
22 /// written.
23 ApplyBefore,
24 /// The next `apply` commits, then reports an error: the ambiguous
25 /// outcome of a connection lost after commit.
26 ApplyAfterCommit,
27 /// The `n`th (0-based) `PackSink::write` of the next upload fails.
28 BlobWrite(u32),
29 /// The next `PackSink::commit` fails before anything becomes visible.
30 BlobCommit,
31}
32
33/// Lock `mutex`, recovering from poisoning: every critical section leaves
34/// the data consistent (normative rule 4), so a panic elsewhere never
35/// makes the store unusable.
36fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
37 mutex.lock().unwrap_or_else(PoisonError::into_inner)
38}
39
40/// Fire `fault` if it is the armed one.
41fn take_fault(armed: &Mutex<Option<MemoryFault>>, fault: MemoryFault) -> Result<(), StoreError> {
42 let mut armed = lock(armed);
43 if *armed == Some(fault) {
44 *armed = None;
45 return Err(StoreError::unavailable(format!("injected fault {fault:?}")));
46 }
47 Ok(())
48}