master: repair the lookup index after a vacuum that raced a disconnect (#10226)

* master: re-create a vacuum-committed volume the index has lost

SetVolumeAvailable dereferenced vid2location[vid] with no nil check. A
disconnect during a long vacuum can drop a single-replica volume from the
lookup index while it stays on the node; the commit then panics the master
instead of re-registering it. Re-create the entry, and seed its size tracking
so assigns are counted right away rather than after the next heartbeat.

* master: re-register a vacuumed volume the index lost on mark-writable

The maintenance worker re-enables a volume by marking it writable after the
vacuum commit, but VolumeMarkReadonly only updated nodes the lookup index
already knew. If a disconnect race dropped the volume from the index during the
vacuum, that path was a no-op and the volume stayed "not found" until the next
full heartbeat healed it. Re-register it from the node that still holds it.
This commit is contained in:
Chris Lu authored and GitHub committed 2026-07-03 14:42:33 -07:00
1 parent 60e7b30009
commit a6effe3cfb
3 files changed
+100 -16

No files matched your search

+19 -3
View File
@@ -15,6 +15,7 @@ import (
"github.com/seaweedfs/raft"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/pb"
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
"github.com/seaweedfs/seaweedfs/weed/security"
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
@@ -323,18 +324,33 @@ func (ms *MasterServer) VolumeMarkReadonly(ctx context.Context, req *master_pb.V
replicaPlacement, _ := super_block.NewReplicaPlacementFromByte(byte(req.ReplicaPlacement))
vl := ms.Topo.GetVolumeLayout(req.Collection, replicaPlacement, needle.LoadTTLFromUint32(req.Ttl), types.ToDiskType(req.DiskType))
dataNodes := ms.Topo.Lookup(req.Collection, needle.VolumeId(req.VolumeId))
vid := needle.VolumeId(req.VolumeId)
dataNodes := ms.Topo.Lookup(req.Collection, vid)
found := false
for _, dn := range dataNodes {
if dn.Ip == req.Ip && dn.Port == int(req.Port) {
found = true
if req.IsReadonly {
vid := needle.VolumeId(req.VolumeId)
vl.SetVolumeReadOnly(dn, vid)
if pending := vl.GetPendingSize(vid); pending > 0 {
glog.V(0).Infof("volume %d marked readonly with %d pending bytes", vid, pending)
}
} else {
vl.SetVolumeWritable(dn, needle.VolumeId(req.VolumeId))
vl.SetVolumeWritable(dn, vid)
}
}
}
// A vacuum worker marks the volume writable after a successful commit. If the
// index lost it meanwhile (e.g. a disconnect race during the vacuum), the loop
// found nothing to update; re-register it from the node that still holds it so
// LookupVolume stops returning "not found".
if !found && !req.IsReadonly {
if dn := ms.Topo.LookupDataNodeByAddress(pb.NewServerAddress(req.Ip, int(req.Port), 0)); dn != nil {
if vi, err := dn.GetVolumesById(vid); err == nil {
ms.Topo.RegisterVolumeLayout(vi, dn)
glog.V(0).Infof("volume %d re-registered from %s:%d, was missing from the lookup index at mark-writable", vid, req.Ip, req.Port)
}
}
}