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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Test-only migration fault-injection seam (VS-4.0.4 work 4.05, issue #46).
//!
//! Compiled **only** under `--features fault-injection` — never in default or
//! release builds, so the production migration path is byte-identical when the
//! feature is off (each call site is a statement-level `#[cfg(...)]` that
//! disappears entirely). This lets a test ARM a simulated crash at a specific
//! migration boundary; the migration path calls [`maybe_inject`] at each of the
//! five boundaries, which either `panic!`s (in-process — Drop runs, so the redb
//! write-txn aborts gracefully / MVCC rolls back) or `raise(SIGKILL)`s (the one
//! subprocess crash-fidelity test — no Drop, forcing redb's file-level recovery).
//!
//! The armed state is **thread-local**: the migration runs synchronously on the
//! same thread that calls `PulseDB::open`, so arming on that thread is sufficient
//! and there is zero cross-test contamination (each `#[test]` gets its own
//! thread, hence its own register).
//!
//! This module changes NO migration behavior — it only observes boundaries. The
//! boundary ordering/semantics are owned by 4.03 (registry re-encode loop) and
//! 4.04 (reordered pre-txn path + backup/fsync + marker rules).
use Cell;
/// The five migration boundaries a test can crash at.
///
/// Two are **pre-txn** (a crash there is NOT covered by a redb txn abort):
/// - [`Boundary::MidBackupPreFsync`] — inside `backup_once`, after the sidecar
/// bytes are copied but before the `#53c` fsync makes them durable.
/// - [`Boundary::PostRedbUpgrade`] — after the destructive in-place redb v2→v3
/// upgrade returns, before the redb-4.1 reopen.
///
/// Three are **write-txn** (a crash before `commit()` rolls the whole txn back):
/// - [`Boundary::PreReencode`] — before the registry-driven codec re-encode pass.
/// - [`Boundary::MidReencode`] — inside the re-encode loop, after ≥1 table pass.
/// - [`Boundary::PreMarker`] — after re-encode, just before the marker insert
/// (the atomic commit point).
/// How to crash when the armed boundary is reached.
thread_local!
/// Arm a crash at `boundary` with `action`, on the current thread.
/// Clear any armed injection on the current thread.
/// RAII: arm on construction, disarm on drop — so a panicking (in-process)
/// injection can't leave the register armed for a later assertion on the same
/// thread. Cheap; tests may also call [`disarm`] explicitly.
;
/// Called by the migration path at each boundary. When the armed boundary
/// matches, crash per the armed action; otherwise a no-op.
pub