quill_sql/recovery/
analysis.rs1use crate::error::QuillSQLResult;
2use crate::recovery::control_file::ControlFileSnapshot;
3use crate::recovery::wal::codec::{
4 decode_checkpoint, CheckpointPayload, ResourceManagerId, WalFrame,
5};
6use crate::recovery::Lsn;
7
8#[derive(Debug, Default, Clone)]
9pub struct AnalysisResult {
10 pub start_lsn: Lsn,
11 pub has_frames: bool,
12}
13
14pub struct AnalysisPass {
15 latest: Option<(Lsn, CheckpointPayload)>,
16 snapshot: Option<ControlFileSnapshot>,
17 has_frames: bool,
18}
19
20impl AnalysisPass {
21 pub fn new(snapshot: Option<ControlFileSnapshot>) -> Self {
22 Self {
23 latest: None,
24 snapshot,
25 has_frames: false,
26 }
27 }
28
29 pub fn observe(&mut self, frame: &WalFrame) {
30 self.has_frames = true;
31 if frame.rmid == ResourceManagerId::Checkpoint {
32 if let Ok(payload) = decode_checkpoint(&frame.body) {
33 self.latest = Some((frame.lsn, payload));
34 }
35 }
36 }
37
38 pub fn finalize(self) -> QuillSQLResult<AnalysisResult> {
39 let start_lsn = if let Some((checkpoint_lsn, payload)) = &self.latest {
40 self.snapshot
41 .map(|snap| snap.checkpoint_redo_start)
42 .filter(|redo| *redo >= payload.last_lsn && *redo <= *checkpoint_lsn)
43 .unwrap_or_else(|| {
44 payload
45 .dpt
46 .iter()
47 .map(|(_, lsn)| *lsn)
48 .min()
49 .unwrap_or(payload.last_lsn)
50 })
51 } else {
52 0
53 };
54
55 Ok(AnalysisResult {
56 start_lsn,
57 has_frames: self.has_frames,
58 })
59 }
60}