mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2025-12-17 09:42:29 +08:00
add redis3
This commit is contained in:
@@ -3,11 +3,13 @@ package redis3
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/util/bptree"
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/util/skiplist"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
const maxNameBatchSizeLimit = 5
|
||||
|
||||
func insertChild(ctx context.Context, client redis.UniversalClient, key string, name string) error {
|
||||
data, err := client.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
@@ -15,12 +17,22 @@ func insertChild(ctx context.Context, client redis.UniversalClient, key string,
|
||||
return fmt.Errorf("read %s: %v", key, err)
|
||||
}
|
||||
}
|
||||
rootNode := &bptree.ProtoNode{}
|
||||
if err := proto.UnmarshalMerge([]byte(data), rootNode); err != nil {
|
||||
return fmt.Errorf("decoding root for %s: %v", key, err)
|
||||
store := newSkipListElementStore(key, client)
|
||||
nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit)
|
||||
|
||||
// println("add", key, name)
|
||||
if err := nameList.WriteName(name); err != nil {
|
||||
glog.Errorf("add %s %s: %v", key, name, err)
|
||||
return err
|
||||
}
|
||||
tree := rootNode.ToBpTree()
|
||||
tree.Add(bptree.String(name), nil)
|
||||
if !nameList.HasChanges() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := client.Set(ctx, key, nameList.ToBytes(), 0).Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -31,19 +43,69 @@ func removeChild(ctx context.Context, client redis.UniversalClient, key string,
|
||||
return fmt.Errorf("read %s: %v", key, err)
|
||||
}
|
||||
}
|
||||
rootNode := &bptree.ProtoNode{}
|
||||
if err := proto.UnmarshalMerge([]byte(data), rootNode); err != nil {
|
||||
return fmt.Errorf("decoding root for %s: %v", key, err)
|
||||
store := newSkipListElementStore(key, client)
|
||||
nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit)
|
||||
|
||||
if err := nameList.DeleteName(name); err != nil {
|
||||
return err
|
||||
}
|
||||
tree := rootNode.ToBpTree()
|
||||
tree.Add(bptree.String(name), nil)
|
||||
if !nameList.HasChanges() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := client.Set(ctx, key, nameList.ToBytes(), 0).Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeChildren(ctx context.Context, client redis.UniversalClient, key string, onDeleteFn func(name string) error) error {
|
||||
|
||||
data, err := client.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
if err != redis.Nil {
|
||||
return fmt.Errorf("read %s: %v", key, err)
|
||||
}
|
||||
}
|
||||
store := newSkipListElementStore(key, client)
|
||||
nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit)
|
||||
|
||||
if err = nameList.ListNames("", func(name string) bool {
|
||||
if err := onDeleteFn(name); err != nil {
|
||||
glog.Errorf("delete %s child %s: %v", key, name, err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = nameList.RemoteAllListElement(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func iterateChildren(ctx context.Context, client redis.UniversalClient, key string, eachFn func(name string) error) error {
|
||||
func listChildren(ctx context.Context, client redis.UniversalClient, key string, startFileName string, eachFn func(name string) bool) error {
|
||||
|
||||
data, err := client.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
if err != redis.Nil {
|
||||
return fmt.Errorf("read %s: %v", key, err)
|
||||
}
|
||||
}
|
||||
store := newSkipListElementStore(key, client)
|
||||
nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit)
|
||||
|
||||
if err = nameList.ListNames(startFileName, func(name string) bool {
|
||||
return eachFn(name)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
52
weed/filer/redis3/skiplist_element_store.go
Normal file
52
weed/filer/redis3/skiplist_element_store.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package redis3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/util/skiplist"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
type SkipListElementStore struct {
|
||||
prefix string
|
||||
client redis.UniversalClient
|
||||
}
|
||||
|
||||
var _ = skiplist.ListStore(&SkipListElementStore{})
|
||||
|
||||
func newSkipListElementStore(prefix string, client redis.UniversalClient) *SkipListElementStore {
|
||||
return &SkipListElementStore{
|
||||
prefix: prefix,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SkipListElementStore) SaveElement(id int64, element *skiplist.SkipListElement) error {
|
||||
key := fmt.Sprintf("%s%d", m.prefix, id)
|
||||
data, err := proto.Marshal(element)
|
||||
if err != nil {
|
||||
glog.Errorf("marshal %s: %v", key, err)
|
||||
}
|
||||
return m.client.Set(context.Background(), key, data, 0).Err()
|
||||
}
|
||||
|
||||
func (m *SkipListElementStore) DeleteElement(id int64) error {
|
||||
key := fmt.Sprintf("%s%d", m.prefix, id)
|
||||
return m.client.Del(context.Background(), key).Err()
|
||||
}
|
||||
|
||||
func (m *SkipListElementStore) LoadElement(id int64) (*skiplist.SkipListElement, error) {
|
||||
key := fmt.Sprintf("%s%d", m.prefix, id)
|
||||
data, err := m.client.Get(context.Background(), key).Result()
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
t := &skiplist.SkipListElement{}
|
||||
err = proto.Unmarshal([]byte(data), t)
|
||||
return t, err
|
||||
}
|
||||
@@ -115,6 +115,8 @@ func (store *UniversalRedis3Store) DeleteFolderChildren(ctx context.Context, ful
|
||||
if err != nil {
|
||||
return fmt.Errorf("DeleteFolderChildren %s in parent dir: %v", fullpath, err)
|
||||
}
|
||||
// not efficient, but need to remove if it is a directory
|
||||
store.Client.Del(ctx, genDirectoryListKey(string(path)))
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -127,41 +129,41 @@ func (store *UniversalRedis3Store) ListDirectoryPrefixedEntries(ctx context.Cont
|
||||
func (store *UniversalRedis3Store) ListDirectoryEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) {
|
||||
|
||||
dirListKey := genDirectoryListKey(string(dirPath))
|
||||
start := int64(0)
|
||||
if startFileName != "" {
|
||||
start, _ = store.Client.ZRank(ctx, dirListKey, startFileName).Result()
|
||||
if !includeStartFile {
|
||||
start++
|
||||
}
|
||||
}
|
||||
members, err := store.Client.ZRange(ctx, dirListKey, start, start+int64(limit)-1).Result()
|
||||
if err != nil {
|
||||
return lastFileName, fmt.Errorf("list %s : %v", dirPath, err)
|
||||
}
|
||||
counter := int64(0)
|
||||
|
||||
err = listChildren(ctx, store.Client, dirListKey, startFileName, func(fileName string) bool {
|
||||
if startFileName != "" {
|
||||
if !includeStartFile && startFileName == fileName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// fetch entry meta
|
||||
for _, fileName := range members {
|
||||
path := util.NewFullPath(string(dirPath), fileName)
|
||||
entry, err := store.FindEntry(ctx, path)
|
||||
lastFileName = fileName
|
||||
if err != nil {
|
||||
glog.V(0).Infof("list %s : %v", path, err)
|
||||
if err == filer_pb.ErrNotFound {
|
||||
continue
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if entry.TtlSec > 0 {
|
||||
if entry.Attr.Crtime.Add(time.Duration(entry.TtlSec) * time.Second).Before(time.Now()) {
|
||||
store.Client.Del(ctx, string(path)).Result()
|
||||
store.Client.ZRem(ctx, dirListKey, fileName).Result()
|
||||
continue
|
||||
return true
|
||||
}
|
||||
}
|
||||
counter++
|
||||
if !eachEntryFunc(entry) {
|
||||
break
|
||||
return false
|
||||
}
|
||||
if counter >= limit {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return lastFileName, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user