Files
seaweedfs/weed/topology/volume_index_digest.go
T

85 lines
2.8 KiB
Go

package topology
import (
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
)
// VolumeIdDigestHash spreads a volume id across the whole 64-bit range so that
// xoring a set of them together does not collide for the near-consecutive ids a
// cluster actually allocates.
func VolumeIdDigestHash(vid needle.VolumeId) uint64 {
h := uint64(vid) + 0x9E3779B97F4A7C15
h = (h ^ (h >> 30)) * 0xBF58476D1CE4E5B9
h = (h ^ (h >> 27)) * 0x94D049BB133111EB
return h ^ (h >> 31)
}
// VolumeIndexDigests returns the digest of the volumes the node's disks hold and
// the digest of the volumes the lookup index will serve from that node.
//
// The master keeps those two indexes separately, and a disconnect racing a
// reconnect has been seen to drop a volume from the lookup index while leaving
// it on the node: the volume stays visible in volume.list and the admin UI while
// LookupVolume answers "volume id not found". A volume server cannot detect that
// -- its own report is identical either way -- so the master has to notice it
// on its own rather than rely on the heartbeat digest.
func (dn *DataNode) VolumeIndexDigests() (held, servable uint64) {
dn.RLock()
for _, c := range dn.children {
held ^= c.(*Disk).VolumeIdDigest()
}
dn.RUnlock()
return held, dn.lookupDigest.Load()
}
// HasConsistentVolumeIndex reports whether every volume on the node is reachable
// through the lookup index, and nothing else is.
func (dn *DataNode) HasConsistentVolumeIndex() bool {
held, servable := dn.VolumeIndexDigests()
return held == servable
}
// releaseLookupOwnership clears the lookup digest bit of every location this
// layout serves. Used when the layout is dropped wholesale with its collection
// rather than volume by volume.
func (vl *VolumeLayout) releaseLookupOwnership() {
vl.accessLock.Lock()
defer vl.accessLock.Unlock()
for vid, location := range vl.vid2location {
for _, dn := range location.list {
moveLookupOwnership(vid, dn, nil)
}
}
vl.vid2location = make(map[needle.VolumeId]*VolumeLocationList)
vl.dropped = true
}
// moveLookupOwnership transfers the digest bit for vid from the node a lookup
// entry used to name to the node it now names. Either may be nil, for an entry
// being created or dropped.
func moveLookupOwnership(vid needle.VolumeId, from, to *DataNode) {
if from == to {
return
}
if from != nil {
from.trackLookupChange(vid)
}
if to != nil {
to.trackLookupChange(vid)
}
}
// trackLookupChange records that vid became reachable through this node, or
// stopped being: xor is its own inverse, so both are the same update. Volume
// layouts for different collections share the node, so it cannot assume the
// caller's lock.
func (dn *DataNode) trackLookupChange(vid needle.VolumeId) {
delta := VolumeIdDigestHash(vid)
for {
old := dn.lookupDigest.Load()
if dn.lookupDigest.CompareAndSwap(old, old^delta) {
return
}
}
}