Replay all audit WAL entries for crash recovery. Returns `(data_lsn, audit_entry_bytes)` pairs in LSN order. The caller deserializes the audit entry bytes and rebuilds the in-memory cache.
(&self)
| 103 | /// Returns `(data_lsn, audit_entry_bytes)` pairs in LSN order. |
| 104 | /// The caller deserializes the audit entry bytes and rebuilds the in-memory cache. |
| 105 | pub fn recover(&self) -> crate::Result<Vec<(u64, Vec<u8>)>> { |
| 106 | let wal = self.wal.lock().map_err(|_| crate::Error::Internal { |
| 107 | detail: "audit WAL lock poisoned".into(), |
| 108 | })?; |
| 109 | |
| 110 | let records = wal.replay().map_err(crate::Error::Wal)?; |
| 111 | |
| 112 | let mut entries = Vec::with_capacity(records.len()); |
| 113 | for record in records { |
| 114 | if record.payload.len() < 8 { |
| 115 | tracing::warn!( |
| 116 | payload_len = record.payload.len(), |
| 117 | "skipping malformed audit WAL record (payload < 8 bytes)" |
| 118 | ); |
| 119 | continue; |
| 120 | } |
| 121 | // Safe: length checked above guarantees exactly 8 bytes. |
| 122 | let data_lsn = u64::from_le_bytes( |
| 123 | record.payload[..8] |
| 124 | .try_into() |
| 125 | .expect("length checked above"), |
| 126 | ); |
| 127 | let audit_bytes = record.payload[8..].to_vec(); |
| 128 | entries.push((data_lsn, audit_bytes)); |
| 129 | } |
| 130 | |
| 131 | Ok(entries) |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | #[cfg(test)] |