116 lines
2.3 KiB
Go
116 lines
2.3 KiB
Go
package dao
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/pkg/errors"
|
|
"go-common/library/cache/redis"
|
|
"go-common/library/log"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
UnLockGetWrong = "UnLockGetWrong"
|
|
LockFailed = "LockFailed"
|
|
UserWalletPrefix = "user_wallet_lock_uid_"
|
|
ErrUnLockGet = errors.New(UnLockGetWrong)
|
|
ErrLockFailed = errors.New(LockFailed)
|
|
)
|
|
|
|
func (d *Dao) IsLockFailedError(err error) bool {
|
|
return err == ErrLockFailed
|
|
}
|
|
|
|
func lockKey(k string) string {
|
|
return "wallet_lock_key:" + k
|
|
}
|
|
|
|
/*
|
|
ttl ms
|
|
retry 重试次数
|
|
retryDelay us
|
|
*/
|
|
func (d *Dao) Lock(ctx context.Context, key string, ttl int, retry int, retryDelay int) (err error, gotLock bool, lockValue string) {
|
|
|
|
if retry <= 0 {
|
|
retry = 1
|
|
}
|
|
lockValue = "locked:" + randomString(5)
|
|
retryTimes := 0
|
|
conn := d.redis.Get(ctx)
|
|
defer conn.Close()
|
|
|
|
realKey := lockKey(key)
|
|
|
|
for ; retryTimes < retry; retryTimes++ {
|
|
var res interface{}
|
|
res, err = conn.Do("SET", realKey, lockValue, "PX", ttl, "NX")
|
|
if err != nil {
|
|
log.Error("redis_lock failed:%s:%s", realKey, err.Error())
|
|
break
|
|
}
|
|
|
|
if res != nil {
|
|
gotLock = true
|
|
break
|
|
}
|
|
time.Sleep(time.Duration(retryDelay * 1000))
|
|
}
|
|
return
|
|
}
|
|
|
|
func (d *Dao) UnLock(ctx context.Context, key string, lockValue string) (err error) {
|
|
conn := d.redis.Get(ctx)
|
|
defer conn.Close()
|
|
realKey := lockKey(key)
|
|
res, err := redis.String(conn.Do("GET", realKey))
|
|
if err != nil {
|
|
return
|
|
}
|
|
if res != lockValue {
|
|
err = ErrUnLockGet
|
|
return
|
|
}
|
|
|
|
_, err = conn.Do("DEL", realKey)
|
|
|
|
return
|
|
}
|
|
|
|
func (d *Dao) ForceUnLock(ctx context.Context, key string) (err error) {
|
|
realKey := lockKey(key)
|
|
conn := d.redis.Get(ctx)
|
|
defer conn.Close()
|
|
_, err = conn.Do("DEL", realKey)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
func (d *Dao) LockTransactionId(ctx context.Context, tid string) (err error) {
|
|
err, gotLock, _ := d.Lock(ctx, tid, 300*1000, 0, 200000)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if !gotLock {
|
|
err = ErrLockFailed
|
|
}
|
|
return
|
|
}
|
|
|
|
func (d *Dao) LockUser(ctx context.Context, uid int64) (err error, gotLock bool, lockValue string) {
|
|
lockTime := 600
|
|
retry := 1
|
|
retryDelay := 10
|
|
|
|
return d.Lock(ctx, getUserLockKey(uid), lockTime*1000, retry, retryDelay*1000)
|
|
}
|
|
|
|
func (d *Dao) UnLockUser(ctx context.Context, uid int64, lockValue string) error {
|
|
return d.UnLock(ctx, getUserLockKey(uid), lockValue)
|
|
}
|
|
|
|
func getUserLockKey(uid int64) string {
|
|
return fmt.Sprintf("%s%v", UserWalletPrefix, uid)
|
|
}
|