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 }