You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
constunlockScript=`local v = redis.call("get",KEYS[1]); if v==false then return -1 end; if v~=ARGV[1] then return -2 else return redis.call("del",KEYS[1]) end`
31
+
const (
32
+
unlockScript=`local v = redis.call("get",KEYS[1]); if v==false then return -1 end; if v~=ARGV[1] then return -2 else return redis.call("del",KEYS[1]) end`
33
+
renewLockScript=`local v = redis.call("get",KEYS[1]); if v==false then return -1 end; if v~=ARGV[1] then return -2 else return redis.call("expire",KEYS[1],ARGV[2]) end`
34
+
)
35
+
36
+
varErrComponentClosed=errors.New("component is closed")
30
37
31
38
// Standalone Redis lock store.
32
39
// Any fail-over related features are not supported, such as Sentinel and Redis Cluster.
33
40
typeStandaloneRedisLockstruct {
34
41
client rediscomponent.RedisClient
35
42
clientSettings*rediscomponent.Settings
36
43
37
-
logger logger.Logger
44
+
closed atomic.Bool
45
+
runnincChchanstruct{}
46
+
logger logger.Logger
38
47
}
39
48
40
49
// NewStandaloneRedisLock returns a new standalone redis lock.
41
-
// Do not use this lock with a redis cluster, which might lead to unexpected lock loss.
50
+
// Do not use this lock with a Redis cluster, which might lead to unexpected lock loss.
// We try to acquire a lock through periodic polling
105
+
// A potentially more efficient way would be to use keyspace notifications to subscribe to changes in the key we subscribe to
106
+
// However, keyspace notifications:
107
+
// 1. Are not enabled by default in Redis, and require an explicit configuration change, which adds quite a bit of complexity for the user: https://redis.io/docs/manual/keyspace-notifications/
108
+
// 2. When a connection to Redis calls SUBSCRIBE to watch for notifications, it cannot be used for anything else (unless we switch the protocol to RESP3, which must be explicitly chosen and only works with Redis 6+: https://redis.io/commands/hello/)
109
+
// So, periodic polling it is
110
+
111
+
// We use an exponential backoff here because it supports a randomization factor
112
+
bo:=backoff.NewExponentialBackOff()
113
+
bo.MaxElapsedTime=0
114
+
bo.InitialInterval=50*time.Millisecond
115
+
bo.MaxInterval=500*time.Millisecond
116
+
bo.RandomizationFactor=0.5
117
+
118
+
// Repat until we get the lock, or context is canceled
119
+
for {
120
+
// Try to acquire the lock
121
+
res, err=r.TryLock(ctx, req)
122
+
iferr!=nil {
123
+
// If we got an error, return right away
124
+
returnnil, err
125
+
}
126
+
127
+
// Let's see if we got the lock
128
+
ifres.Success {
129
+
returnres, nil
130
+
}
131
+
132
+
// Sleep till the next tick and try again
133
+
// Stop when context is done or component is closed
134
+
t:=time.NewTimer(bo.NextBackOff())
135
+
select {
136
+
case<-t.C:
137
+
// Nop, retry
138
+
case<-ctx.Done():
139
+
returnnil, ctx.Err()
140
+
case<-r.runnincCh:
141
+
returnnil, ErrComponentClosed
142
+
}
143
+
}
144
+
}
145
+
82
146
// TryLock tries to acquire a lock.
83
147
// If the lock cannot be acquired, it returns immediately.
0 commit comments