You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

154 lines
4.0 KiB

package topology
import (
"container/list"
"fmt"
"time"
"github.com/chrislusf/seaweedfs/go/glog"
"github.com/chrislusf/seaweedfs/go/storage"
)
var (
isReplicateCheckerRunning = false
)
const ReplicateTaskTimeout = time.Hour
type ReplicateTask struct {
Vid storage.VolumeId
Collection string
SrcDN *DataNode
DstDN *DataNode
}
func (t *ReplicateTask) Run(topo *Topology) error {
//is lookup thread safe?
locationList := topo.Lookup(t.Collection, t.Vid)
rp := topo.CollectionSettings.GetReplicaPlacement(t.Collection)
if locationList.CalcReplicaPlacement().Compare(rp) >= 0 {
glog.V(0).Infof("volume [%v] has right replica placement, rp: %s", t.Vid, rp.String())
return nil
}
if !SetVolumeReadonly(locationList, t.Vid.String(), true) {
return fmt.Errorf("set volume readonly failed, vid=%v", t.Vid)
}
defer SetVolumeReadonly(locationList, t.Vid.String(), false)
tc, e := storage.NewTaskCli(t.DstDN.Url(), storage.TaskReplicate, storage.TaskParams{
"volume": t.Vid.String(),
"source": t.SrcDN.Url(),
"collection": t.Collection,
})
if e != nil {
return e
}
if e = tc.WaitAndQueryResult(ReplicateTaskTimeout); e != nil {
tc.Clean()
return e
}
e = tc.Commit()
return e
}
func (t *ReplicateTask) WorkingDataNodes() []*DataNode {
return []*DataNode{
t.SrcDN,
t.DstDN,
}
}
func planReplicateTasks(t *Topology) (tasks []*ReplicateTask) {
for i1 := range t.collectionMap.IterItems() {
c := i1.Value.(*Collection)
glog.V(0).Infoln("checking replicate on collection:", c.Name)
growOption := &VolumeGrowOption{ReplicaPlacement: c.rp}
for i2 := range c.storageType2VolumeLayout.IterItems() {
if i2.Value == nil {
continue
}
volumeLayout := i2.Value.(*VolumeLayout)
for vid, locationList := range volumeLayout.vid2location {
rp1 := locationList.CalcReplicaPlacement()
if rp1.Compare(volumeLayout.rp) >= 0 {
continue
}
if additionServers, e := FindEmptySlotsForOneVolume(t, growOption, locationList); e == nil {
for _, s := range additionServers {
s.UpAdjustPlannedVolumeCountDelta(1)
rt := &ReplicateTask{
Vid: vid,
Collection: c.Name,
SrcDN: locationList.PickForRead(),
DstDN: s,
}
tasks = append(tasks, rt)
glog.V(0).Infof("add replicate task, vid: %v, src: %s, dst: %s", vid, rt.SrcDN.Url(), rt.DstDN.Url())
}
} else {
glog.V(0).Infof("find empty slots error, vid: %v, rp: %s => %s, %v", vid, rp1.String(), volumeLayout.rp.String(), e)
}
}
}
}
return
}
func (topo *Topology) CheckReplicate() {
isReplicateCheckerRunning = true
defer func() {
isReplicateCheckerRunning = false
}()
glog.V(1).Infoln("Start replicate checker on demand")
busyDataNodes := make(map[*DataNode]int)
taskCount := 0
taskQueue := list.New()
for _, t := range planReplicateTasks(topo) {
taskQueue.PushBack(t)
taskCount++
}
taskChan := make(chan *ReplicateTask)
for taskCount > 0 {
TaskQueueLoop:
for e := taskQueue.Front(); e != nil; e = e.Next() {
task := e.Value.(*ReplicateTask)
//only one task will run on the data node
dns := task.WorkingDataNodes()
for _, dn := range dns {
if busyDataNodes[dn] > 0 {
continue TaskQueueLoop
}
}
for _, dn := range dns {
busyDataNodes[dn]++
}
go func(t *ReplicateTask) {
if e := t.Run(topo); e != nil {
glog.V(0).Infof("ReplicateTask run error, vid: %v, dst: %s. %v", t.Vid, t.DstDN.Url(), e)
} else {
glog.V(2).Infof("ReplicateTask finished, vid: %v, dst: %s", t.Vid, t.DstDN.Url())
}
taskChan <- t
}(task)
taskQueue.Remove(e)
}
finishedTask := <-taskChan
for _, dn := range finishedTask.WorkingDataNodes() {
if busyDataNodes[dn] > 0 {
busyDataNodes[dn]--
}
}
taskCount--
finishedTask.DstDN.UpAdjustPlannedVolumeCountDelta(-1)
}
glog.V(0).Infoln("finish replicate check.")
}
func (topo *Topology) StartCheckReplicate() {
if isReplicateCheckerRunning {
return
}
go topo.CheckReplicate()
}