Analysis phase: scan WAL to determine transaction states
(&mut self, report: &mut RecoveryReport)
| 97 | |
| 98 | /// Analysis phase: scan WAL to determine transaction states |
| 99 | fn analysis_phase(&mut self, report: &mut RecoveryReport) -> Result<(), RecoveryError> { |
| 100 | log::debug!("🔍 Starting WAL analysis phase..."); |
| 101 | |
| 102 | // Read all WAL files |
| 103 | let mut file_number = 1u64; |
| 104 | loop { |
| 105 | match self.wal.read_wal_file(file_number) { |
| 106 | Ok(entries) => { |
| 107 | report.total_wal_entries += entries.len(); |
| 108 | |
| 109 | for entry in entries { |
| 110 | self.process_entry_analysis(entry)?; |
| 111 | } |
| 112 | |
| 113 | file_number += 1; |
| 114 | } |
| 115 | Err(WALError::IOError(msg)) if msg.contains("not found") => { |
| 116 | // No more WAL files |
| 117 | break; |
| 118 | } |
| 119 | Err(e) => { |
| 120 | return Err(RecoveryError::WALRead(e.to_string())); |
| 121 | } |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | // Determine which transactions need recovery |
| 126 | for (txn_id, state) in &self.recovered_transactions { |
| 127 | match state.status { |
| 128 | RecoveryStatus::InProgress => { |
| 129 | report.incomplete_transactions.push(*txn_id); |
| 130 | } |
| 131 | RecoveryStatus::Committed => { |
| 132 | report.committed_transactions.push(*txn_id); |
| 133 | } |
| 134 | RecoveryStatus::RolledBack => { |
| 135 | report.rolled_back_transactions.push(*txn_id); |
| 136 | } |
| 137 | _ => {} |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | log::debug!( |
| 142 | "✅ Analysis phase complete: {} transactions to recover", |
| 143 | report.incomplete_transactions.len() + report.committed_transactions.len() |
| 144 | ); |
| 145 | |
| 146 | Ok(()) |
| 147 | } |
| 148 | |
| 149 | /// Process a WAL entry during analysis |
| 150 | fn process_entry_analysis(&mut self, entry: WALEntry) -> Result<(), RecoveryError> { |
no test coverage detected