bsm / redislock

Simplified distributed locking implementation using Redis
Other
1.45k stars 157 forks source link

the Obtain may be have a problem #28

Closed jwrookie closed 3 years ago

jwrookie commented 3 years ago
// Obtain tries to obtain a new lock using a key with the given TTL.
// May return ErrNotObtained if not successful.
func (c *Client) Obtain(ctx context.Context, key string, ttl time.Duration, opt *Options) (*Lock, error) {
    // Create a random token
    token, err := c.randomToken()
    if err != nil {
        return nil, err
    }

    value := token + opt.getMetadata()
    retry := opt.getRetryStrategy()

        // todo:  Having only one context may invalidate a retry
    deadlinectx, cancel := context.WithDeadline(ctx, time.Now().Add(ttl))
    defer cancel()

    var timer *time.Timer
    for {
        ok, err := c.obtain(deadlinectx, key, value, ttl)
        if err != nil {
            return nil, err
        } else if ok {
            return &Lock{client: c, key: key, value: value}, nil
        }

        backoff := retry.NextBackoff()
        if backoff < 1 {
            return nil, ErrNotObtained
        }

        if timer == nil {
            timer = time.NewTimer(backoff)
            defer timer.Stop()
        } else {
            timer.Reset(backoff)
        }

        select {
                //  todo : the deadtime should be multiplied by retry max
        case <-deadlinectx.Done():
            return nil, ErrNotObtained
        case <-timer.C:
        }
    }
}
dim commented 3 years ago

No, that's intentional. The if retry is configured, you want to continue retrying for up-to ttl. If you want to abort earlier than that, you can pass your own custom context.WithDeadline to Obtain. The other option is to NextBackoff return 0 which will instantly cancel retries.