60 lines
1.2 KiB
Go
60 lines
1.2 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
|
||
|
timeout time.Duration
|
||
|
}
|
||
|
|
||
|
// 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")
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
func NewLocker(client redis.UniversalClient) *Locker {
|
||
|
return &Locker{
|
||
|
client: client,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var _ lock.Locker = &Locker{}
|