migrate from github
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
package lock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/XingfenD/yoresee_doc/pkg/errs"
|
||||
"github.com/XingfenD/yoresee_doc/pkg/storage"
|
||||
)
|
||||
|
||||
// distributed lock, depredated by usage of singleflight
|
||||
type DistributedLock struct {
|
||||
key string
|
||||
value string
|
||||
expiration time.Duration
|
||||
}
|
||||
|
||||
func NewDistributedLock(key string, expiration time.Duration) *DistributedLock {
|
||||
return &DistributedLock{
|
||||
key: key,
|
||||
value: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||
expiration: expiration,
|
||||
}
|
||||
}
|
||||
|
||||
func (dl *DistributedLock) Lock(ctx context.Context) error {
|
||||
success, err := storage.SetNX(ctx, dl.key, dl.value, dl.expiration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !success {
|
||||
return errs.Detail(errs.ErrLockAcquireFailed, dl.key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dl *DistributedLock) TryLock(ctx context.Context) (bool, error) {
|
||||
return storage.SetNX(ctx, dl.key, dl.value, dl.expiration)
|
||||
}
|
||||
|
||||
func (dl *DistributedLock) Unlock(ctx context.Context) error {
|
||||
script := `
|
||||
if redis.call("get", KEYS[1]) == ARGV[1] then
|
||||
return redis.call("del", KEYS[1])
|
||||
else
|
||||
return 0
|
||||
end
|
||||
`
|
||||
result, err := storage.GetRedis().Eval(ctx, script, []string{dl.key}, dl.value).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.(int64) == 0 {
|
||||
return errs.Detail(errs.ErrLockNotHeld, dl.key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package lock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const LockCachePrefix = "lock:"
|
||||
|
||||
type RetryLockFunc func(ctx context.Context) (interface{}, error)
|
||||
|
||||
type CheckFunc func(ctx context.Context) (result interface{}, completed bool, err error)
|
||||
|
||||
func AcquireWithRetry(ctx context.Context, lockKey string, expiration time.Duration,
|
||||
maxRetries int, retryInterval time.Duration, checkFn CheckFunc, execFn RetryLockFunc) (interface{}, error) {
|
||||
|
||||
distributedLock := NewDistributedLock(lockKey, expiration)
|
||||
|
||||
err := distributedLock.Lock(ctx)
|
||||
if err == nil {
|
||||
defer distributedLock.Unlock(ctx)
|
||||
|
||||
return execFn(ctx)
|
||||
}
|
||||
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
time.Sleep(retryInterval)
|
||||
|
||||
if checkFn != nil {
|
||||
result, completed, err := checkFn(ctx)
|
||||
if err == nil && completed {
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
acquired, lockErr := distributedLock.TryLock(ctx)
|
||||
if lockErr != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if acquired {
|
||||
defer distributedLock.Unlock(ctx)
|
||||
|
||||
return execFn(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
Reference in New Issue
Block a user