mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2025-04-05 20:52:50 +08:00
[master] refactor func ShouldGrowVolumes (#5884)
This commit is contained in:
parent
eb02946c97
commit
67a252ee8a
@ -27,7 +27,7 @@ func (ms *MasterServer) DoAutomaticVolumeGrow(req *topology.VolumeGrowRequest) {
|
|||||||
newVidLocations, err := ms.vg.AutomaticGrowByType(req.Option, ms.grpcDialOption, ms.Topo, req.Count)
|
newVidLocations, err := ms.vg.AutomaticGrowByType(req.Option, ms.grpcDialOption, ms.Topo, req.Count)
|
||||||
glog.V(1).Infoln("finished automatic volume grow, cost ", time.Now().Sub(start))
|
glog.V(1).Infoln("finished automatic volume grow, cost ", time.Now().Sub(start))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Warningf("automatic volume grow %s: %+v", req.Option, err)
|
glog.V(1).Infof("automatic volume grow failed: %+v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, newVidLocation := range newVidLocations {
|
for _, newVidLocation := range newVidLocations {
|
||||||
@ -38,19 +38,37 @@ func (ms *MasterServer) DoAutomaticVolumeGrow(req *topology.VolumeGrowRequest) {
|
|||||||
func (ms *MasterServer) ProcessGrowRequest() {
|
func (ms *MasterServer) ProcessGrowRequest() {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(14*time.Minute + time.Duration(120*rand.Float32())*time.Second)
|
|
||||||
if !ms.Topo.IsLeader() {
|
if !ms.Topo.IsLeader() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, vl := range ms.Topo.ListVolumeLayouts() {
|
dcs := ms.Topo.ListDataCenters()
|
||||||
if !vl.HasGrowRequest() && vl.ShouldGrowVolumes(&topology.VolumeGrowOption{}) {
|
for _, vlc := range ms.Topo.ListVolumeLayoutCollections() {
|
||||||
|
vl := vlc.VolumeLayout
|
||||||
|
if vl.HasGrowRequest() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if vl.ShouldGrowVolumes(vlc.Collection) {
|
||||||
vl.AddGrowRequest()
|
vl.AddGrowRequest()
|
||||||
ms.volumeGrowthRequestChan <- &topology.VolumeGrowRequest{
|
ms.volumeGrowthRequestChan <- &topology.VolumeGrowRequest{
|
||||||
Option: vl.ToGrowOption(),
|
Option: vlc.ToGrowOption(),
|
||||||
Count: vl.GetLastGrowCount(),
|
Count: vl.GetLastGrowCount(),
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for _, dc := range dcs {
|
||||||
|
if vl.ShouldGrowVolumesByDataNode("DataCenter", dc) {
|
||||||
|
vl.AddGrowRequest()
|
||||||
|
volumeGrowOption := vlc.ToGrowOption()
|
||||||
|
volumeGrowOption.DataCenter = dc
|
||||||
|
ms.volumeGrowthRequestChan <- &topology.VolumeGrowRequest{
|
||||||
|
Option: volumeGrowOption,
|
||||||
|
Count: vl.GetLastGrowCount(),
|
||||||
|
Force: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
time.Sleep(14*time.Minute + time.Duration(120*rand.Float32())*time.Second)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
@ -81,19 +99,20 @@ func (ms *MasterServer) ProcessGrowRequest() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// not atomic but it's okay
|
// not atomic but it's okay
|
||||||
if !found && vl.ShouldGrowVolumes(option) {
|
if found || (!req.Force && !vl.ShouldGrowVolumes(req.Option.Collection)) {
|
||||||
filter.Store(req, nil)
|
|
||||||
// we have lock called inside vg
|
|
||||||
go func(req *topology.VolumeGrowRequest, vl *topology.VolumeLayout) {
|
|
||||||
ms.DoAutomaticVolumeGrow(req)
|
|
||||||
vl.DoneGrowRequest()
|
|
||||||
filter.Delete(req)
|
|
||||||
}(req, vl)
|
|
||||||
} else {
|
|
||||||
glog.V(4).Infoln("discard volume grow request")
|
glog.V(4).Infoln("discard volume grow request")
|
||||||
time.Sleep(time.Millisecond * 211)
|
time.Sleep(time.Millisecond * 211)
|
||||||
vl.DoneGrowRequest()
|
vl.DoneGrowRequest()
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filter.Store(req, nil)
|
||||||
|
// we have lock called inside vg
|
||||||
|
go func(req *topology.VolumeGrowRequest, vl *topology.VolumeLayout) {
|
||||||
|
ms.DoAutomaticVolumeGrow(req)
|
||||||
|
vl.DoneGrowRequest()
|
||||||
|
filter.Delete(req)
|
||||||
|
}(req, vl)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -70,13 +70,21 @@ var (
|
|||||||
Help: "replica placement mismatch",
|
Help: "replica placement mismatch",
|
||||||
}, []string{"collection", "id"})
|
}, []string{"collection", "id"})
|
||||||
|
|
||||||
MasterVolumeLayout = prometheus.NewGaugeVec(
|
MasterVolumeLayoutWritable = prometheus.NewGaugeVec(
|
||||||
prometheus.GaugeOpts{
|
prometheus.GaugeOpts{
|
||||||
Namespace: Namespace,
|
Namespace: Namespace,
|
||||||
Subsystem: "master",
|
Subsystem: "master",
|
||||||
Name: "volume_layout_total",
|
Name: "volume_layout_writable",
|
||||||
Help: "Number of volumes in volume layouts",
|
Help: "Number of writable volumes in volume layouts",
|
||||||
}, []string{"collection", "dataCenter", "type"})
|
}, []string{"collection", "disk", "rp", "ttl"})
|
||||||
|
|
||||||
|
MasterVolumeLayoutCrowded = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Namespace: Namespace,
|
||||||
|
Subsystem: "master",
|
||||||
|
Name: "volume_layout_crowded",
|
||||||
|
Help: "Number of crowded volumes in volume layouts",
|
||||||
|
}, []string{"collection", "disk", "rp", "ttl"})
|
||||||
|
|
||||||
MasterPickForWriteErrorCounter = prometheus.NewCounter(
|
MasterPickForWriteErrorCounter = prometheus.NewCounter(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
@ -281,7 +289,8 @@ func init() {
|
|||||||
Gather.MustRegister(MasterReceivedHeartbeatCounter)
|
Gather.MustRegister(MasterReceivedHeartbeatCounter)
|
||||||
Gather.MustRegister(MasterLeaderChangeCounter)
|
Gather.MustRegister(MasterLeaderChangeCounter)
|
||||||
Gather.MustRegister(MasterReplicaPlacementMismatch)
|
Gather.MustRegister(MasterReplicaPlacementMismatch)
|
||||||
Gather.MustRegister(MasterVolumeLayout)
|
Gather.MustRegister(MasterVolumeLayoutWritable)
|
||||||
|
Gather.MustRegister(MasterVolumeLayoutCrowded)
|
||||||
|
|
||||||
Gather.MustRegister(FilerRequestCounter)
|
Gather.MustRegister(FilerRequestCounter)
|
||||||
Gather.MustRegister(FilerHandlerCounter)
|
Gather.MustRegister(FilerHandlerCounter)
|
||||||
|
@ -329,7 +329,7 @@ func (t *Topology) UnRegisterVolumeLayout(v storage.VolumeInfo, dn *DataNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Topology) DataCenterExists(dcName string) bool {
|
func (t *Topology) DataCenterExists(dcName string) bool {
|
||||||
return dcName == "" || t.GetOrCreateDataCenter(dcName) != nil
|
return dcName == "" || t.GetDataCenter(dcName) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Topology) GetDataCenter(dcName string) (dc *DataCenter) {
|
func (t *Topology) GetDataCenter(dcName string) (dc *DataCenter) {
|
||||||
@ -358,6 +358,15 @@ func (t *Topology) GetOrCreateDataCenter(dcName string) *DataCenter {
|
|||||||
return dc
|
return dc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Topology) ListDataCenters() (dcs []string) {
|
||||||
|
t.RLock()
|
||||||
|
defer t.RUnlock()
|
||||||
|
for _, c := range t.children {
|
||||||
|
dcs = append(dcs, string(c.(*DataCenter).Id()))
|
||||||
|
}
|
||||||
|
return dcs
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Topology) SyncDataNodeRegistration(volumes []*master_pb.VolumeInformationMessage, dn *DataNode) (newVolumes, deletedVolumes []storage.VolumeInfo) {
|
func (t *Topology) SyncDataNodeRegistration(volumes []*master_pb.VolumeInformationMessage, dn *DataNode) (newVolumes, deletedVolumes []storage.VolumeInfo) {
|
||||||
// convert into in memory struct storage.VolumeInfo
|
// convert into in memory struct storage.VolumeInfo
|
||||||
var volumeInfos []storage.VolumeInfo
|
var volumeInfos []storage.VolumeInfo
|
||||||
|
@ -13,6 +13,11 @@ type TopologyInfo struct {
|
|||||||
Layouts []VolumeLayoutInfo `json:"Layouts"`
|
Layouts []VolumeLayoutInfo `json:"Layouts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VolumeLayoutCollection struct {
|
||||||
|
Collection string
|
||||||
|
VolumeLayout *VolumeLayout
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Topology) ToInfo() (info TopologyInfo) {
|
func (t *Topology) ToInfo() (info TopologyInfo) {
|
||||||
info.Max = t.diskUsages.GetMaxVolumeCount()
|
info.Max = t.diskUsages.GetMaxVolumeCount()
|
||||||
info.Free = t.diskUsages.FreeSpace()
|
info.Free = t.diskUsages.FreeSpace()
|
||||||
@ -42,10 +47,12 @@ func (t *Topology) ToInfo() (info TopologyInfo) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Topology) ListVolumeLayouts() (volumeLayouts []*VolumeLayout) {
|
func (t *Topology) ListVolumeLayoutCollections() (volumeLayouts []*VolumeLayoutCollection) {
|
||||||
for _, col := range t.collectionMap.Items() {
|
for _, col := range t.collectionMap.Items() {
|
||||||
for _, volumeLayout := range col.(*Collection).storageType2VolumeLayout.Items() {
|
for _, volumeLayout := range col.(*Collection).storageType2VolumeLayout.Items() {
|
||||||
volumeLayouts = append(volumeLayouts, volumeLayout.(*VolumeLayout))
|
volumeLayouts = append(volumeLayouts,
|
||||||
|
&VolumeLayoutCollection{col.(*Collection).Name, volumeLayout.(*VolumeLayout)},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return volumeLayouts
|
return volumeLayouts
|
||||||
|
@ -28,6 +28,7 @@ This package is created to resolve these replica placement issues:
|
|||||||
type VolumeGrowRequest struct {
|
type VolumeGrowRequest struct {
|
||||||
Option *VolumeGrowOption
|
Option *VolumeGrowOption
|
||||||
Count uint32
|
Count uint32
|
||||||
|
Force bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type volumeGrowthStrategy struct {
|
type volumeGrowthStrategy struct {
|
||||||
|
@ -3,7 +3,7 @@ package topology
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/seaweedfs/seaweedfs/weed/stats"
|
"github.com/seaweedfs/seaweedfs/weed/stats"
|
||||||
"math/rand/v2"
|
"math/rand"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -218,17 +218,21 @@ func (vl *VolumeLayout) EnsureCorrectWritables(v *storage.VolumeInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (vl *VolumeLayout) ensureCorrectWritables(vid needle.VolumeId) {
|
func (vl *VolumeLayout) ensureCorrectWritables(vid needle.VolumeId) {
|
||||||
if vl.enoughCopies(vid) && vl.isAllWritable(vid) {
|
isEnoughCopies := vl.enoughCopies(vid)
|
||||||
if !vl.oversizedVolumes.IsTrue(vid) {
|
isAllWritable := vl.isAllWritable(vid)
|
||||||
vl.setVolumeWritable(vid)
|
isOversizedVolume := vl.oversizedVolumes.IsTrue(vid)
|
||||||
}
|
if isEnoughCopies && isAllWritable && !isOversizedVolume {
|
||||||
|
vl.setVolumeWritable(vid)
|
||||||
} else {
|
} else {
|
||||||
if !vl.enoughCopies(vid) {
|
if !isEnoughCopies {
|
||||||
glog.V(0).Infof("volume %d does not have enough copies", vid)
|
glog.V(0).Infof("volume %d does not have enough copies", vid)
|
||||||
}
|
}
|
||||||
if !vl.isAllWritable(vid) {
|
if !isAllWritable {
|
||||||
glog.V(0).Infof("volume %d are not all writable", vid)
|
glog.V(0).Infof("volume %d are not all writable", vid)
|
||||||
}
|
}
|
||||||
|
if isOversizedVolume {
|
||||||
|
glog.V(1).Infof("volume %d are oversized", vid)
|
||||||
|
}
|
||||||
glog.V(0).Infof("volume %d remove from writable", vid)
|
glog.V(0).Infof("volume %d remove from writable", vid)
|
||||||
vl.removeFromWritable(vid)
|
vl.removeFromWritable(vid)
|
||||||
}
|
}
|
||||||
@ -254,6 +258,10 @@ func (vl *VolumeLayout) isOversized(v *storage.VolumeInfo) bool {
|
|||||||
return uint64(v.Size) >= vl.volumeSizeLimit
|
return uint64(v.Size) >= vl.volumeSizeLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vl *VolumeLayout) isCrowdedVolume(v *storage.VolumeInfo) bool {
|
||||||
|
return float64(v.Size) > float64(vl.volumeSizeLimit)*VolumeGrowStrategy.Threshold
|
||||||
|
}
|
||||||
|
|
||||||
func (vl *VolumeLayout) isWritable(v *storage.VolumeInfo) bool {
|
func (vl *VolumeLayout) isWritable(v *storage.VolumeInfo) bool {
|
||||||
return !vl.isOversized(v) &&
|
return !vl.isOversized(v) &&
|
||||||
v.Version == needle.CurrentVersion &&
|
v.Version == needle.CurrentVersion &&
|
||||||
@ -296,7 +304,7 @@ func (vl *VolumeLayout) PickForWrite(count uint64, option *VolumeGrowOption) (vi
|
|||||||
return 0, 0, nil, true, fmt.Errorf("%s in volume layout", noWritableVolumes)
|
return 0, 0, nil, true, fmt.Errorf("%s in volume layout", noWritableVolumes)
|
||||||
}
|
}
|
||||||
if option.DataCenter == "" && option.Rack == "" && option.DataNode == "" {
|
if option.DataCenter == "" && option.Rack == "" && option.DataNode == "" {
|
||||||
vid := vl.writables[rand.IntN(lenWriters)]
|
vid := vl.writables[rand.Intn(lenWriters)]
|
||||||
locationList = vl.vid2location[vid]
|
locationList = vl.vid2location[vid]
|
||||||
if locationList == nil || len(locationList.list) == 0 {
|
if locationList == nil || len(locationList.list) == 0 {
|
||||||
return 0, 0, nil, false, fmt.Errorf("Strangely vid %s is on no machine!", vid.String())
|
return 0, 0, nil, false, fmt.Errorf("Strangely vid %s is on no machine!", vid.String())
|
||||||
@ -351,40 +359,45 @@ func (vl *VolumeLayout) GetLastGrowCount() uint32 {
|
|||||||
return vl.lastGrowCount.Load()
|
return vl.lastGrowCount.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vl *VolumeLayout) ShouldGrowVolumes(option *VolumeGrowOption) bool {
|
func (vl *VolumeLayout) ShouldGrowVolumes(collection string) bool {
|
||||||
total, active, crowded := vl.GetActiveVolumeCount(option)
|
writable, crowded := vl.GetWritableVolumeCount()
|
||||||
stats.MasterVolumeLayout.WithLabelValues(option.Collection, option.DataCenter, "total").Set(float64(total))
|
stats.MasterVolumeLayoutWritable.WithLabelValues(collection, vl.diskType.String(), vl.rp.String(), vl.ttl.String()).Set(float64(writable))
|
||||||
stats.MasterVolumeLayout.WithLabelValues(option.Collection, option.DataCenter, "active").Set(float64(active))
|
stats.MasterVolumeLayoutCrowded.WithLabelValues(collection, vl.diskType.String(), vl.rp.String(), vl.ttl.String()).Set(float64(crowded))
|
||||||
stats.MasterVolumeLayout.WithLabelValues(option.Collection, option.DataCenter, "crowded").Set(float64(crowded))
|
return writable <= crowded
|
||||||
//glog.V(0).Infof("active volume: %d, high usage volume: %d\n", active, high)
|
|
||||||
return active <= crowded
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vl *VolumeLayout) GetActiveVolumeCount(option *VolumeGrowOption) (total, active, crowded int) {
|
func (vl *VolumeLayout) ShouldGrowVolumesByDataNode(nodeType string, dataNode string) bool {
|
||||||
vl.accessLock.RLock()
|
vl.accessLock.RLock()
|
||||||
defer vl.accessLock.RUnlock()
|
writables := make([]needle.VolumeId, len(vl.writables))
|
||||||
if option.DataCenter == "" {
|
copy(writables, vl.writables)
|
||||||
return len(vl.writables), len(vl.writables), len(vl.crowded)
|
vl.accessLock.RUnlock()
|
||||||
}
|
|
||||||
total = len(vl.writables)
|
dataNodeId := NodeId(dataNode)
|
||||||
for _, v := range vl.writables {
|
for _, v := range writables {
|
||||||
for _, dn := range vl.vid2location[v].list {
|
for _, dn := range vl.vid2location[v].list {
|
||||||
if dn.GetDataCenter().Id() == NodeId(option.DataCenter) {
|
dataNodeFound := false
|
||||||
if option.Rack != "" && dn.GetRack().Id() != NodeId(option.Rack) {
|
switch nodeType {
|
||||||
continue
|
case "DataCenter":
|
||||||
}
|
dataNodeFound = dn.GetDataCenter().Id() == dataNodeId
|
||||||
if option.DataNode != "" && dn.Id() != NodeId(option.DataNode) {
|
case "Rack":
|
||||||
continue
|
dataNodeFound = dn.GetRack().Id() == dataNodeId
|
||||||
}
|
case "DataNode":
|
||||||
active++
|
dataNodeFound = dn.Id() == dataNodeId
|
||||||
info, _ := dn.GetVolumesById(v)
|
}
|
||||||
if float64(info.Size) > float64(vl.volumeSizeLimit)*VolumeGrowStrategy.Threshold {
|
if dataNodeFound {
|
||||||
crowded++
|
if info, err := dn.GetVolumesById(v); err == nil && !vl.isCrowdedVolume(&info) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vl *VolumeLayout) GetWritableVolumeCount() (active, crowded int) {
|
||||||
|
vl.accessLock.RLock()
|
||||||
|
defer vl.accessLock.RUnlock()
|
||||||
|
return len(vl.writables), len(vl.crowded)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vl *VolumeLayout) removeFromWritable(vid needle.VolumeId) bool {
|
func (vl *VolumeLayout) removeFromWritable(vid needle.VolumeId) bool {
|
||||||
@ -531,12 +544,13 @@ func (vl *VolumeLayout) ToInfo() (info VolumeLayoutInfo) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vl *VolumeLayout) ToGrowOption() (option *VolumeGrowOption) {
|
func (vlc *VolumeLayoutCollection) ToGrowOption() (option *VolumeGrowOption) {
|
||||||
option = &VolumeGrowOption{}
|
return &VolumeGrowOption{
|
||||||
option.ReplicaPlacement = vl.rp
|
Collection: vlc.Collection,
|
||||||
option.Ttl = vl.ttl
|
ReplicaPlacement: vlc.VolumeLayout.rp,
|
||||||
option.DiskType = vl.diskType
|
Ttl: vlc.VolumeLayout.ttl,
|
||||||
return
|
DiskType: vlc.VolumeLayout.diskType,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vl *VolumeLayout) Stats() *VolumeLayoutStats {
|
func (vl *VolumeLayout) Stats() *VolumeLayoutStats {
|
||||||
|
Loading…
Reference in New Issue
Block a user