shell: confirm trim survivors still serve the volume before deleting (#12590)

* shell: confirm trim survivors still serve the volume before deleting

volume.fix.replication compared the doomed replica's index with every
survivor and then deleted it unconditionally. A survivor that went away
while the indexes were read still let the delete through, leaving fewer
copies than the check had counted. Re-read each survivor's VolumeStatus
after the index checks and skip the trim if any no longer has the volume.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* shell: test a trim whose survivor goes away after its index read

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chris LuandClaude Opus 5.5 authored and GitHub committed 2026-11-12 09:32:37 -08:00
1 parent 4cba4eb7bf
commit a3b68aa37c
2 files changed
+88

No files matched your search

@@ -382,6 +382,18 @@ func (c *commandVolumeFixReplication) deleteOneVolume(commandEnv *CommandEnv, wr
break
}
}
// A survivor can go away while the indexes are read.
for _, replicaB := range replicas {
if checkErr != nil {
break
}
if replicaB.location.dataNode == replica.location.dataNode {
continue
}
if _, checkErr = readVolumeStatus(commandEnv.option.GrpcDialOption, pb.NewServerAddressFromDataNode(replicaB.location.dataNode), needle.VolumeId(replicaB.info.Id)); checkErr != nil {
fmt.Fprintf(writer, "skip deleting volume %d from %s: %s no longer serves it: %v\n", replica.info.Id, replica.location.dataNode.Id, replicaB.location.dataNode.Id, checkErr)
}
}
if checkErr != nil {
continue
}