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
//! The shared writer fields a candidate commit advances, held aside until the
//! header that makes them durable has been written.
use WriterState;
/// The subset of [`WriterState`] a commit must advance *before* it can know
/// whether it will succeed: the allocation cursor (advanced by materializing
/// the trees) and the commit-history root, version, and cached row count
/// (advanced by writing this commit's history entry).
///
/// The invariant this type exists to hold: **a candidate commit that does not
/// become durable must leave no trace in shared state.** Advancing the live
/// `WriterState` in place breaks it two ways. The allocation cursor would stay
/// advanced over pages the refused attempt bump-allocated and then discarded,
/// and the next *successful* commit would persist that inflated cursor — so
/// those ids end up below the durable cursor, referenced by no root and named
/// by no free-list entry, leaked for the life of the store. Worse, the
/// commit-history root would name a tree whose pages died with the failed
/// transaction's dirty set, so the next commit would open a root that was
/// never written.
///
/// So the commit path advances *this* copy and calls [`Self::publish`] only
/// once the A/B header naming these values is durable. Every fallible step
/// before that point may return with no unwind, because there is nothing to
/// unwind — which is also why a fallible check added between materialization
/// and the header write cannot reintroduce the leak.
pub