WaitLock tries to acquire the lock within a specified waiting period. It will attempt to acquire the lock with exponential backoff if the lock is held by another process. Parameters: - ctx: The context for managing the wait request lifecycle. - lockTimeout: The TTL to set for the lock when acquired.
(ctx context.Context, lockTimeout, waitTimeout time.Duration)
| 145 | // - waitTimeout: The maximum time to wait for the lock to become available. |
| 146 | // Returns an error if the lock could not be acquired within the wait timeout. |
| 147 | func (l *Locker) WaitLock(ctx context.Context, lockTimeout, waitTimeout time.Duration) error { |
| 148 | if err := ctx.Err(); err != nil { |
| 149 | return fmt.Errorf("lock wait cancelled for key %s: %w", l.key, err) |
| 150 | } |
| 151 | |
| 152 | deadline := time.Now().Add(waitTimeout) |
| 153 | for time.Now().Before(deadline) { |
| 154 | err := l.Lock(ctx, lockTimeout) |
| 155 | if err == nil { |
| 156 | return nil |
| 157 | } |
| 158 | if !errors.Is(err, ErrLockHeld) { |
| 159 | if ctxErr := ctx.Err(); ctxErr != nil { |
| 160 | return fmt.Errorf("lock wait cancelled for key %s: %w", l.key, ctxErr) |
| 161 | } |
| 162 | return err |
| 163 | } |
| 164 | if err := sleepWithJitter(ctx, deadline); err != nil { |
| 165 | return fmt.Errorf("lock wait cancelled for key %s: %w", l.key, err) |
| 166 | } |
| 167 | } |
| 168 | return &lockError{ |
| 169 | err: ErrLockWaitTimeout, |
| 170 | msg: fmt.Sprintf("failed to acquire lock for key %s within the wait timeout", l.key), |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | // NewMultiLocker creates a new MultiLocker instance that manages locks for multiple keys. |
| 175 | // Keys are deduplicated and sorted lexicographically to ensure consistent lock ordering |