quill_sql/recovery/
analysis.rs

1use 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}