mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2025-10-15 20:06:19 +08:00
redis3 adds distributed locking
This commit is contained in:
@@ -10,7 +10,18 @@ import (
|
||||
|
||||
const maxNameBatchSizeLimit = 1000
|
||||
|
||||
func insertChild(ctx context.Context, client redis.UniversalClient, key string, name string) error {
|
||||
func insertChild(ctx context.Context, redisStore *UniversalRedis3Store, key string, name string) error {
|
||||
|
||||
// lock and unlock
|
||||
mutex := redisStore.redsync.NewMutex(key+"lock")
|
||||
if err := mutex.Lock(); err != nil {
|
||||
return fmt.Errorf("lock %s: %v", key, err)
|
||||
}
|
||||
defer func() {
|
||||
mutex.Unlock()
|
||||
}()
|
||||
|
||||
client := redisStore.Client
|
||||
data, err := client.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
if err != redis.Nil {
|
||||
@@ -20,11 +31,11 @@ func insertChild(ctx context.Context, client redis.UniversalClient, key string,
|
||||
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
|
||||
}
|
||||
|
||||
if !nameList.HasChanges() {
|
||||
return nil
|
||||
}
|
||||
@@ -36,7 +47,16 @@ func insertChild(ctx context.Context, client redis.UniversalClient, key string,
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeChild(ctx context.Context, client redis.UniversalClient, key string, name string) error {
|
||||
func removeChild(ctx context.Context, redisStore *UniversalRedis3Store, key string, name string) error {
|
||||
|
||||
// lock and unlock
|
||||
mutex := redisStore.redsync.NewMutex(key+"lock")
|
||||
if err := mutex.Lock(); err != nil {
|
||||
return fmt.Errorf("lock %s: %v", key, err)
|
||||
}
|
||||
defer mutex.Unlock()
|
||||
|
||||
client := redisStore.Client
|
||||
data, err := client.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
if err != redis.Nil {
|
||||
@@ -60,8 +80,16 @@ func removeChild(ctx context.Context, client redis.UniversalClient, key string,
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeChildren(ctx context.Context, client redis.UniversalClient, key string, onDeleteFn func(name string) error) error {
|
||||
func removeChildren(ctx context.Context, redisStore *UniversalRedis3Store, key string, onDeleteFn func(name string) error) error {
|
||||
|
||||
// lock and unlock
|
||||
mutex := redisStore.redsync.NewMutex(key+"lock")
|
||||
if err := mutex.Lock(); err != nil {
|
||||
return fmt.Errorf("lock %s: %v", key, err)
|
||||
}
|
||||
defer mutex.Unlock()
|
||||
|
||||
client := redisStore.Client
|
||||
data, err := client.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
if err != redis.Nil {
|
||||
@@ -84,13 +112,13 @@ func removeChildren(ctx context.Context, client redis.UniversalClient, key strin
|
||||
if err = nameList.RemoteAllListElement(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func listChildren(ctx context.Context, client redis.UniversalClient, key string, startFileName string, eachFn func(name string) bool) error {
|
||||
|
||||
func listChildren(ctx context.Context, redisStore *UniversalRedis3Store, key string, startFileName string, eachFn func(name string) bool) error {
|
||||
client := redisStore.Client
|
||||
data, err := client.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
if err != redis.Nil {
|
||||
|
75
weed/filer/redis3/kv_directory_children_test.go
Normal file
75
weed/filer/redis3/kv_directory_children_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package redis3
|
||||
|
||||
import (
|
||||
"github.com/chrislusf/seaweedfs/weed/util/skiplist"
|
||||
goredislib "github.com/go-redis/redis/v8"
|
||||
"github.com/stvp/tempredis"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var names = []string{
|
||||
"cassandra.in.sh",
|
||||
"cassandra",
|
||||
"debug-cql.bat",
|
||||
"nodetool",
|
||||
"nodetool.bat",
|
||||
"source-conf.ps1",
|
||||
"sstableloader",
|
||||
"sstableloader.bat",
|
||||
"sstablescrub",
|
||||
"sstablescrub.bat",
|
||||
"sstableupgrade",
|
||||
"sstableupgrade.bat",
|
||||
"sstableutil",
|
||||
"sstableutil.bat",
|
||||
"sstableverify",
|
||||
"sstableverify.bat",
|
||||
"stop-server",
|
||||
"stop-server.bat",
|
||||
"stop-server.ps1",
|
||||
"cassandra.in.bat",
|
||||
"cqlsh.py",
|
||||
"cqlsh",
|
||||
"cassandra.ps1",
|
||||
"cqlsh.bat",
|
||||
"debug-cql",
|
||||
"cassandra.bat",
|
||||
}
|
||||
|
||||
func TestNameList(t *testing.T) {
|
||||
server, err := tempredis.Start(tempredis.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer server.Term()
|
||||
|
||||
client := goredislib.NewClient(&goredislib.Options{
|
||||
Network: "unix",
|
||||
Addr: server.Socket(),
|
||||
})
|
||||
|
||||
store := newSkipListElementStore("/yyy/bin", client)
|
||||
var data []byte
|
||||
for _, name := range names {
|
||||
nameList := skiplist.LoadNameList(data, store, maxNameBatchSizeLimit)
|
||||
nameList.WriteName(name)
|
||||
|
||||
nameList.ListNames("", func(name string) bool {
|
||||
println(" * ", name)
|
||||
return true
|
||||
})
|
||||
|
||||
if nameList.HasChanges() {
|
||||
println("has some changes")
|
||||
data = nameList.ToBytes()
|
||||
}
|
||||
println()
|
||||
}
|
||||
|
||||
nameList := skiplist.LoadNameList(data, store, maxNameBatchSizeLimit)
|
||||
nameList.ListNames("", func(name string) bool {
|
||||
println(name)
|
||||
return true
|
||||
})
|
||||
|
||||
}
|
@@ -4,6 +4,8 @@ import (
|
||||
"github.com/chrislusf/seaweedfs/weed/filer"
|
||||
"github.com/chrislusf/seaweedfs/weed/util"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/go-redsync/redsync/v4"
|
||||
"github.com/go-redsync/redsync/v4/redis/goredis/v8"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -38,5 +40,6 @@ func (store *RedisCluster3Store) initialize(addresses []string, password string,
|
||||
ReadOnly: readOnly,
|
||||
RouteByLatency: routeByLatency,
|
||||
})
|
||||
store.redsync = redsync.New(goredis.NewPool(store.Client))
|
||||
return
|
||||
}
|
||||
|
@@ -4,6 +4,8 @@ import (
|
||||
"github.com/chrislusf/seaweedfs/weed/filer"
|
||||
"github.com/chrislusf/seaweedfs/weed/util"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/go-redsync/redsync/v4"
|
||||
"github.com/go-redsync/redsync/v4/redis/goredis/v8"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -32,5 +34,6 @@ func (store *Redis3Store) initialize(hostPort string, password string, database
|
||||
Password: password,
|
||||
DB: database,
|
||||
})
|
||||
store.redsync = redsync.New(goredis.NewPool(store.Client))
|
||||
return
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package redis3
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-redsync/redsync/v4"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
@@ -18,7 +19,8 @@ const (
|
||||
)
|
||||
|
||||
type UniversalRedis3Store struct {
|
||||
Client redis.UniversalClient
|
||||
Client redis.UniversalClient
|
||||
redsync *redsync.Redsync
|
||||
}
|
||||
|
||||
func (store *UniversalRedis3Store) BeginTransaction(ctx context.Context) (context.Context, error) {
|
||||
@@ -49,7 +51,7 @@ func (store *UniversalRedis3Store) InsertEntry(ctx context.Context, entry *filer
|
||||
dir, name := entry.FullPath.DirAndName()
|
||||
|
||||
if name != "" {
|
||||
if err = insertChild(ctx, store.Client, genDirectoryListKey(dir), name); err != nil {
|
||||
if err = insertChild(ctx, store, genDirectoryListKey(dir), name); err != nil {
|
||||
return fmt.Errorf("persisting %s in parent dir: %v", entry.FullPath, err)
|
||||
}
|
||||
}
|
||||
@@ -99,7 +101,7 @@ func (store *UniversalRedis3Store) DeleteEntry(ctx context.Context, fullpath uti
|
||||
dir, name := fullpath.DirAndName()
|
||||
|
||||
if name != "" {
|
||||
if err = removeChild(ctx, store.Client, genDirectoryListKey(dir), name); err != nil {
|
||||
if err = removeChild(ctx, store, genDirectoryListKey(dir), name); err != nil {
|
||||
return fmt.Errorf("DeleteEntry %s in parent dir: %v", fullpath, err)
|
||||
}
|
||||
}
|
||||
@@ -109,7 +111,7 @@ func (store *UniversalRedis3Store) DeleteEntry(ctx context.Context, fullpath uti
|
||||
|
||||
func (store *UniversalRedis3Store) DeleteFolderChildren(ctx context.Context, fullpath util.FullPath) (err error) {
|
||||
|
||||
return removeChildren(ctx, store.Client, genDirectoryListKey(string(fullpath)), func(name string) error {
|
||||
return removeChildren(ctx, store, genDirectoryListKey(string(fullpath)), func(name string) error {
|
||||
path := util.NewFullPath(string(fullpath), name)
|
||||
_, err = store.Client.Del(ctx, string(path)).Result()
|
||||
if err != nil {
|
||||
@@ -131,7 +133,7 @@ func (store *UniversalRedis3Store) ListDirectoryEntries(ctx context.Context, dir
|
||||
dirListKey := genDirectoryListKey(string(dirPath))
|
||||
counter := int64(0)
|
||||
|
||||
err = listChildren(ctx, store.Client, dirListKey, startFileName, func(fileName string) bool {
|
||||
err = listChildren(ctx, store, dirListKey, startFileName, func(fileName string) bool {
|
||||
if startFileName != "" {
|
||||
if !includeStartFile && startFileName == fileName {
|
||||
return true
|
||||
|
Reference in New Issue
Block a user