-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
321 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
database,key,type,size,size_readable,element_count | ||
0,large,string,2056,2K,0 | ||
0,list,list,66,66B,4 | ||
0,hash,hash,64,64B,2 | ||
0,zset,zset,57,57B,2 | ||
0,set,set,39,39B,2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package helper | ||
|
||
import ( | ||
"container/heap" | ||
"encoding/csv" | ||
"errors" | ||
"fmt" | ||
"github.com/hdt3213/rdb/bytefmt" | ||
"github.com/hdt3213/rdb/core" | ||
"github.com/hdt3213/rdb/model" | ||
"os" | ||
"strconv" | ||
) | ||
|
||
type redisHeap struct { | ||
list []model.RedisObject | ||
capacity int | ||
minSize int // size of min object | ||
minIndex int // index of min object | ||
} | ||
|
||
func (h redisHeap) Len() int { | ||
return len(h.list) | ||
} | ||
|
||
// Max Heap | ||
func (h *redisHeap) Less(i, j int) bool { | ||
return h.list[i].GetSize() > h.list[j].GetSize() | ||
} | ||
|
||
func (h *redisHeap) Swap(i, j int) { | ||
h.list[i], h.list[j] = h.list[j], h.list[i] | ||
} | ||
|
||
func (h *redisHeap) Push(x interface{}) { | ||
h.list = append(h.list, x.(model.RedisObject)) | ||
} | ||
|
||
func (h *redisHeap) Pop() interface{} { | ||
item := h.list[len(h.list)-1] | ||
h.list = h.list[0 : len(h.list)-1] | ||
return item | ||
} | ||
|
||
// time complexity: O(n*log(m)), n is number of redis object, m is heap capacity. m if far less than n | ||
func (h *redisHeap) Append(x model.RedisObject) { | ||
// heap is full, skip | ||
if x.GetSize() <= h.minSize && h.Len() >= h.capacity { | ||
return | ||
} | ||
// if heap is full, pop min object | ||
if h.Len() >= h.capacity { | ||
// assert h.minIndex >= 0 | ||
heap.Remove(h, h.minIndex) | ||
} | ||
heap.Push(h, x) | ||
// update h.minSize | ||
h.minSize = 1<<31 - 1 | ||
for i := h.Len() - 1; i >= 0; i-- { // | ||
o := h.list[i] | ||
if o.GetSize() < h.minSize { | ||
h.minSize = o.GetSize() | ||
h.minIndex = i | ||
} | ||
} | ||
} | ||
|
||
func (h *redisHeap) Dump() []model.RedisObject { | ||
result := make([]model.RedisObject, 0, h.Len()) | ||
for h.Len() > 0 { | ||
o := heap.Pop(h).(model.RedisObject) | ||
result = append(result, o) | ||
} | ||
return result | ||
} | ||
|
||
func newRedisHeap(cap int) *redisHeap { | ||
list := make([]model.RedisObject, 0, cap) | ||
h := &redisHeap{ | ||
list: list, | ||
capacity: cap, | ||
minIndex: -1, | ||
} | ||
heap.Init(h) | ||
return h | ||
} | ||
|
||
// FindBiggestKeys read rdb file and find the largest N keys. | ||
// The invoker owns output, FindBiggestKeys won't close it | ||
func FindBiggestKeys(rdbFilename string, topN int, output *os.File) error { | ||
if rdbFilename == "" { | ||
return errors.New("src file path is required") | ||
} | ||
if topN <= 0 { | ||
return errors.New("n must greater than 0") | ||
} | ||
rdbFile, err := os.Open(rdbFilename) | ||
if err != nil { | ||
return fmt.Errorf("open rdb %s failed, %v", rdbFilename, err) | ||
} | ||
defer func() { | ||
_ = rdbFile.Close() | ||
}() | ||
p := core.NewDecoder(rdbFile) | ||
topList := newRedisHeap(topN) | ||
err = p.Parse(func(object model.RedisObject) bool { | ||
topList.Append(object) | ||
return true | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
_, err = output.WriteString("database,key,type,size,size_readable,element_count\n") | ||
if err != nil { | ||
return fmt.Errorf("write header failed: %v", err) | ||
} | ||
csvWriter := csv.NewWriter(output) | ||
defer csvWriter.Flush() | ||
for topList.Len() > 0 { | ||
object := heap.Pop(topList).(model.RedisObject) | ||
err = csvWriter.Write([]string{ | ||
strconv.Itoa(object.GetDBIndex()), | ||
object.GetKey(), | ||
object.GetType(), | ||
strconv.Itoa(object.GetSize()), | ||
bytefmt.FormatSize(uint64(object.GetSize())), | ||
strconv.Itoa(object.GetElemCount()), | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("csv write failed: %v", err) | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package helper | ||
|
||
import ( | ||
"github.com/hdt3213/rdb/model" | ||
"math/rand" | ||
"sort" | ||
"strconv" | ||
"testing" | ||
) | ||
|
||
func TestRedisHeap_Append(t *testing.T) { | ||
sizeMap := make(map[int]struct{}) // The behavior when encountering objects of the same size is undefined | ||
topN := 100 | ||
n := topN * 10 | ||
objects := make([]model.RedisObject, 0) | ||
for i := 0; i < n; i++ { | ||
var size int | ||
for { | ||
size = rand.Intn(n * 10) | ||
if _, ok := sizeMap[size]; !ok { | ||
sizeMap[size] = struct{}{} | ||
break | ||
} | ||
} | ||
o := &model.StringObject{ | ||
BaseObject: &model.BaseObject{ | ||
Key: strconv.Itoa(i), | ||
Size: size, | ||
}, | ||
} | ||
objects = append(objects, o) | ||
} | ||
topList := newRedisHeap(topN) | ||
for _, o := range objects { | ||
topList.Append(o) | ||
} | ||
actual := topList.Dump() | ||
sort.Slice(objects, func(i, j int) bool { | ||
return objects[i].GetSize() > objects[j].GetSize() | ||
}) | ||
expect := objects[0:topN] | ||
for i := 0; i < topN; i++ { | ||
o1 := actual[i] | ||
o2 := expect[i] | ||
if o1.GetSize() != o2.GetSize() { | ||
t.Errorf("wrong answer at index: %d", i) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.