Skip to main content

icydb_core/db/commit/
guard.rs

1use crate::error::{ErrorClass, ErrorOrigin, InternalError};
2use std::panic::{AssertUnwindSafe, catch_unwind};
3
4///
5/// CommitApplyGuard
6///
7/// Executor-internal guard for the commit-marker apply phase.
8///
9/// This guard is strictly transitional infrastructure:
10/// - Durable atomicity is owned by commit markers + recovery replay.
11/// - Rollback closures here are best-effort, in-process cleanup only.
12/// - This type does not provide transactional semantics or durable undo.
13/// - New code must not rely on closure-based rollback for correctness.
14///
15/// Long-term direction:
16/// marker application should become fully mechanical/idempotent so this guard
17/// can be removed without changing user-visible correctness.
18///
19
20pub struct CommitApplyGuard {
21    phase: &'static str,
22    finished: bool,
23    rollbacks: Vec<Box<dyn FnOnce()>>,
24}
25
26impl CommitApplyGuard {
27    pub(crate) const fn new(phase: &'static str) -> Self {
28        Self {
29            phase,
30            finished: false,
31            rollbacks: Vec::new(),
32        }
33    }
34
35    pub(crate) fn record_rollback(&mut self, rollback: impl FnOnce() + 'static) {
36        self.rollbacks.push(Box::new(rollback));
37    }
38
39    pub(crate) fn finish(mut self) -> Result<(), InternalError> {
40        if self.finished {
41            return Err(InternalError::new(
42                ErrorClass::InvariantViolation,
43                ErrorOrigin::Executor,
44                format!(
45                    "commit apply guard invariant violated: finish called twice ({})",
46                    self.phase
47                ),
48            ));
49        }
50
51        self.finished = true;
52        self.rollbacks.clear();
53        Ok(())
54    }
55
56    fn rollback_best_effort(&mut self) {
57        if self.finished {
58            // Defensive: rollback after finish is a logic error, but must not panic.
59            return;
60        }
61
62        // Transitional cleanup only:
63        // - reverse order to mirror write application
64        // - never unwind past this boundary
65        while let Some(rollback) = self.rollbacks.pop() {
66            let _ = catch_unwind(AssertUnwindSafe(rollback));
67        }
68    }
69}
70
71impl Drop for CommitApplyGuard {
72    fn drop(&mut self) {
73        if !self.finished {
74            self.rollback_best_effort();
75        }
76    }
77}