mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-20 13:30:46 +02:00
85 lines
2.8 KiB
Go
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
|
|
}
|
|
}
|
|
}
|