2012-08-28 04:52:02 +08:00
|
|
|
package replication
|
|
|
|
|
|
|
|
import (
|
2012-09-17 08:31:15 +08:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2012-09-03 16:50:04 +08:00
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
2012-09-17 08:31:15 +08:00
|
|
|
"net/url"
|
2012-09-03 16:50:04 +08:00
|
|
|
"pkg/storage"
|
2012-08-28 04:52:02 +08:00
|
|
|
"pkg/topology"
|
2012-09-17 08:31:15 +08:00
|
|
|
"pkg/util"
|
|
|
|
"strconv"
|
2012-08-28 04:52:02 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
|
|
|
This package is created to resolve these replica placement issues:
|
|
|
|
1. growth factor for each replica level, e.g., add 10 volumes for 1 copy, 20 volumes for 2 copies, 30 volumes for 3 copies
|
|
|
|
2. in time of tight storage, how to reduce replica level
|
|
|
|
3. optimizing for hot data on faster disk, cold data on cheaper storage,
|
|
|
|
4. volume allocation for each bucket
|
|
|
|
*/
|
|
|
|
|
|
|
|
type VolumeGrowth struct {
|
|
|
|
copy1factor int
|
|
|
|
copy2factor int
|
|
|
|
copy3factor int
|
|
|
|
copyAll int
|
|
|
|
}
|
|
|
|
|
2012-09-17 08:31:15 +08:00
|
|
|
func NewDefaultVolumeGrowth() *VolumeGrowth {
|
|
|
|
return &VolumeGrowth{copy1factor: 7, copy2factor: 6, copy3factor: 3}
|
|
|
|
}
|
|
|
|
|
2012-09-17 14:18:47 +08:00
|
|
|
func (vg *VolumeGrowth) GrowByType(repType storage.ReplicationType, topo *topology.Topology) (int, error) {
|
2012-09-17 08:31:15 +08:00
|
|
|
switch repType {
|
|
|
|
case storage.Copy00:
|
2012-09-17 14:18:47 +08:00
|
|
|
return vg.GrowByCountAndType(vg.copy1factor, repType, topo)
|
2012-09-17 08:31:15 +08:00
|
|
|
case storage.Copy10:
|
2012-09-17 14:18:47 +08:00
|
|
|
return vg.GrowByCountAndType(vg.copy2factor, repType, topo)
|
2012-09-17 08:31:15 +08:00
|
|
|
case storage.Copy20:
|
2012-09-17 14:18:47 +08:00
|
|
|
return vg.GrowByCountAndType(vg.copy3factor, repType, topo)
|
2012-09-17 08:31:15 +08:00
|
|
|
case storage.Copy01:
|
2012-09-17 14:18:47 +08:00
|
|
|
return vg.GrowByCountAndType(vg.copy2factor, repType, topo)
|
2012-09-17 08:31:15 +08:00
|
|
|
case storage.Copy11:
|
2012-09-17 14:18:47 +08:00
|
|
|
return vg.GrowByCountAndType(vg.copy3factor, repType, topo)
|
2012-09-17 08:31:15 +08:00
|
|
|
}
|
2012-09-17 16:48:09 +08:00
|
|
|
return 0, errors.New("Unknown Replication Type!")
|
2012-09-17 08:31:15 +08:00
|
|
|
}
|
2012-09-17 14:18:47 +08:00
|
|
|
func (vg *VolumeGrowth) GrowByCountAndType(count int, repType storage.ReplicationType, topo *topology.Topology) (counter int, err error) {
|
|
|
|
counter = 0
|
2012-09-17 08:31:15 +08:00
|
|
|
switch repType {
|
|
|
|
case storage.Copy00:
|
|
|
|
for i := 0; i < count; i++ {
|
2012-09-17 14:18:47 +08:00
|
|
|
if ok, server, vid := topo.RandomlyReserveOneVolume(); ok {
|
|
|
|
if err = vg.grow(topo, *vid, repType, server); err == nil {
|
|
|
|
counter++
|
|
|
|
}
|
2012-09-03 16:50:04 +08:00
|
|
|
}
|
|
|
|
}
|
2012-09-17 08:31:15 +08:00
|
|
|
case storage.Copy10:
|
|
|
|
for i := 0; i < count; i++ {
|
2012-09-03 16:50:04 +08:00
|
|
|
nl := topology.NewNodeList(topo.Children(), nil)
|
|
|
|
picked, ret := nl.RandomlyPickN(2)
|
|
|
|
vid := topo.NextVolumeId()
|
|
|
|
if ret {
|
2012-09-09 07:25:44 +08:00
|
|
|
var servers []*topology.DataNode
|
2012-09-03 16:50:04 +08:00
|
|
|
for _, n := range picked {
|
2012-09-17 10:18:37 +08:00
|
|
|
if n.FreeSpace() > 0 {
|
|
|
|
if ok, server := n.ReserveOneVolume(rand.Intn(n.FreeSpace()), vid); ok {
|
|
|
|
servers = append(servers, server)
|
|
|
|
}
|
2012-09-03 16:50:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(servers) == 2 {
|
2012-09-17 14:18:47 +08:00
|
|
|
if err = vg.grow(topo, vid, repType, servers[0], servers[1]); err == nil {
|
|
|
|
counter++
|
|
|
|
}
|
2012-09-03 16:50:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-17 08:31:15 +08:00
|
|
|
case storage.Copy20:
|
|
|
|
for i := 0; i < count; i++ {
|
2012-09-03 16:50:04 +08:00
|
|
|
nl := topology.NewNodeList(topo.Children(), nil)
|
|
|
|
picked, ret := nl.RandomlyPickN(3)
|
|
|
|
vid := topo.NextVolumeId()
|
|
|
|
if ret {
|
2012-09-09 07:25:44 +08:00
|
|
|
var servers []*topology.DataNode
|
2012-09-03 16:50:04 +08:00
|
|
|
for _, n := range picked {
|
2012-09-17 10:18:37 +08:00
|
|
|
if n.FreeSpace() > 0 {
|
|
|
|
if ok, server := n.ReserveOneVolume(rand.Intn(n.FreeSpace()), vid); ok {
|
|
|
|
servers = append(servers, server)
|
|
|
|
}
|
2012-09-03 16:50:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(servers) == 3 {
|
2012-09-17 14:18:47 +08:00
|
|
|
if err = vg.grow(topo, vid, repType, servers[0], servers[1], servers[2]); err == nil {
|
|
|
|
counter++
|
|
|
|
}
|
2012-09-03 16:50:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-17 08:31:15 +08:00
|
|
|
case storage.Copy01:
|
|
|
|
for i := 0; i < count; i++ {
|
2012-09-03 16:50:04 +08:00
|
|
|
//randomly pick one server, and then choose from the same rack
|
2012-09-17 14:18:47 +08:00
|
|
|
if ok, server1, vid := topo.RandomlyReserveOneVolume(); ok {
|
2012-09-03 16:50:04 +08:00
|
|
|
rack := server1.Parent()
|
|
|
|
exclusion := make(map[string]topology.Node)
|
|
|
|
exclusion[server1.String()] = server1
|
|
|
|
newNodeList := topology.NewNodeList(rack.Children(), exclusion)
|
2012-09-17 08:31:15 +08:00
|
|
|
if newNodeList.FreeSpace() > 0 {
|
2012-09-17 14:18:47 +08:00
|
|
|
if ok2, server2 := newNodeList.ReserveOneVolume(rand.Intn(newNodeList.FreeSpace()), *vid); ok2 {
|
|
|
|
if err = vg.grow(topo, *vid, repType, server1, server2); err == nil {
|
|
|
|
counter++
|
|
|
|
}
|
2012-09-17 08:31:15 +08:00
|
|
|
}
|
2012-09-03 16:50:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-17 08:31:15 +08:00
|
|
|
case storage.Copy11:
|
|
|
|
for i := 0; i < count; i++ {
|
2012-08-28 04:52:02 +08:00
|
|
|
}
|
2012-09-17 14:18:47 +08:00
|
|
|
err = errors.New("Replication Type Not Implemented Yet!")
|
2012-08-28 04:52:02 +08:00
|
|
|
}
|
2012-09-17 14:18:47 +08:00
|
|
|
return
|
2012-08-28 04:52:02 +08:00
|
|
|
}
|
2012-09-17 14:18:47 +08:00
|
|
|
func (vg *VolumeGrowth) grow(topo *topology.Topology, vid storage.VolumeId, repType storage.ReplicationType, servers ...*topology.DataNode) error {
|
2012-09-03 16:50:04 +08:00
|
|
|
for _, server := range servers {
|
2012-09-17 08:31:15 +08:00
|
|
|
if err := AllocateVolume(server, vid, repType); err == nil {
|
2012-09-20 17:11:08 +08:00
|
|
|
vi := storage.VolumeInfo{Id: vid, Size: 0, RepType:repType}
|
2012-09-17 08:31:15 +08:00
|
|
|
server.AddOrUpdateVolume(vi)
|
2012-09-19 16:45:30 +08:00
|
|
|
topo.RegisterVolumeLayout(&vi, server)
|
2012-09-17 14:21:18 +08:00
|
|
|
fmt.Println("Created Volume", vid, "on", server)
|
2012-09-17 08:31:15 +08:00
|
|
|
} else {
|
|
|
|
fmt.Println("Failed to assign", vid, "to", servers)
|
2012-09-17 14:18:47 +08:00
|
|
|
return errors.New("Failed to assign " + vid.String())
|
2012-09-17 08:31:15 +08:00
|
|
|
}
|
2012-09-03 16:50:04 +08:00
|
|
|
}
|
2012-09-17 14:18:47 +08:00
|
|
|
return nil
|
2012-09-03 16:50:04 +08:00
|
|
|
}
|
2012-09-17 08:31:15 +08:00
|
|
|
|
|
|
|
type AllocateVolumeResult struct {
|
|
|
|
Error string
|
|
|
|
}
|
|
|
|
|
|
|
|
func AllocateVolume(dn *topology.DataNode, vid storage.VolumeId, repType storage.ReplicationType) error {
|
|
|
|
values := make(url.Values)
|
|
|
|
values.Add("volume", vid.String())
|
|
|
|
values.Add("replicationType", repType.String())
|
|
|
|
jsonBlob, err := util.Post("http://"+dn.Ip+":"+strconv.Itoa(dn.Port)+"/admin/assign_volume", values)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var ret AllocateVolumeResult
|
|
|
|
if err := json.Unmarshal(jsonBlob, &ret); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ret.Error != "" {
|
|
|
|
return errors.New(ret.Error)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|