Apply a `MigrationCheckpoint` entry against the shared state table. Returns `Err` on CRC32C mismatch (fatal — corruption is louder than data loss) and on storage failures. Idempotent: if `(migration_id, phase, attempt)` is already present, the call is a no-op.
(
table: &SharedMigrationStateTable,
migration_id: uuid::Uuid,
_phase: MigrationPhaseTag,
attempt: u32,
payload: crate::metadata_group::migration_state::MigrationCheckpointPayload,
| 156 | /// loss) and on storage failures. Idempotent: if |
| 157 | /// `(migration_id, phase, attempt)` is already present, the call is a no-op. |
| 158 | pub fn apply_migration_checkpoint( |
| 159 | table: &SharedMigrationStateTable, |
| 160 | migration_id: uuid::Uuid, |
| 161 | _phase: MigrationPhaseTag, |
| 162 | attempt: u32, |
| 163 | payload: crate::metadata_group::migration_state::MigrationCheckpointPayload, |
| 164 | expected_crc: u32, |
| 165 | ts_ms: u64, |
| 166 | ) -> Result<(), ClusterError> { |
| 167 | // Validate CRC32C against the encoded payload bytes. |
| 168 | let actual_crc = payload.crc32c()?; |
| 169 | if actual_crc != expected_crc { |
| 170 | return Err(ClusterError::MigrationCheckpoint( |
| 171 | MigrationCheckpointError::Crc32cMismatch { |
| 172 | migration_id, |
| 173 | expected: expected_crc, |
| 174 | actual: actual_crc, |
| 175 | }, |
| 176 | )); |
| 177 | } |
| 178 | |
| 179 | let row = PersistedMigrationCheckpoint { |
| 180 | migration_id: migration_id.hyphenated().to_string(), |
| 181 | attempt, |
| 182 | payload, |
| 183 | crc32c: actual_crc, |
| 184 | ts_ms, |
| 185 | }; |
| 186 | |
| 187 | let mut guard = table.lock().unwrap_or_else(|p| p.into_inner()); |
| 188 | guard.upsert(row) |
| 189 | } |
| 190 | |
| 191 | /// Apply a `MigrationAbort` entry against the shared state table and |
| 192 | /// live routing table. |