1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//! Native-neutral whole-record persistence contract for durable branch records.
//!
//! WAL append/read/flush/recovery is deliberately absent. Node publication is
//! the separate [`crate::store::NodeStore`] contract.
use core::fmt::Debug;
use super::refrecord::BranchRefRecord;
use super::time::Timestamp;
/// Outcome of the seam's create-exclusive operation.
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum CreateExclusive {
Installed,
TargetExists(BranchRefRecord),
}
/// Expected state fenced after a record-directory entry operation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum EntryFence<'a> {
Present(&'a BranchRefRecord),
Absent(&'a str),
}
/// Exactly the five whole-record operations required by the branch core.
///
/// Implementations must perform creation identity and sequence comparison in
/// `cas_replace_install`; callers must never synthesize compare-then-put.
///
/// `Send + Sync` is a supertrait obligation, not a per-site bound: a durable
/// record store is a cross-thread object, and v0.6.0 shipped `BranchRefStore`
/// without `Send`/`Sync` because the boxed seam carried no bound (trait
/// objects are `?Send` by default). The supertrait makes every current and
/// future embedding site inherit the auto-traits instead of re-remembering
/// them per box.
pub(super) trait DurableRecordStore: Debug + Send + Sync {
type Error;
/// Load every whole record at open, sweeping only backend-pinned temp debris.
fn list_read_at_open(&mut self) -> Result<Vec<BranchRefRecord>, Self::Error>;
/// Atomically install `record` only when its durable name is absent.
fn create_exclusive(
&mut self,
record: &BranchRefRecord,
) -> Result<CreateExclusive, Self::Error>;
/// CAS and atomically replace the whole record, returning what was installed.
fn cas_replace_install(
&mut self,
name: &str,
expected_created: Timestamp,
expected_seq: u64,
replacement: &BranchRefRecord,
) -> Result<BranchRefRecord, Self::Error>;
/// Idempotently unlink a whole record. `true` means an entry was removed.
fn unlink(&mut self, name: &str) -> Result<bool, Self::Error>;
/// Fence the preceding create, replace, or unlink entry operation.
fn entry_fence(&mut self, expected: EntryFence<'_>) -> Result<(), Self::Error>;
}
pub(super) fn entry_fence_with_source<S: DurableRecordStore + ?Sized>(
store: &mut S,
expected: EntryFence<'_>,
) -> Result<(), super::operation_error::FenceOperationError<S::Error>> {
super::operation_error::preserve_record_entry_fence(store.entry_fence(expected))
}
#[cfg(test)]
pub(super) fn entry_fence_compat<S: DurableRecordStore + ?Sized>(
store: &mut S,
expected: EntryFence<'_>,
) -> Result<(), S::Error> {
entry_fence_with_source(store, expected).map_err(|error| match error {
super::operation_error::FenceOperationError::RecordEntry(source) => source,
super::operation_error::FenceOperationError::NodePublication(_) => {
unreachable!("node fence returned from record entry core")
}
})
}