(
routing: Option<&std::sync::Arc<std::sync::RwLock<RoutingTable>>>,
step: usize,
migration_id: uuid::Uuid,
comp: &Compensation,
)
| 217 | } |
| 218 | |
| 219 | fn apply_compensation( |
| 220 | routing: Option<&std::sync::Arc<std::sync::RwLock<RoutingTable>>>, |
| 221 | step: usize, |
| 222 | migration_id: uuid::Uuid, |
| 223 | comp: &Compensation, |
| 224 | ) -> Result<(), ClusterError> { |
| 225 | let Some(live) = routing else { |
| 226 | // No routing handle attached — log and treat as success. |
| 227 | // This path is only reachable in unit tests without live state. |
| 228 | debug!( |
| 229 | migration_id = %migration_id, |
| 230 | step, |
| 231 | ?comp, |
| 232 | "compensation: no live routing handle, skipping" |
| 233 | ); |
| 234 | return Ok(()); |
| 235 | }; |
| 236 | |
| 237 | let mut rt = live.write().unwrap_or_else(|p| p.into_inner()); |
| 238 | match comp { |
| 239 | Compensation::RemoveLearner { group_id, peer_id } |
| 240 | | Compensation::RemoveVoter { group_id, peer_id } => { |
| 241 | rt.remove_group_member(*group_id, *peer_id); |
| 242 | } |
| 243 | Compensation::RestoreLeaderHint { group_id, peer_id } => { |
| 244 | rt.set_leader(*group_id, *peer_id); |
| 245 | } |
| 246 | Compensation::RemoveGhostStub { vshard_id: _ } => { |
| 247 | // Ghost stub removal is handled by the caller's ghost_table; |
| 248 | // the routing table has no ghost state. |
| 249 | } |
| 250 | } |
| 251 | drop(rt); |
| 252 | |
| 253 | debug!( |
| 254 | migration_id = %migration_id, |
| 255 | step, |
| 256 | ?comp, |
| 257 | "compensation applied" |
| 258 | ); |
| 259 | Ok(()) |
| 260 | } |
no test coverage detected