github.com/teng231/glock@v1.1.11/optimistic_lock.go (about)

     1  package glock
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"time"
     7  
     8  	"github.com/go-redis/redis/v8"
     9  )
    10  
    11  type IOptimisticLock interface {
    12  	Lock(key string, otherDuration ...time.Duration) error
    13  	Unlock(key string) error
    14  }
    15  
    16  type OptimisticLock struct {
    17  	client   *redis.Client
    18  	prefix   string
    19  	timelock time.Duration
    20  }
    21  
    22  func StartOptimisticLock(cf *ConnectConfig) (*OptimisticLock, error) {
    23  	client := redis.NewClient(&redis.Options{
    24  		Addr:            cf.RedisAddr,
    25  		Password:        cf.RedisPw,
    26  		MaxRetries:      10,
    27  		MinRetryBackoff: 15 * time.Millisecond,
    28  		MaxRetryBackoff: 1000 * time.Millisecond,
    29  		DialTimeout:     10 * time.Second,
    30  		Username:        cf.RedisUsername,
    31  		DB:              cf.RedisDb, // use default DB
    32  	})
    33  	if cf.Timelock < 0 {
    34  		return nil, errors.New("timelock is required")
    35  	}
    36  	ctx, cancel := context.WithTimeout(context.Background(), cf.Timelock)
    37  	defer cancel()
    38  	if err := client.Ping(ctx).Err(); err != nil {
    39  		return nil, err
    40  	}
    41  	return &OptimisticLock{
    42  		client,
    43  		cf.Prefix,
    44  		cf.Timelock,
    45  	}, nil
    46  }
    47  
    48  // CreateOptimisticLock deprecated
    49  func CreateOptimisticLock(addr, pw, prefix string, timelock time.Duration) (*OptimisticLock, error) {
    50  	client := redis.NewClient(&redis.Options{
    51  		Addr:            addr,
    52  		Password:        pw,
    53  		MaxRetries:      10,
    54  		MinRetryBackoff: 15 * time.Millisecond,
    55  		MaxRetryBackoff: 1000 * time.Millisecond,
    56  		DialTimeout:     10 * time.Second,
    57  		DB:              1, // use default DB
    58  	})
    59  	if timelock < 0 {
    60  		return nil, errors.New("timelock is required")
    61  	}
    62  	ctx, cancel := context.WithTimeout(context.Background(), timelock)
    63  	defer cancel()
    64  	if err := client.Ping(ctx).Err(); err != nil {
    65  		return nil, err
    66  	}
    67  	return &OptimisticLock{
    68  		client,
    69  		prefix,
    70  		timelock,
    71  	}, nil
    72  }
    73  
    74  func (ol *OptimisticLock) Lock(key string, otherDuration ...time.Duration) error {
    75  	duration := ol.timelock
    76  	if len(otherDuration) == 1 {
    77  		duration = otherDuration[0]
    78  	}
    79  	ctx, cancel := context.WithTimeout(context.Background(), duration)
    80  	defer cancel()
    81  	allowed, err := ol.client.SetNX(ctx, ol.prefix+key, "ok", duration).Result()
    82  	if err != nil {
    83  		return err
    84  	}
    85  	if !allowed {
    86  		return errors.New(StatusLocked)
    87  	}
    88  	return nil
    89  }
    90  
    91  func (ol *OptimisticLock) Unlock(key string) error {
    92  	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    93  	defer cancel()
    94  	err := ol.client.Del(ctx, ol.prefix+key).Err()
    95  	return err
    96  }