redis3 adds distributed locking

This commit is contained in:
Chris Lu
2021-10-06 00:03:54 -07:00
parent f0d1e7bd05
commit 893f0587b1
8 changed files with 142 additions and 14 deletions

View File

@@ -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 {

View 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
})
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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