Chris Lu
6 years ago
1 changed files with 194 additions and 0 deletions
@ -0,0 +1,194 @@ |
|||
package shell |
|||
|
|||
import ( |
|||
"context" |
|||
"fmt" |
|||
"github.com/chrislusf/seaweedfs/weed/operation" |
|||
"github.com/chrislusf/seaweedfs/weed/pb/master_pb" |
|||
"github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb" |
|||
"github.com/chrislusf/seaweedfs/weed/storage" |
|||
"io" |
|||
"math/rand" |
|||
"sort" |
|||
) |
|||
|
|||
func init() { |
|||
commands = append(commands, &commandVolumeFixReplication{}) |
|||
} |
|||
|
|||
type commandVolumeFixReplication struct { |
|||
} |
|||
|
|||
func (c *commandVolumeFixReplication) Name() string { |
|||
return "volume.fix.replication" |
|||
} |
|||
|
|||
func (c *commandVolumeFixReplication) Help() string { |
|||
return `add replicas to volumes that are missing replicas |
|||
|
|||
-n do not take action |
|||
` |
|||
} |
|||
|
|||
func (c *commandVolumeFixReplication) Do(args []string, commandEnv *commandEnv, writer io.Writer) (err error) { |
|||
|
|||
takeAction := true |
|||
if len(args) > 0 && args[0] == "-n" { |
|||
takeAction = false |
|||
} |
|||
|
|||
var resp *master_pb.VolumeListResponse |
|||
ctx := context.Background() |
|||
err = commandEnv.masterClient.WithClient(ctx, func(client master_pb.SeaweedClient) error { |
|||
resp, err = client.VolumeList(ctx, &master_pb.VolumeListRequest{}) |
|||
return err |
|||
}) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
// find all volumes that needs replication
|
|||
// collect all data nodes
|
|||
replicatedVolumeLocations := make(map[uint32][]location) |
|||
replicatedVolumeInfo := make(map[uint32]*master_pb.VolumeInformationMessage) |
|||
var allLocations []location |
|||
for _, dc := range resp.TopologyInfo.DataCenterInfos { |
|||
for _, rack := range dc.RackInfos { |
|||
for _, dn := range rack.DataNodeInfos { |
|||
loc := newLocation(dc.Id, rack.Id, dn) |
|||
for _, v := range dn.VolumeInfos { |
|||
if v.ReplicaPlacement > 0 { |
|||
replicatedVolumeLocations[v.Id] = append(replicatedVolumeLocations[v.Id], loc) |
|||
replicatedVolumeInfo[v.Id] = v |
|||
} |
|||
} |
|||
allLocations = append(allLocations, loc) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// find all under replicated volumes
|
|||
underReplicatedVolumeLocations := make(map[uint32][]location) |
|||
for vid, locations := range replicatedVolumeLocations { |
|||
volumeInfo := replicatedVolumeInfo[vid] |
|||
replicaPlacement, _ := storage.NewReplicaPlacementFromByte(byte(volumeInfo.ReplicaPlacement)) |
|||
if replicaPlacement.GetCopyCount() > len(locations) { |
|||
underReplicatedVolumeLocations[vid] = locations |
|||
} |
|||
} |
|||
|
|||
if len(underReplicatedVolumeLocations) == 0 { |
|||
return fmt.Errorf("no under replicated volumes") |
|||
} |
|||
|
|||
if len(allLocations) == 0 { |
|||
return fmt.Errorf("no data nodes at all") |
|||
} |
|||
|
|||
// find the most under populated data nodes
|
|||
keepDataNodesSorted(allLocations) |
|||
|
|||
for vid, locations := range underReplicatedVolumeLocations { |
|||
volumeInfo := replicatedVolumeInfo[vid] |
|||
replicaPlacement, _ := storage.NewReplicaPlacementFromByte(byte(volumeInfo.ReplicaPlacement)) |
|||
foundNewLocation := false |
|||
for _, dst := range allLocations { |
|||
// check whether data nodes satisfy the constraints
|
|||
if dst.dataNode.FreeVolumeCount > 0 && satisfyReplicaPlacement(replicaPlacement, locations, dst) { |
|||
// ask the volume server to replicate the volume
|
|||
sourceNodes := underReplicatedVolumeLocations[vid] |
|||
sourceNode := sourceNodes[rand.Intn(len(sourceNodes))] |
|||
foundNewLocation = true |
|||
fmt.Fprintf(writer, "replicating volume %d %s from %s to dataNode %s ...\n", volumeInfo.Id, replicaPlacement, sourceNode.dataNode.Id, dst.dataNode.Id) |
|||
|
|||
if !takeAction { |
|||
break |
|||
} |
|||
|
|||
err := operation.WithVolumeServerClient(dst.dataNode.Id, commandEnv.option.GrpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { |
|||
_, replicateErr := volumeServerClient.ReplicateVolume(ctx, &volume_server_pb.ReplicateVolumeRequest{ |
|||
VolumeId: volumeInfo.Id, |
|||
Collection: volumeInfo.Collection, |
|||
SourceDataNode: sourceNode.dataNode.Id, |
|||
}) |
|||
return replicateErr |
|||
}) |
|||
|
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
// adjust free volume count
|
|||
dst.dataNode.FreeVolumeCount-- |
|||
keepDataNodesSorted(allLocations) |
|||
break |
|||
} |
|||
} |
|||
if !foundNewLocation { |
|||
fmt.Fprintf(writer, "failed to place volume %d replica as %s, existing:%+v\n", volumeInfo.Id, replicaPlacement, locations) |
|||
} |
|||
|
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
func keepDataNodesSorted(dataNodes []location) { |
|||
sort.Slice(dataNodes, func(i, j int) bool { |
|||
return dataNodes[i].dataNode.FreeVolumeCount > dataNodes[j].dataNode.FreeVolumeCount |
|||
}) |
|||
} |
|||
|
|||
func satisfyReplicaPlacement(replicaPlacement *storage.ReplicaPlacement, existingLocations []location, possibleLocation location) bool { |
|||
|
|||
existingDataCenters := make(map[string]bool) |
|||
existingRacks := make(map[string]bool) |
|||
existingDataNodes := make(map[string]bool) |
|||
for _, loc := range existingLocations { |
|||
existingDataCenters[loc.DataCenter()] = true |
|||
existingRacks[loc.Rack()] = true |
|||
existingDataNodes[loc.String()] = true |
|||
} |
|||
|
|||
if replicaPlacement.DiffDataCenterCount >= len(existingDataCenters) { |
|||
// check dc, good if different from any existing data centers
|
|||
_, found := existingDataCenters[possibleLocation.DataCenter()] |
|||
return !found |
|||
} else if replicaPlacement.DiffRackCount >= len(existingRacks) { |
|||
// check rack, good if different from any existing racks
|
|||
_, found := existingRacks[possibleLocation.Rack()] |
|||
return !found |
|||
} else if replicaPlacement.SameRackCount >= len(existingDataNodes) { |
|||
// check data node, good if different from any existing data nodes
|
|||
_, found := existingDataNodes[possibleLocation.String()] |
|||
return !found |
|||
} |
|||
|
|||
return false |
|||
} |
|||
|
|||
type location struct { |
|||
dc string |
|||
rack string |
|||
dataNode *master_pb.DataNodeInfo |
|||
} |
|||
|
|||
func newLocation(dc, rack string, dataNode *master_pb.DataNodeInfo) location { |
|||
return location{ |
|||
dc: dc, |
|||
rack: rack, |
|||
dataNode: dataNode, |
|||
} |
|||
} |
|||
|
|||
func (l location) String() string { |
|||
return fmt.Sprintf("%s %s %s", l.dc, l.rack, l.dataNode.Id) |
|||
} |
|||
|
|||
func (l location) Rack() string { |
|||
return fmt.Sprintf("%s %s", l.dc, l.rack) |
|||
} |
|||
|
|||
func (l location) DataCenter() string { |
|||
return l.dc |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue