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
//! WAL checkpoint admission and execution.
//!
//! Physical source admission is separate from the executor's backfill/reset
//! state machine so even a zero-backfill plan is checked before any target
//! callback. The caller still owns generation/append coordination throughout
//! this operation; these read-only checks do not replace that coordination.
#[path = "checkpoint_admission.rs"]
mod admission;
#[path = "checkpoint_executor_impl.rs"]
mod implementation;
pub use implementation::{CheckpointExecutionResult, CheckpointTarget, CheckpointTargetFuture};
use fsqlite_error::Result;
use fsqlite_types::cx::Cx;
use fsqlite_vfs::VfsFile;
use crate::checkpoint::{CheckpointMode, CheckpointPostAction, CheckpointState, plan_checkpoint};
use crate::wal::WalFile;
/// Validate the accepted WAL snapshot, then backfill and optionally reset it.
///
/// Unlike a cached frame-count comparison, admission detects a newer committed
/// suffix written through another handle and a replaced/truncated generation.
/// Rejection changes neither the WAL handle nor the target. An already empty,
/// headerless WAL is a no-op, not permission to write a synthetic generation.
///
/// # Errors
/// Returns source corruption, stale-plan, cancellation, or I/O errors before
/// mutation when admission fails. Execution errors retain the existing
/// backfill/reset ownership and recovery semantics.
pub async fn execute_checkpoint<F: VfsFile>(
cx: &Cx,
wal: &mut WalFile<F>,
mode: CheckpointMode,
state: CheckpointState,
target: &mut impl CheckpointTarget,
) -> Result<CheckpointExecutionResult> {
if admission::validate_source(cx, wal, mode, state).await? {
// There is no physical generation to publish or reset. In particular,
// TRUNCATE on an already zero-byte WAL must not create a new header.
let mut plan = plan_checkpoint(mode, state);
plan.post_action = CheckpointPostAction::None;
return Ok(CheckpointExecutionResult {
plan,
frames_backfilled: 0,
db_size_pages: None,
wal_was_reset: false,
});
}
implementation::execute_checkpoint(cx, wal, mode, state, target).await
}