(&mut self, rec: RecoveryRecord)
| 71 | } |
| 72 | |
| 73 | pub fn apply_record(&mut self, rec: RecoveryRecord) -> Result<(), RecoveryError> { |
| 74 | let durable = self.store.manifest().durable_lsn; |
| 75 | match rec { |
| 76 | RecoveryRecord::Put { lsn, payload } => { |
| 77 | if lsn <= durable { |
| 78 | self.stats.puts_skipped += 1; |
| 79 | return Ok(()); |
| 80 | } |
| 81 | let cells: Vec<ArrayPutCell> = payload.cells; |
| 82 | // Invariant: every cell's `system_from_ms` must be the |
| 83 | // leader-stamped value from the WAL payload. The recovery |
| 84 | // path MUST NOT call `HlcClock::now()` or any wall-clock |
| 85 | // function to derive a replacement — that would cause the |
| 86 | // replay to assign a different stamp than the leader did, |
| 87 | // breaking cross-replica consistency at any `system_as_of` |
| 88 | // cutoff that falls between the two stamps. |
| 89 | debug_assert!( |
| 90 | cells.iter().all(|c| c.system_from_ms > 0), |
| 91 | "recovery: ArrayPutCell.system_from_ms must be the leader-stamped \ |
| 92 | value (> 0), not a default — check that the WAL decoder does not \ |
| 93 | zero-fill this field on version mismatch" |
| 94 | ); |
| 95 | stamp_put_cells(self.store, cells, lsn)?; |
| 96 | self.stats.puts_applied += 1; |
| 97 | } |
| 98 | RecoveryRecord::Delete { lsn, payload } => { |
| 99 | if lsn <= durable { |
| 100 | self.stats.deletes_skipped += 1; |
| 101 | return Ok(()); |
| 102 | } |
| 103 | // Same invariant for deletes: `system_from_ms` is the |
| 104 | // tombstone version key; it must come from the WAL payload, |
| 105 | // not from the local clock. |
| 106 | debug_assert!( |
| 107 | payload.cells.iter().all(|c| c.system_from_ms > 0), |
| 108 | "recovery: ArrayDeleteCell.system_from_ms must be the leader-stamped \ |
| 109 | value (> 0), not a default" |
| 110 | ); |
| 111 | stamp_delete_cells(self.store, payload.cells, lsn)?; |
| 112 | self.stats.deletes_applied += 1; |
| 113 | } |
| 114 | RecoveryRecord::Flush { lsn, .. } => { |
| 115 | let m = self.store.manifest_mut(); |
| 116 | m.durable_lsn = m.durable_lsn.max(lsn); |
| 117 | } |
| 118 | } |
| 119 | Ok(()) |
| 120 | } |
| 121 | } |
nothing calls this directly
no test coverage detected