Skip to content

Commit

Permalink
More graceful nil-lock handling (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
dim authored Apr 7, 2023
1 parent bb1982d commit 9de9468
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## v0.9.2

- Feature: better handling of nil lock.Release() [#68](https://github.com/bsm/redislock/pull/68)

## v0.9.1

- Fix: reset backoff ticker for exponential backoff [#58](https://github.com/bsm/redislock/pull/58)
Expand Down
18 changes: 11 additions & 7 deletions redislock.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (c *Client) Obtain(ctx context.Context, key string, ttl time.Duration, opt
if err != nil {
return nil, err
} else if ok {
return &Lock{client: c, key: key, value: value}, nil
return &Lock{Client: c, key: key, value: value}, nil
}

backoff := retry.NextBackoff()
Expand Down Expand Up @@ -116,9 +116,9 @@ func (c *Client) randomToken() (string, error) {

// Lock represents an obtained, distributed lock.
type Lock struct {
client *Client
key string
value string
*Client
key string
value string
}

// Obtain is a short-cut for New(...).Obtain(...).
Expand All @@ -143,7 +143,7 @@ func (l *Lock) Metadata() string {

// TTL returns the remaining time-to-live. Returns 0 if the lock has expired.
func (l *Lock) TTL(ctx context.Context) (time.Duration, error) {
res, err := luaPTTL.Run(ctx, l.client.client, []string{l.key}, l.value).Result()
res, err := luaPTTL.Run(ctx, l.client, []string{l.key}, l.value).Result()
if err == redis.Nil {
return 0, nil
} else if err != nil {
Expand All @@ -160,7 +160,7 @@ func (l *Lock) TTL(ctx context.Context) (time.Duration, error) {
// May return ErrNotObtained if refresh is unsuccessful.
func (l *Lock) Refresh(ctx context.Context, ttl time.Duration, opt *Options) error {
ttlVal := strconv.FormatInt(int64(ttl/time.Millisecond), 10)
status, err := luaRefresh.Run(ctx, l.client.client, []string{l.key}, l.value, ttlVal).Result()
status, err := luaRefresh.Run(ctx, l.client, []string{l.key}, l.value, ttlVal).Result()
if err != nil {
return err
} else if status == int64(1) {
Expand All @@ -172,7 +172,11 @@ func (l *Lock) Refresh(ctx context.Context, ttl time.Duration, opt *Options) err
// Release manually releases the lock.
// May return ErrLockNotHeld.
func (l *Lock) Release(ctx context.Context) error {
res, err := luaRelease.Run(ctx, l.client.client, []string{l.key}, l.value).Result()
if l == nil {
return ErrLockNotHeld
}

res, err := luaRelease.Run(ctx, l.client, []string{l.key}, l.value).Result()
if err == redis.Nil {
return ErrLockNotHeld
} else if err != nil {
Expand Down
20 changes: 20 additions & 0 deletions redislock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,26 @@ func TestLock_Release_not_own(t *testing.T) {
}
}

func TestLock_Release_not_held(t *testing.T) {
ctx := context.Background()
rc := redis.NewClient(redisOpts)
defer teardown(t, rc)

lock1 := quickObtain(t, rc, time.Hour)
defer lock1.Release(ctx)

lock2, err := Obtain(context.Background(), rc, lockKey, time.Minute, nil)
if exp, got := ErrNotObtained, err; !errors.Is(got, exp) {
t.Fatalf("expected %v, got %v", exp, got)
}
if exp, got := (*Lock)(nil), lock2; exp != got {
t.Fatalf("expected %v, got %v", exp, got)
}
if exp, got := ErrLockNotHeld, lock2.Release(ctx); !errors.Is(got, exp) {
t.Fatalf("expected %v, got %v", exp, got)
}
}

func quickObtain(t *testing.T, rc *redis.Client, ttl time.Duration) *Lock {
t.Helper()

Expand Down

0 comments on commit 9de9468

Please sign in to comment.