api: Adding Destroy to cleanup a lock
This commit is contained in:
parent
18ebb1a8e9
commit
a12883a5be
44
api/lock.go
44
api/lock.go
|
@ -33,6 +33,10 @@ var (
|
||||||
// ErrLockNotHeld is returned if we attempt to unlock a lock
|
// ErrLockNotHeld is returned if we attempt to unlock a lock
|
||||||
// that we do not hold.
|
// that we do not hold.
|
||||||
ErrLockNotHeld = fmt.Errorf("Lock not held")
|
ErrLockNotHeld = fmt.Errorf("Lock not held")
|
||||||
|
|
||||||
|
// ErrLockInUse is returned if we attempt to destroy a lock
|
||||||
|
// that is in use.
|
||||||
|
ErrLockInUse = fmt.Errorf("Lock in use")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lock is used to implement client-side leader election. It is follows the
|
// Lock is used to implement client-side leader election. It is follows the
|
||||||
|
@ -217,6 +221,46 @@ func (l *Lock) Unlock() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Destroy is used to cleanup the lock entry. It is not necessary
|
||||||
|
// to invoke. It will fail if the lock is in use.
|
||||||
|
func (l *Lock) Destroy() error {
|
||||||
|
// Hold the lock as we try to release
|
||||||
|
l.l.Lock()
|
||||||
|
defer l.l.Unlock()
|
||||||
|
|
||||||
|
// Check if we already hold the lock
|
||||||
|
if l.isHeld {
|
||||||
|
return ErrLockHeld
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for an existing lock
|
||||||
|
kv := l.c.KV()
|
||||||
|
pair, _, err := kv.Get(l.opts.Key, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read lock: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing to do if the lock does not exist
|
||||||
|
if pair == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it is in use
|
||||||
|
if pair.Session != "" {
|
||||||
|
return ErrLockInUse
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt the delete
|
||||||
|
didRemove, _, err := kv.DeleteCAS(pair, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to remove lock: %v", err)
|
||||||
|
}
|
||||||
|
if !didRemove {
|
||||||
|
return ErrLockInUse
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// createSession is used to create a new managed session
|
// createSession is used to create a new managed session
|
||||||
func (l *Lock) createSession() (string, error) {
|
func (l *Lock) createSession() (string, error) {
|
||||||
session := l.c.Session()
|
session := l.c.Session()
|
||||||
|
|
|
@ -182,3 +182,71 @@ func TestLock_Contend(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLock_Destroy(t *testing.T) {
|
||||||
|
c, s := makeClient(t)
|
||||||
|
defer s.stop()
|
||||||
|
|
||||||
|
lock, err := c.LockKey("test/lock")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should work
|
||||||
|
leaderCh, err := lock.Lock(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if leaderCh == nil {
|
||||||
|
t.Fatalf("not leader")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy should fail
|
||||||
|
if err := lock.Destroy(); err != ErrLockHeld {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be able to release
|
||||||
|
err = lock.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acquire with a different lock
|
||||||
|
l2, err := c.LockKey("test/lock")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should work
|
||||||
|
leaderCh, err = l2.Lock(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if leaderCh == nil {
|
||||||
|
t.Fatalf("not leader")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy should still fail
|
||||||
|
if err := lock.Destroy(); err != ErrLockInUse {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should relese
|
||||||
|
err = l2.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy should work
|
||||||
|
err = lock.Destroy()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double destroy should work
|
||||||
|
err = l2.Destroy()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue