package topology import ( "fmt" "github.com/seaweedfs/seaweedfs/weed/storage/needle" ) // maxTrackedLocations is how many replicas of one volume can carry per-node // state. Beyond that the flags are simply not recorded, which loses a read-only // or oversized marking rather than misreporting one. const maxTrackedLocations = 32 type VolumeLocationList struct { list []*DataNode // readOnly and oversized mirror list by index, so the per-node state that // used to need an index of its own rides along with the location it // describes. Both are only ever asked whether any node reports them. readOnly uint32 oversized uint32 } func NewVolumeLocationList() *VolumeLocationList { return &VolumeLocationList{} } func (dnll *VolumeLocationList) String() string { return fmt.Sprintf("%v", dnll.list) } func (dnll *VolumeLocationList) Copy() *VolumeLocationList { list := make([]*DataNode, len(dnll.list)) copy(list, dnll.list) return &VolumeLocationList{ list: list, readOnly: dnll.readOnly, oversized: dnll.oversized, } } func (dnll *VolumeLocationList) indexOf(loc *DataNode) int { for i, dnl := range dnll.list { if loc.Ip == dnl.Ip && loc.Port == dnl.Port { return i } } return -1 } func setFlag(flags *uint32, index int, on bool) { if index < 0 || index >= maxTrackedLocations { return } if on { *flags |= 1 << uint(index) } else { *flags &^= 1 << uint(index) } } func hasFlag(flags uint32, index int) bool { return index >= 0 && index < maxTrackedLocations && flags&(1<= maxTrackedLocations { return } low := *flags & (1<> uint(index+1) << uint(index) *flags = low | high } // SetReadOnly records whether loc reports the volume read-only. func (dnll *VolumeLocationList) SetReadOnly(loc *DataNode, readOnly bool) { setFlag(&dnll.readOnly, dnll.indexOf(loc), readOnly) } // AnyReadOnly reports whether any location has the volume read-only. func (dnll *VolumeLocationList) AnyReadOnly() bool { return dnll != nil && dnll.readOnly != 0 } // SetOversized records whether loc reports the volume past the size limit. func (dnll *VolumeLocationList) SetOversized(loc *DataNode, oversized bool) { setFlag(&dnll.oversized, dnll.indexOf(loc), oversized) } // AnyOversized reports whether any location has the volume past the size limit. func (dnll *VolumeLocationList) AnyOversized() bool { return dnll != nil && dnll.oversized != 0 } func (dnll *VolumeLocationList) Head() *DataNode { //mark first node as master volume if dnll.Length() == 0 { return nil } return dnll.list[0] } func (dnll *VolumeLocationList) Rest() []*DataNode { //mark first node as master volume return dnll.list[1:] } func (dnll *VolumeLocationList) Length() int { if dnll == nil { return 0 } return len(dnll.list) } // Set adds loc, or replaces the entry at the same address, returning the node it // displaced. Two volume servers can share an address -- GetOrCreateDataNode keys // on the reported id and refuses to merge a new id onto an address an older node // still claims -- so the displaced node is not necessarily loc, and callers // tracking which node a volume is reachable through must move their bookkeeping // off it rather than assume loc already owned the entry. func (dnll *VolumeLocationList) Set(loc *DataNode) (displaced *DataNode) { for i := 0; i < len(dnll.list); i++ { if loc.Ip == dnll.list[i].Ip && loc.Port == dnll.list[i].Port { displaced = dnll.list[i] dnll.list[i] = loc return displaced } } dnll.list = append(dnll.list, loc) return nil } // Remove drops the entry at loc's address and returns the node removed, or nil // if the volume was not reachable there. As with Set, the removed node is // matched by address and need not be loc. func (dnll *VolumeLocationList) Remove(loc *DataNode) (removed *DataNode) { for i, dnl := range dnll.list { if loc.Ip == dnl.Ip && loc.Port == dnl.Port { dnll.list = append(dnll.list[:i], dnll.list[i+1:]...) removeFlag(&dnll.readOnly, i) removeFlag(&dnll.oversized, i) return dnl } } return nil } func (dnll *VolumeLocationList) Refresh(freshThreshHold int64) { var changed bool for _, dnl := range dnll.list { if dnl.LastSeen < freshThreshHold { changed = true break } } if !changed { return } // The flags index the locations, so dropping some means rebuilding both // rather than leaving bits describing whoever moved into their place. var l []*DataNode var readOnly, oversized uint32 for i, dnl := range dnll.list { if dnl.LastSeen < freshThreshHold { continue } setFlag(&readOnly, len(l), hasFlag(dnll.readOnly, i)) setFlag(&oversized, len(l), hasFlag(dnll.oversized, i)) l = append(l, dnl) } dnll.list, dnll.readOnly, dnll.oversized = l, readOnly, oversized } // Stats returns logic size and count. Both subtractions are clamped: the // deleted counters are maintained apart from the totals they come off and can // transiently exceed them. func (dnll *VolumeLocationList) Stats(vid needle.VolumeId, freshThreshHold int64) (size uint64, fileCount int) { for _, dnl := range dnll.list { if dnl.LastSeen < freshThreshHold { vinfo, err := dnl.GetVolumesById(vid) if err == nil { if vinfo.Size > vinfo.DeletedByteCount { size = vinfo.Size - vinfo.DeletedByteCount } if vinfo.FileCount > vinfo.DeleteCount { fileCount = int(vinfo.FileCount - vinfo.DeleteCount) } return size, fileCount } } } return 0, 0 }