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 } } }