mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2024-01-19 02:48:24 +00:00
periodically compact volumes
This commit is contained in:
parent
5e020196f6
commit
94dbe60233
|
@ -57,6 +57,15 @@ func assignVolumeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
debug("assign volume =", r.FormValue("volume"), ", replicationType =", r.FormValue("replicationType"), ", error =", err)
|
debug("assign volume =", r.FormValue("volume"), ", replicationType =", r.FormValue("replicationType"), ", error =", err)
|
||||||
}
|
}
|
||||||
|
func vacuumVolumeCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err, ret := store.CheckCompactVolume(r.FormValue("volume"), r.FormValue("garbageThreshold"))
|
||||||
|
if err == nil {
|
||||||
|
writeJson(w, r, map[string]interface{}{"error": "", "result": ret})
|
||||||
|
} else {
|
||||||
|
writeJson(w, r, map[string]interface{}{"error": err.Error(), "result": false})
|
||||||
|
}
|
||||||
|
debug("checked compacting volume =", r.FormValue("volume"), "garbageThreshold =", r.FormValue("garbageThreshold"), "vacuum =", ret)
|
||||||
|
}
|
||||||
func vacuumVolumeCompactHandler(w http.ResponseWriter, r *http.Request) {
|
func vacuumVolumeCompactHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
err := store.CompactVolume(r.FormValue("volume"))
|
err := store.CompactVolume(r.FormValue("volume"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -67,9 +76,9 @@ func vacuumVolumeCompactHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
debug("compacted volume =", r.FormValue("volume"), ", error =", err)
|
debug("compacted volume =", r.FormValue("volume"), ", error =", err)
|
||||||
}
|
}
|
||||||
func vacuumVolumeCommitHandler(w http.ResponseWriter, r *http.Request) {
|
func vacuumVolumeCommitHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
count, err := store.CommitCompactVolume(r.FormValue("volume"))
|
err := store.CommitCompactVolume(r.FormValue("volume"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
writeJson(w, r, map[string]interface{}{"error": "", "size": count})
|
writeJson(w, r, map[string]interface{}{"error": ""})
|
||||||
} else {
|
} else {
|
||||||
writeJson(w, r, map[string]string{"error": err.Error()})
|
writeJson(w, r, map[string]string{"error": err.Error()})
|
||||||
}
|
}
|
||||||
|
@ -302,6 +311,7 @@ func runVolume(cmd *Command, args []string) bool {
|
||||||
http.HandleFunc("/", storeHandler)
|
http.HandleFunc("/", storeHandler)
|
||||||
http.HandleFunc("/status", statusHandler)
|
http.HandleFunc("/status", statusHandler)
|
||||||
http.HandleFunc("/admin/assign_volume", assignVolumeHandler)
|
http.HandleFunc("/admin/assign_volume", assignVolumeHandler)
|
||||||
|
http.HandleFunc("/admin/vacuum_volume_check", vacuumVolumeCheckHandler)
|
||||||
http.HandleFunc("/admin/vacuum_volume_compact", vacuumVolumeCompactHandler)
|
http.HandleFunc("/admin/vacuum_volume_compact", vacuumVolumeCompactHandler)
|
||||||
http.HandleFunc("/admin/vacuum_volume_commit", vacuumVolumeCommitHandler)
|
http.HandleFunc("/admin/vacuum_volume_commit", vacuumVolumeCommitHandler)
|
||||||
|
|
||||||
|
|
|
@ -29,17 +29,25 @@ func NewCompactSection(start Key) CompactSection {
|
||||||
start: start,
|
start: start,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (cs *CompactSection) Set(key Key, offset uint32, size uint32) {
|
|
||||||
|
//return old entry size
|
||||||
|
func (cs *CompactSection) Set(key Key, offset uint32, size uint32) uint32 {
|
||||||
|
ret := uint32(0)
|
||||||
if key > cs.end {
|
if key > cs.end {
|
||||||
cs.end = key
|
cs.end = key
|
||||||
}
|
}
|
||||||
if i := cs.binarySearchValues(key); i >= 0 {
|
if i := cs.binarySearchValues(key); i >= 0 {
|
||||||
|
ret = cs.values[i].Size
|
||||||
|
//println("key", key, "old size", ret)
|
||||||
cs.values[i].Offset, cs.values[i].Size = offset, size
|
cs.values[i].Offset, cs.values[i].Size = offset, size
|
||||||
} else {
|
} else {
|
||||||
needOverflow := cs.counter >= batch
|
needOverflow := cs.counter >= batch
|
||||||
needOverflow = needOverflow || cs.counter > 0 && cs.values[cs.counter-1].Key > key
|
needOverflow = needOverflow || cs.counter > 0 && cs.values[cs.counter-1].Key > key
|
||||||
if needOverflow {
|
if needOverflow {
|
||||||
//println("start", cs.start, "counter", cs.counter, "key", key)
|
//println("start", cs.start, "counter", cs.counter, "key", key)
|
||||||
|
if oldValue := cs.overflow[key]; oldValue != nil {
|
||||||
|
ret = oldValue.Size
|
||||||
|
}
|
||||||
cs.overflow[key] = &NeedleValue{Key: key, Offset: offset, Size: size}
|
cs.overflow[key] = &NeedleValue{Key: key, Offset: offset, Size: size}
|
||||||
} else {
|
} else {
|
||||||
p := &cs.values[cs.counter]
|
p := &cs.values[cs.counter]
|
||||||
|
@ -48,12 +56,23 @@ func (cs *CompactSection) Set(key Key, offset uint32, size uint32) {
|
||||||
cs.counter++
|
cs.counter++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
func (cs *CompactSection) Delete(key Key) {
|
|
||||||
|
//return old entry size
|
||||||
|
func (cs *CompactSection) Delete(key Key) uint32 {
|
||||||
|
ret := uint32(0)
|
||||||
if i := cs.binarySearchValues(key); i >= 0 {
|
if i := cs.binarySearchValues(key); i >= 0 {
|
||||||
|
if cs.values[i].Size > 0 {
|
||||||
|
ret = cs.values[i].Size
|
||||||
cs.values[i].Size = 0
|
cs.values[i].Size = 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if v := cs.overflow[key]; v != nil {
|
||||||
delete(cs.overflow, key)
|
delete(cs.overflow, key)
|
||||||
|
ret = v.Size
|
||||||
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
func (cs *CompactSection) Get(key Key) (*NeedleValue, bool) {
|
func (cs *CompactSection) Get(key Key) (*NeedleValue, bool) {
|
||||||
if v, ok := cs.overflow[key]; ok {
|
if v, ok := cs.overflow[key]; ok {
|
||||||
|
@ -94,21 +113,21 @@ func NewCompactMap() CompactMap {
|
||||||
return CompactMap{}
|
return CompactMap{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *CompactMap) Set(key Key, offset uint32, size uint32) {
|
func (cm *CompactMap) Set(key Key, offset uint32, size uint32) uint32 {
|
||||||
x := cm.binarySearchCompactSection(key)
|
x := cm.binarySearchCompactSection(key)
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
//println(x, "creating", len(cm.list), "section1, starting", key)
|
//println(x, "creating", len(cm.list), "section1, starting", key)
|
||||||
cm.list = append(cm.list, NewCompactSection(key))
|
cm.list = append(cm.list, NewCompactSection(key))
|
||||||
x = len(cm.list) - 1
|
x = len(cm.list) - 1
|
||||||
}
|
}
|
||||||
cm.list[x].Set(key, offset, size)
|
return cm.list[x].Set(key, offset, size)
|
||||||
}
|
}
|
||||||
func (cm *CompactMap) Delete(key Key) {
|
func (cm *CompactMap) Delete(key Key) uint32 {
|
||||||
x := cm.binarySearchCompactSection(key)
|
x := cm.binarySearchCompactSection(key)
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
return
|
return uint32(0)
|
||||||
}
|
}
|
||||||
cm.list[x].Delete(key)
|
return cm.list[x].Delete(key)
|
||||||
}
|
}
|
||||||
func (cm *CompactMap) Get(key Key) (*NeedleValue, bool) {
|
func (cm *CompactMap) Get(key Key) (*NeedleValue, bool) {
|
||||||
x := cm.binarySearchCompactSection(key)
|
x := cm.binarySearchCompactSection(key)
|
||||||
|
|
|
@ -12,8 +12,10 @@ type NeedleMap struct {
|
||||||
|
|
||||||
//transient
|
//transient
|
||||||
bytes []byte
|
bytes []byte
|
||||||
|
|
||||||
deletionCounter int
|
deletionCounter int
|
||||||
fileCounter int
|
fileCounter int
|
||||||
|
deletionByteCounter uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNeedleMap(file *os.File) *NeedleMap {
|
func NewNeedleMap(file *os.File) *NeedleMap {
|
||||||
|
@ -43,13 +45,18 @@ func LoadNeedleMap(file *os.File) *NeedleMap {
|
||||||
offset := util.BytesToUint32(bytes[i+8 : i+12])
|
offset := util.BytesToUint32(bytes[i+8 : i+12])
|
||||||
size := util.BytesToUint32(bytes[i+12 : i+16])
|
size := util.BytesToUint32(bytes[i+12 : i+16])
|
||||||
if offset > 0 {
|
if offset > 0 {
|
||||||
nm.m.Set(Key(key), offset, size)
|
oldSize := nm.m.Set(Key(key), offset, size)
|
||||||
//log.Println("reading key", key, "offset", offset, "size", size)
|
//log.Println("reading key", key, "offset", offset, "size", size, "oldSize", oldSize)
|
||||||
nm.fileCounter++
|
nm.fileCounter++
|
||||||
|
if oldSize > 0 {
|
||||||
|
nm.deletionCounter++
|
||||||
|
nm.deletionByteCounter = nm.deletionByteCounter + oldSize
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
nm.m.Delete(Key(key))
|
nm.m.Delete(Key(key))
|
||||||
//log.Println("removing key", key)
|
//log.Println("removing key", key)
|
||||||
nm.deletionCounter++
|
nm.deletionCounter++
|
||||||
|
nm.deletionByteCounter = nm.deletionByteCounter + size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,11 +66,15 @@ func LoadNeedleMap(file *os.File) *NeedleMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nm *NeedleMap) Put(key uint64, offset uint32, size uint32) (int, error) {
|
func (nm *NeedleMap) Put(key uint64, offset uint32, size uint32) (int, error) {
|
||||||
nm.m.Set(Key(key), offset, size)
|
oldSize := nm.m.Set(Key(key), offset, size)
|
||||||
util.Uint64toBytes(nm.bytes[0:8], key)
|
util.Uint64toBytes(nm.bytes[0:8], key)
|
||||||
util.Uint32toBytes(nm.bytes[8:12], offset)
|
util.Uint32toBytes(nm.bytes[8:12], offset)
|
||||||
util.Uint32toBytes(nm.bytes[12:16], size)
|
util.Uint32toBytes(nm.bytes[12:16], size)
|
||||||
nm.fileCounter++
|
nm.fileCounter++
|
||||||
|
if oldSize > 0 {
|
||||||
|
nm.deletionCounter++
|
||||||
|
nm.deletionByteCounter = nm.deletionByteCounter + oldSize
|
||||||
|
}
|
||||||
return nm.indexFile.Write(nm.bytes)
|
return nm.indexFile.Write(nm.bytes)
|
||||||
}
|
}
|
||||||
func (nm *NeedleMap) Get(key uint64) (element *NeedleValue, ok bool) {
|
func (nm *NeedleMap) Get(key uint64) (element *NeedleValue, ok bool) {
|
||||||
|
@ -71,7 +82,7 @@ func (nm *NeedleMap) Get(key uint64) (element *NeedleValue, ok bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (nm *NeedleMap) Delete(key uint64) {
|
func (nm *NeedleMap) Delete(key uint64) {
|
||||||
nm.m.Delete(Key(key))
|
nm.deletionByteCounter = nm.deletionByteCounter + nm.m.Delete(Key(key))
|
||||||
util.Uint64toBytes(nm.bytes[0:8], key)
|
util.Uint64toBytes(nm.bytes[0:8], key)
|
||||||
util.Uint32toBytes(nm.bytes[8:12], 0)
|
util.Uint32toBytes(nm.bytes[8:12], 0)
|
||||||
util.Uint32toBytes(nm.bytes[12:16], 0)
|
util.Uint32toBytes(nm.bytes[12:16], 0)
|
||||||
|
|
|
@ -69,6 +69,17 @@ func (s *Store) addVolume(vid VolumeId, replicationType ReplicationType) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Store) CheckCompactVolume(volumeIdString string, garbageThresholdString string) (error, bool) {
|
||||||
|
vid, err := NewVolumeId(volumeIdString)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Volume Id " + volumeIdString + " is not a valid unsigned integer!"), false
|
||||||
|
}
|
||||||
|
garbageThreshold, e := strconv.ParseFloat(garbageThresholdString, 32)
|
||||||
|
if e != nil {
|
||||||
|
return errors.New("garbageThreshold " + garbageThresholdString + " is not a valid float number!"), false
|
||||||
|
}
|
||||||
|
return nil, garbageThreshold < s.volumes[vid].garbageLevel()
|
||||||
|
}
|
||||||
func (s *Store) CompactVolume(volumeIdString string) error {
|
func (s *Store) CompactVolume(volumeIdString string) error {
|
||||||
vid, err := NewVolumeId(volumeIdString)
|
vid, err := NewVolumeId(volumeIdString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,10 +87,10 @@ func (s *Store) CompactVolume(volumeIdString string) error {
|
||||||
}
|
}
|
||||||
return s.volumes[vid].compact()
|
return s.volumes[vid].compact()
|
||||||
}
|
}
|
||||||
func (s *Store) CommitCompactVolume(volumeIdString string) (int,error) {
|
func (s *Store) CommitCompactVolume(volumeIdString string) (error) {
|
||||||
vid, err := NewVolumeId(volumeIdString)
|
vid, err := NewVolumeId(volumeIdString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, errors.New("Volume Id " + volumeIdString + " is not a valid unsigned integer!")
|
return errors.New("Volume Id " + volumeIdString + " is not a valid unsigned integer!")
|
||||||
}
|
}
|
||||||
return s.volumes[vid].commitCompact()
|
return s.volumes[vid].commitCompact()
|
||||||
}
|
}
|
||||||
|
@ -104,7 +115,7 @@ func (s *Store) Status() []*VolumeInfo {
|
||||||
var stats []*VolumeInfo
|
var stats []*VolumeInfo
|
||||||
for k, v := range s.volumes {
|
for k, v := range s.volumes {
|
||||||
s := new(VolumeInfo)
|
s := new(VolumeInfo)
|
||||||
s.Id, s.Size, s.RepType, s.FileCount, s.DeleteCount = VolumeId(k), v.Size(), v.replicaType, v.nm.fileCounter, v.nm.deletionCounter
|
s.Id, s.Size, s.RepType, s.FileCount, s.DeleteCount, s.DeletedByteCount = VolumeId(k), v.Size(), v.replicaType, v.nm.fileCounter, v.nm.deletionCounter, v.nm.deletionByteCounter
|
||||||
stats = append(stats, s)
|
stats = append(stats, s)
|
||||||
}
|
}
|
||||||
return stats
|
return stats
|
||||||
|
@ -113,7 +124,7 @@ func (s *Store) Join(mserver string) error {
|
||||||
stats := new([]*VolumeInfo)
|
stats := new([]*VolumeInfo)
|
||||||
for k, v := range s.volumes {
|
for k, v := range s.volumes {
|
||||||
s := new(VolumeInfo)
|
s := new(VolumeInfo)
|
||||||
s.Id, s.Size, s.RepType, s.FileCount, s.DeleteCount = VolumeId(k), v.Size(), v.replicaType, v.nm.fileCounter, v.nm.deletionCounter
|
s.Id, s.Size, s.RepType, s.FileCount, s.DeleteCount, s.DeletedByteCount = VolumeId(k), v.Size(), v.replicaType, v.nm.fileCounter, v.nm.deletionCounter, v.nm.deletionByteCounter
|
||||||
*stats = append(*stats, s)
|
*stats = append(*stats, s)
|
||||||
}
|
}
|
||||||
bytes, _ := json.Marshal(stats)
|
bytes, _ := json.Marshal(stats)
|
||||||
|
|
|
@ -55,13 +55,18 @@ func (v *Volume) load() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (v *Volume) Size() int64 {
|
func (v *Volume) Size() int64 {
|
||||||
|
v.accessLock.Lock()
|
||||||
|
defer v.accessLock.Unlock()
|
||||||
stat, e := v.dataFile.Stat()
|
stat, e := v.dataFile.Stat()
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return stat.Size()
|
return stat.Size()
|
||||||
}
|
}
|
||||||
|
fmt.Printf("Failed to read file size %s %s\n", v.dataFile.Name(), e.Error())
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
func (v *Volume) Close() {
|
func (v *Volume) Close() {
|
||||||
|
v.accessLock.Lock()
|
||||||
|
defer v.accessLock.Unlock()
|
||||||
v.nm.Close()
|
v.nm.Close()
|
||||||
v.dataFile.Close()
|
v.dataFile.Close()
|
||||||
}
|
}
|
||||||
|
@ -115,6 +120,7 @@ func (v *Volume) delete(n *Needle) uint32 {
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Volume) read(n *Needle) (int, error) {
|
func (v *Volume) read(n *Needle) (int, error) {
|
||||||
v.accessLock.Lock()
|
v.accessLock.Lock()
|
||||||
defer v.accessLock.Unlock()
|
defer v.accessLock.Unlock()
|
||||||
|
@ -126,6 +132,10 @@ func (v *Volume) read(n *Needle) (int, error) {
|
||||||
return -1, errors.New("Not Found")
|
return -1, errors.New("Not Found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Volume) garbageLevel() float64 {
|
||||||
|
return float64(v.nm.deletionByteCounter)/float64(v.Size())
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Volume) compact() error {
|
func (v *Volume) compact() error {
|
||||||
v.accessLock.Lock()
|
v.accessLock.Lock()
|
||||||
defer v.accessLock.Unlock()
|
defer v.accessLock.Unlock()
|
||||||
|
@ -133,21 +143,21 @@ func (v *Volume) compact() error {
|
||||||
filePath := path.Join(v.dir, v.Id.String())
|
filePath := path.Join(v.dir, v.Id.String())
|
||||||
return v.copyDataAndGenerateIndexFile(filePath+".dat", filePath+".cpd", filePath+".cpx")
|
return v.copyDataAndGenerateIndexFile(filePath+".dat", filePath+".cpd", filePath+".cpx")
|
||||||
}
|
}
|
||||||
func (v *Volume) commitCompact() (int, error) {
|
func (v *Volume) commitCompact() (error) {
|
||||||
v.accessLock.Lock()
|
v.accessLock.Lock()
|
||||||
defer v.accessLock.Unlock()
|
defer v.accessLock.Unlock()
|
||||||
v.dataFile.Close()
|
v.dataFile.Close()
|
||||||
var e error
|
var e error
|
||||||
if e = os.Rename(path.Join(v.dir, v.Id.String()+".cpd"), path.Join(v.dir, v.Id.String()+".dat")); e != nil {
|
if e = os.Rename(path.Join(v.dir, v.Id.String()+".cpd"), path.Join(v.dir, v.Id.String()+".dat")); e != nil {
|
||||||
return 0, e
|
return e
|
||||||
}
|
}
|
||||||
if e = os.Rename(path.Join(v.dir, v.Id.String()+".cpx"), path.Join(v.dir, v.Id.String()+".idx")); e != nil {
|
if e = os.Rename(path.Join(v.dir, v.Id.String()+".cpx"), path.Join(v.dir, v.Id.String()+".idx")); e != nil {
|
||||||
return 0, e
|
return e
|
||||||
}
|
}
|
||||||
if e = v.load(); e != nil {
|
if e = v.load(); e != nil {
|
||||||
return 0, e
|
return e
|
||||||
}
|
}
|
||||||
return 0, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Volume) copyDataAndGenerateIndexFile(srcName, dstName, idxName string) (err error) {
|
func (v *Volume) copyDataAndGenerateIndexFile(srcName, dstName, idxName string) (err error) {
|
||||||
|
@ -183,7 +193,6 @@ func (v *Volume) copyDataAndGenerateIndexFile(srcName, dstName, idxName string)
|
||||||
nv, ok := v.nm.Get(n.Id)
|
nv, ok := v.nm.Get(n.Id)
|
||||||
//log.Println("file size is", n.Size, "rest", rest)
|
//log.Println("file size is", n.Size, "rest", rest)
|
||||||
if !ok || nv.Offset*8 != old_offset {
|
if !ok || nv.Offset*8 != old_offset {
|
||||||
log.Println("expected offset should be", nv.Offset*8, "skipping", (rest - 16), "key", n.Id, "volume offset", old_offset, "data_size", n.Size, "rest", rest)
|
|
||||||
src.Seek(int64(rest), 1)
|
src.Seek(int64(rest), 1)
|
||||||
} else {
|
} else {
|
||||||
if nv.Size > 0 {
|
if nv.Size > 0 {
|
||||||
|
|
|
@ -9,4 +9,5 @@ type VolumeInfo struct {
|
||||||
RepType ReplicationType
|
RepType ReplicationType
|
||||||
FileCount int
|
FileCount int
|
||||||
DeleteCount int
|
DeleteCount int
|
||||||
|
DeletedByteCount uint32
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,6 +164,7 @@ func (n *NodeImpl) CollectDeadNodeAndFullVolumes(freshThreshHold int64, volumeSi
|
||||||
}
|
}
|
||||||
for _, v := range dn.volumes {
|
for _, v := range dn.volumes {
|
||||||
if uint64(v.Size) >= volumeSizeLimit {
|
if uint64(v.Size) >= volumeSizeLimit {
|
||||||
|
//fmt.Println("volume",v.Id,"size",v.Size,">",volumeSizeLimit)
|
||||||
n.GetTopology().chanFullVolumes <- &v
|
n.GetTopology().chanFullVolumes <- &v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,37 +10,82 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *Topology) Vacuum() int {
|
func batchVacuumVolumeCheck(vl *VolumeLayout, vid storage.VolumeId, locationlist *VolumeLocationList) bool {
|
||||||
total_counter := 0
|
ch := make(chan bool, locationlist.Length())
|
||||||
for _, vl := range t.replicaType2VolumeLayout {
|
for index, dn := range locationlist.list {
|
||||||
if vl != nil {
|
go func(index int, url string, vid storage.VolumeId) {
|
||||||
for vid, locationlist := range vl.vid2location {
|
//fmt.Println(index, "Check vacuuming", vid, "on", dn.Url())
|
||||||
each_volume_counter := 0
|
if e, ret := vacuumVolume_Check(url, vid); e != nil {
|
||||||
vl.removeFromWritable(vid)
|
//fmt.Println(index, "Error when checking vacuuming", vid, "on", url, e)
|
||||||
ch := make(chan int, locationlist.Length())
|
ch <- false
|
||||||
for _, dn := range locationlist.list {
|
} else {
|
||||||
go func(url string, vid storage.VolumeId) {
|
//fmt.Println(index, "Checked vacuuming", vid, "on", url)
|
||||||
vacuumVolume_Compact(url, vid)
|
ch <- ret
|
||||||
}(dn.Url(), vid)
|
|
||||||
}
|
}
|
||||||
|
}(index, dn.Url(), vid)
|
||||||
|
}
|
||||||
|
isCheckSuccess := true
|
||||||
for _ = range locationlist.list {
|
for _ = range locationlist.list {
|
||||||
select {
|
select {
|
||||||
case count := <-ch:
|
case canVacuum := <-ch:
|
||||||
each_volume_counter += count
|
isCheckSuccess = isCheckSuccess && canVacuum
|
||||||
case <-time.After(30 * time.Minute):
|
case <-time.After(30 * time.Minute):
|
||||||
each_volume_counter = 0
|
isCheckSuccess = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if each_volume_counter > 0 {
|
return isCheckSuccess
|
||||||
|
}
|
||||||
|
func batchVacuumVolumeCompact(vl *VolumeLayout, vid storage.VolumeId, locationlist *VolumeLocationList) bool {
|
||||||
|
vl.removeFromWritable(vid)
|
||||||
|
ch := make(chan bool, locationlist.Length())
|
||||||
|
for index, dn := range locationlist.list {
|
||||||
|
go func(index int, url string, vid storage.VolumeId) {
|
||||||
|
fmt.Println(index, "Start vacuuming", vid, "on", dn.Url())
|
||||||
|
if e := vacuumVolume_Compact(url, vid); e != nil {
|
||||||
|
fmt.Println(index, "Error when vacuuming", vid, "on", url, e)
|
||||||
|
ch <- false
|
||||||
|
} else {
|
||||||
|
fmt.Println(index, "Complete vacuuming", vid, "on", url)
|
||||||
|
ch <- true
|
||||||
|
}
|
||||||
|
}(index, dn.Url(), vid)
|
||||||
|
}
|
||||||
|
isVacuumSuccess := true
|
||||||
|
for _ = range locationlist.list {
|
||||||
|
select {
|
||||||
|
case _ = <-ch:
|
||||||
|
case <-time.After(30 * time.Minute):
|
||||||
|
isVacuumSuccess = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isVacuumSuccess
|
||||||
|
}
|
||||||
|
func batchVacuumVolumeCommit(vl *VolumeLayout, vid storage.VolumeId, locationlist *VolumeLocationList) bool {
|
||||||
|
isCommitSuccess := true
|
||||||
for _, dn := range locationlist.list {
|
for _, dn := range locationlist.list {
|
||||||
|
fmt.Println("Start Commiting vacuum", vid, "on", dn.Url())
|
||||||
if e := vacuumVolume_Commit(dn.Url(), vid); e != nil {
|
if e := vacuumVolume_Commit(dn.Url(), vid); e != nil {
|
||||||
fmt.Println("Error when committing on", dn.Url(), e)
|
fmt.Println("Error when committing vacuum", vid, "on", dn.Url(), e)
|
||||||
panic(e)
|
isCommitSuccess = false
|
||||||
|
} else {
|
||||||
|
fmt.Println("Complete Commiting vacuum", vid, "on", dn.Url())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if isCommitSuccess {
|
||||||
vl.setVolumeWritable(vid)
|
vl.setVolumeWritable(vid)
|
||||||
total_counter += each_volume_counter
|
}
|
||||||
|
return isCommitSuccess
|
||||||
|
}
|
||||||
|
func (t *Topology) Vacuum() int {
|
||||||
|
for _, vl := range t.replicaType2VolumeLayout {
|
||||||
|
if vl != nil {
|
||||||
|
for vid, locationlist := range vl.vid2location {
|
||||||
|
if batchVacuumVolumeCheck(vl, vid, locationlist) {
|
||||||
|
if batchVacuumVolumeCompact(vl, vid, locationlist) {
|
||||||
|
batchVacuumVolumeCommit(vl, vid, locationlist)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,25 +94,42 @@ func (t *Topology) Vacuum() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
type VacuumVolumeResult struct {
|
type VacuumVolumeResult struct {
|
||||||
Bytes int
|
Result bool
|
||||||
Error string
|
Error string
|
||||||
}
|
}
|
||||||
|
|
||||||
func vacuumVolume_Compact(urlLocation string, vid storage.VolumeId) (error, int) {
|
func vacuumVolume_Check(urlLocation string, vid storage.VolumeId) (error, bool) {
|
||||||
|
values := make(url.Values)
|
||||||
|
values.Add("volume", vid.String())
|
||||||
|
values.Add("garbageThreshold", "0.3")
|
||||||
|
jsonBlob, err := util.Post("http://"+urlLocation+"/admin/vacuum_volume_check", values)
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
var ret VacuumVolumeResult
|
||||||
|
if err := json.Unmarshal(jsonBlob, &ret); err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
if ret.Error != "" {
|
||||||
|
return errors.New(ret.Error), false
|
||||||
|
}
|
||||||
|
return nil, ret.Result
|
||||||
|
}
|
||||||
|
func vacuumVolume_Compact(urlLocation string, vid storage.VolumeId) error {
|
||||||
values := make(url.Values)
|
values := make(url.Values)
|
||||||
values.Add("volume", vid.String())
|
values.Add("volume", vid.String())
|
||||||
jsonBlob, err := util.Post("http://"+urlLocation+"/admin/vacuum_volume_compact", values)
|
jsonBlob, err := util.Post("http://"+urlLocation+"/admin/vacuum_volume_compact", values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, 0
|
return err
|
||||||
}
|
}
|
||||||
var ret VacuumVolumeResult
|
var ret VacuumVolumeResult
|
||||||
if err := json.Unmarshal(jsonBlob, &ret); err != nil {
|
if err := json.Unmarshal(jsonBlob, &ret); err != nil {
|
||||||
return err, 0
|
return err
|
||||||
}
|
}
|
||||||
if ret.Error != "" {
|
if ret.Error != "" {
|
||||||
return errors.New(ret.Error), 0
|
return errors.New(ret.Error)
|
||||||
}
|
}
|
||||||
return nil, ret.Bytes
|
return nil
|
||||||
}
|
}
|
||||||
func vacuumVolume_Commit(urlLocation string, vid storage.VolumeId) error {
|
func vacuumVolume_Commit(urlLocation string, vid storage.VolumeId) error {
|
||||||
values := make(url.Values)
|
values := make(url.Values)
|
||||||
|
|
|
@ -15,6 +15,12 @@ func (t *Topology) StartRefreshWritableVolumes() {
|
||||||
time.Sleep(time.Duration(float32(t.pulse*1e3)*(1+rand.Float32())) * time.Millisecond)
|
time.Sleep(time.Duration(float32(t.pulse*1e3)*(1+rand.Float32())) * time.Millisecond)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
go func() {
|
||||||
|
c := time.Tick(15 * time.Minute)
|
||||||
|
for _ = range c {
|
||||||
|
t.Vacuum()
|
||||||
|
}
|
||||||
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
Loading…
Reference in a new issue