chrislu
2 years ago
1 changed files with 151 additions and 0 deletions
@ -0,0 +1,151 @@ |
|||||
|
package cluster |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"fmt" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/glog" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/pb" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/util" |
||||
|
"google.golang.org/grpc" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
type LockClient struct { |
||||
|
grpcDialOption grpc.DialOption |
||||
|
maxLockDuration time.Duration |
||||
|
sleepDuration time.Duration |
||||
|
} |
||||
|
|
||||
|
func NewLockClient(grpcDialOption grpc.DialOption) *LockClient { |
||||
|
return &LockClient{ |
||||
|
grpcDialOption: grpcDialOption, |
||||
|
maxLockDuration: 5 * time.Second, |
||||
|
sleepDuration: 4 * time.Millisecond, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
type LiveLock struct { |
||||
|
key string |
||||
|
renewToken string |
||||
|
expireAtNs int64 |
||||
|
filer pb.ServerAddress |
||||
|
cancelCh chan struct{} |
||||
|
grpcDialOption grpc.DialOption |
||||
|
isLocked bool |
||||
|
} |
||||
|
|
||||
|
func (lc *LockClient) NewLock(filer pb.ServerAddress, key string, lockDuration time.Duration) (lock *LiveLock) { |
||||
|
lock = &LiveLock{ |
||||
|
key: key, |
||||
|
filer: filer, |
||||
|
cancelCh: make(chan struct{}), |
||||
|
expireAtNs: time.Now().Add(lockDuration).UnixNano(), |
||||
|
grpcDialOption: lc.grpcDialOption, |
||||
|
} |
||||
|
var needRenewal bool |
||||
|
if lockDuration > lc.maxLockDuration { |
||||
|
lockDuration = lc.maxLockDuration |
||||
|
needRenewal = true |
||||
|
} |
||||
|
util.RetryForever("create lock:"+key, func() error { |
||||
|
errorMessage, err := lock.doLock(lockDuration) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
if errorMessage != "" { |
||||
|
return fmt.Errorf("%v", errorMessage) |
||||
|
} |
||||
|
return nil |
||||
|
}, func(err error) (shouldContinue bool) { |
||||
|
if err != nil { |
||||
|
glog.Warningf("create lock %s: %s", key, err) |
||||
|
} |
||||
|
return lock.renewToken == "" |
||||
|
}) |
||||
|
|
||||
|
lock.isLocked = true |
||||
|
|
||||
|
if needRenewal { |
||||
|
go lc.keepLock(lock) |
||||
|
} |
||||
|
|
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (lock *LiveLock) IsLocked() bool { |
||||
|
return lock.isLocked |
||||
|
} |
||||
|
|
||||
|
func (lock *LiveLock) Unlock() error { |
||||
|
close(lock.cancelCh) |
||||
|
return pb.WithFilerClient(false, 0, lock.filer, lock.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { |
||||
|
_, err := client.Unlock(context.Background(), &filer_pb.UnlockRequest{ |
||||
|
Name: lock.key, |
||||
|
RenewToken: lock.renewToken, |
||||
|
}) |
||||
|
return err |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
func (lc *LockClient) keepLock(lock *LiveLock) { |
||||
|
for { |
||||
|
select { |
||||
|
case <-time.After(lc.sleepDuration): |
||||
|
// renew the lock if lock.expireAtNs is still greater than now
|
||||
|
util.RetryForever("keep lock:"+lock.key, func() error { |
||||
|
lockDuration := time.Duration(lock.expireAtNs-time.Now().UnixNano()) * time.Nanosecond |
||||
|
if lockDuration > lc.maxLockDuration { |
||||
|
lockDuration = lc.maxLockDuration |
||||
|
} |
||||
|
if lockDuration <= 0 { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
errorMessage, err := lock.doLock(lockDuration) |
||||
|
if err != nil { |
||||
|
lock.isLocked = false |
||||
|
return err |
||||
|
} |
||||
|
if errorMessage != "" { |
||||
|
lock.isLocked = false |
||||
|
return fmt.Errorf("%v", errorMessage) |
||||
|
} |
||||
|
return nil |
||||
|
}, func(err error) (shouldContinue bool) { |
||||
|
if err == nil { |
||||
|
return false |
||||
|
} |
||||
|
glog.Warningf("keep lock %s: %v", lock.key, err) |
||||
|
return true |
||||
|
}) |
||||
|
if !lock.isLocked { |
||||
|
return |
||||
|
} |
||||
|
case <-lock.cancelCh: |
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (lock *LiveLock) doLock(lockDuration time.Duration) (errorMessage string, err error) { |
||||
|
err = pb.WithFilerClient(false, 0, lock.filer, lock.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { |
||||
|
resp, err := client.Lock(context.Background(), &filer_pb.LockRequest{ |
||||
|
Name: lock.key, |
||||
|
SecondsToLock: int64(lockDuration.Seconds()), |
||||
|
RenewToken: lock.renewToken, |
||||
|
IsMoved: false, |
||||
|
}) |
||||
|
if err == nil { |
||||
|
lock.renewToken = resp.RenewToken |
||||
|
} |
||||
|
if resp != nil { |
||||
|
errorMessage = resp.Error |
||||
|
if resp.MovedTo != "" { |
||||
|
lock.filer = pb.ServerAddress(resp.MovedTo) |
||||
|
} |
||||
|
} |
||||
|
return err |
||||
|
}) |
||||
|
return |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue