Skip to main content

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}