mirror of
https://github.com/openimsdk/open-im-server.git
synced 2026-09-04 22:53:17 +08:00
61 lines
1.5 KiB
Go
61 lines
1.5 KiB
Go
package redis
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
|
"github.com/openimsdk/tools/errs"
|
|
"github.com/openimsdk/tools/log"
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
const lockPrefix = "LOCK:"
|
|
|
|
func NewLock(rdb redis.UniversalClient) cache.Lock {
|
|
return &redisLock{rdb: rdb}
|
|
}
|
|
|
|
type redisLock struct {
|
|
rdb redis.UniversalClient
|
|
}
|
|
|
|
func (x *redisLock) Lock(ctx context.Context, key string, timeout time.Duration) (string, error) {
|
|
uid, err := uuid.NewUUID()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if timeout < time.Second {
|
|
timeout = time.Minute * 2
|
|
}
|
|
value := hex.EncodeToString(uid[:])
|
|
key = lockPrefix + key
|
|
for {
|
|
ok, err := x.rdb.SetNX(ctx, key, value, timeout).Result()
|
|
if err != nil {
|
|
return "", errs.WrapMsg(err, "get redis lock", "key", key)
|
|
}
|
|
if ok {
|
|
return value, nil
|
|
}
|
|
timer := time.NewTimer(50 * time.Millisecond)
|
|
select {
|
|
case <-ctx.Done():
|
|
timer.Stop()
|
|
return "", context.Cause(ctx)
|
|
case <-timer.C:
|
|
}
|
|
}
|
|
}
|
|
|
|
func (x *redisLock) Unlock(ctx context.Context, key, value string) {
|
|
ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 10*time.Second)
|
|
defer cancel()
|
|
script := "\nlocal value = redis.call(\"GET\", KEYS[1])\nif value == ARGV[1] then\n return redis.call(\"DEL\", KEYS[1])\nend\nreturn 0"
|
|
if err := x.rdb.Eval(ctx, script, []string{lockPrefix + key}, value).Err(); err != nil {
|
|
log.ZWarn(ctx, "unlock redis lock", err, "key", key)
|
|
}
|
|
}
|