Skip to content

Confusion around returned error when lock not obtained due to timeout #79

@pranay1208

Description

@pranay1208

Description

Per the example in the official documentation (see: https://pkg.go.dev/github.com/bsm/redislock@v0.9.4#example-Client.Obtain-CustomDeadline), if the Obtain function fails to obtain lock, it will return redislock.ErrNotObtained.

For the following code, lets assume the lock is already obtained and not released.

locker := redislock.New(client)

// Retry every 500ms, for up-to a minute
backoff := redislock.LinearBackoff(500 * time.Millisecond)
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute))
defer cancel()

// Obtain lock with retry + custom deadline
lock, err := locker.Obtain(ctx, "my-key", time.Second, &redislock.Options{
	RetryStrategy: backoff,
})
if err == redislock.ErrNotObtained {
	fmt.Println("Could not obtain lock!")
} else if err != nil {
	log.Fatalln(err)
}

Expectation: console will print Could not obtain lock

Observed behaviour: console will print context deadline exceeded and halt function execution (due to log.Fatalln).

Why this occurs

Seems like this change was brought about in #63 and corresponding PR #64

select {
case <-ctx.Done():
	return nil, ctx.Err()
case <-ticker.C:
}

Suggestion

Replace return nil, ctx.Err() with return nil, errors.Join(ctx.Err(), ErrNotObtained)

This way, if someone does not care why the lock could not be obtained, then they can still check against the ErrNotObtained error.

// assume cannot acquire lock because context deadline is exceeded
lock, err := locker.Obtain(ctx, "key", time.Second, &redislock.Options{RetryStrategy: linearBackoff})

// this condition will be true
if errors.Is(err, redislock.ErrNotObtained) {
    fmt.Println("lock not obtained")
}

// this condition will also be true
if errors.Is(err, context.DeadlineExceeded) {
    fmt.Println("context deadline exceeded")
}

I'd be happy to make an MR to contribute this change if you think this is something valuable. Looking forward to your response, thanks

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions