68 lines
1.4 KiB
Go
68 lines
1.4 KiB
Go
package redis
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"forge.cadoles.com/cadoles/bouncer/internal/lock"
|
|
"github.com/bsm/redislock"
|
|
"github.com/pkg/errors"
|
|
"github.com/redis/go-redis/v9"
|
|
"gitlab.com/wpetit/goweb/logger"
|
|
)
|
|
|
|
type Locker struct {
|
|
client redis.UniversalClient
|
|
maxRetries int
|
|
}
|
|
|
|
// WithLock implements lock.Locker.
|
|
func (l *Locker) WithLock(ctx context.Context, key string, timeout time.Duration, fn func(ctx context.Context) error) error {
|
|
locker := redislock.New(l.client)
|
|
|
|
backoff := redislock.ExponentialBackoff(time.Second, timeout*2)
|
|
|
|
ctx = logger.With(ctx, logger.F("lockTimeout", timeout), logger.F("lockKey", key))
|
|
|
|
logger.Debug(ctx, "acquiring lock")
|
|
|
|
err := retryWithBackoff(ctx, l.maxRetries, func(ctx context.Context) error {
|
|
lock, err := locker.Obtain(ctx, key, timeout, &redislock.Options{
|
|
RetryStrategy: backoff,
|
|
})
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
logger.Debug(ctx, "lock obtained")
|
|
|
|
defer func() {
|
|
if err := lock.Release(ctx); err != nil {
|
|
logger.Error(ctx, "could not release lock", logger.E(errors.WithStack(err)))
|
|
}
|
|
|
|
logger.Debug(ctx, "lock released")
|
|
}()
|
|
|
|
if err := fn(ctx); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func NewLocker(client redis.UniversalClient, maxRetries int) *Locker {
|
|
return &Locker{
|
|
client: client,
|
|
maxRetries: maxRetries,
|
|
}
|
|
}
|
|
|
|
var _ lock.Locker = &Locker{}
|