bouncer/internal/lock/redis/locker.go

68 lines
1.4 KiB
Go
Raw Normal View History

2024-03-27 17:47:39 +01:00
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
2024-03-27 17:47:39 +01:00
}
// 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)
}
2024-03-27 17:47:39 +01:00
logger.Debug(ctx, "lock obtained")
2024-03-27 17:47:39 +01:00
defer func() {
if err := lock.Release(ctx); err != nil {
logger.Error(ctx, "could not release lock", logger.CapturedE(errors.WithStack(err)))
}
logger.Debug(ctx, "lock released")
}()
2024-03-27 17:47:39 +01:00
if err := fn(ctx); err != nil {
return errors.WithStack(err)
}
2024-03-27 17:47:39 +01:00
return nil
})
if err != nil {
2024-03-27 17:47:39 +01:00
return errors.WithStack(err)
}
return nil
}
func NewLocker(client redis.UniversalClient, maxRetries int) *Locker {
2024-03-27 17:47:39 +01:00
return &Locker{
client: client,
maxRetries: maxRetries,
2024-03-27 17:47:39 +01:00
}
}
var _ lock.Locker = &Locker{}