effect-distributed-lock 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -66
- package/examples/index.ts +49 -21
- package/package.json +2 -2
- package/redis-semaphore/.codeclimate.yml +5 -0
- package/redis-semaphore/.fossa.yml +14 -0
- package/redis-semaphore/.github/dependabot.yml +6 -0
- package/redis-semaphore/.github/workflows/branches.yml +39 -0
- package/redis-semaphore/.github/workflows/pull-requests.yml +35 -0
- package/redis-semaphore/.mocharc.yaml +6 -0
- package/redis-semaphore/.prettierrc +6 -0
- package/redis-semaphore/.snyk +4 -0
- package/redis-semaphore/.yarnrc.yml +2 -0
- package/redis-semaphore/CHANGELOG.md +70 -0
- package/redis-semaphore/Dockerfile +5 -0
- package/redis-semaphore/LICENSE +21 -0
- package/redis-semaphore/README.md +445 -0
- package/redis-semaphore/docker-compose.yml +31 -0
- package/redis-semaphore/eslint.config.mjs +73 -0
- package/redis-semaphore/package.json +79 -0
- package/redis-semaphore/setup-redis-servers.sh +2 -0
- package/redis-semaphore/src/Lock.ts +172 -0
- package/redis-semaphore/src/RedisMultiSemaphore.ts +56 -0
- package/redis-semaphore/src/RedisMutex.ts +45 -0
- package/redis-semaphore/src/RedisSemaphore.ts +49 -0
- package/redis-semaphore/src/RedlockMultiSemaphore.ts +56 -0
- package/redis-semaphore/src/RedlockMutex.ts +52 -0
- package/redis-semaphore/src/RedlockSemaphore.ts +49 -0
- package/redis-semaphore/src/errors/LostLockError.ts +1 -0
- package/redis-semaphore/src/errors/TimeoutError.ts +1 -0
- package/redis-semaphore/src/index.ts +23 -0
- package/redis-semaphore/src/misc.ts +12 -0
- package/redis-semaphore/src/multiSemaphore/acquire/index.ts +53 -0
- package/redis-semaphore/src/multiSemaphore/acquire/lua.ts +31 -0
- package/redis-semaphore/src/multiSemaphore/refresh/index.ts +32 -0
- package/redis-semaphore/src/multiSemaphore/refresh/lua.ts +31 -0
- package/redis-semaphore/src/multiSemaphore/release/index.ts +22 -0
- package/redis-semaphore/src/multiSemaphore/release/lua.ts +17 -0
- package/redis-semaphore/src/mutex/acquire.ts +42 -0
- package/redis-semaphore/src/mutex/refresh.ts +37 -0
- package/redis-semaphore/src/mutex/release.ts +30 -0
- package/redis-semaphore/src/redlockMultiSemaphore/acquire.ts +56 -0
- package/redis-semaphore/src/redlockMultiSemaphore/refresh.ts +68 -0
- package/redis-semaphore/src/redlockMultiSemaphore/release.ts +19 -0
- package/redis-semaphore/src/redlockMutex/acquire.ts +54 -0
- package/redis-semaphore/src/redlockMutex/refresh.ts +53 -0
- package/redis-semaphore/src/redlockMutex/release.ts +19 -0
- package/redis-semaphore/src/redlockSemaphore/acquire.ts +55 -0
- package/redis-semaphore/src/redlockSemaphore/refresh.ts +60 -0
- package/redis-semaphore/src/redlockSemaphore/release.ts +18 -0
- package/redis-semaphore/src/semaphore/acquire/index.ts +52 -0
- package/redis-semaphore/src/semaphore/acquire/lua.ts +25 -0
- package/redis-semaphore/src/semaphore/refresh/index.ts +31 -0
- package/redis-semaphore/src/semaphore/refresh/lua.ts +25 -0
- package/redis-semaphore/src/semaphore/release.ts +14 -0
- package/redis-semaphore/src/types.ts +63 -0
- package/redis-semaphore/src/utils/createEval.ts +45 -0
- package/redis-semaphore/src/utils/index.ts +13 -0
- package/redis-semaphore/src/utils/redlock.ts +7 -0
- package/redis-semaphore/test/init.test.ts +9 -0
- package/redis-semaphore/test/redisClient.ts +82 -0
- package/redis-semaphore/test/setup.ts +6 -0
- package/redis-semaphore/test/shell.test.ts +15 -0
- package/redis-semaphore/test/shell.ts +48 -0
- package/redis-semaphore/test/src/Lock.test.ts +37 -0
- package/redis-semaphore/test/src/RedisMultiSemaphore.test.ts +425 -0
- package/redis-semaphore/test/src/RedisMutex.test.ts +334 -0
- package/redis-semaphore/test/src/RedisSemaphore.test.ts +367 -0
- package/redis-semaphore/test/src/RedlockMultiSemaphore.test.ts +671 -0
- package/redis-semaphore/test/src/RedlockMutex.test.ts +328 -0
- package/redis-semaphore/test/src/RedlockSemaphore.test.ts +579 -0
- package/redis-semaphore/test/src/index.test.ts +22 -0
- package/redis-semaphore/test/src/multiSemaphore/acquire/index.test.ts +51 -0
- package/redis-semaphore/test/src/multiSemaphore/acquire/internal.test.ts +67 -0
- package/redis-semaphore/test/src/multiSemaphore/refresh/index.test.ts +52 -0
- package/redis-semaphore/test/src/multiSemaphore/release/index.test.ts +18 -0
- package/redis-semaphore/test/src/mutex/acquire.test.ts +78 -0
- package/redis-semaphore/test/src/mutex/refresh.test.ts +22 -0
- package/redis-semaphore/test/src/mutex/release.test.ts +17 -0
- package/redis-semaphore/test/src/redlockMutex/acquire.test.ts +90 -0
- package/redis-semaphore/test/src/redlockMutex/refresh.test.ts +27 -0
- package/redis-semaphore/test/src/redlockMutex/release.test.ts +17 -0
- package/redis-semaphore/test/src/semaphore/acquire/index.test.ts +49 -0
- package/redis-semaphore/test/src/semaphore/acquire/internal.test.ts +65 -0
- package/redis-semaphore/test/src/semaphore/refresh/index.test.ts +44 -0
- package/redis-semaphore/test/src/semaphore/release.test.ts +18 -0
- package/redis-semaphore/test/src/utils/eval.test.ts +22 -0
- package/redis-semaphore/test/src/utils/index.test.ts +19 -0
- package/redis-semaphore/test/src/utils/redlock.test.ts +31 -0
- package/redis-semaphore/test/unhandledRejection.ts +28 -0
- package/redis-semaphore/tsconfig.build-commonjs.json +9 -0
- package/redis-semaphore/tsconfig.build-es.json +9 -0
- package/redis-semaphore/tsconfig.json +11 -0
- package/redis-semaphore/yarn.lock +5338 -0
- package/src/Backing.ts +87 -0
- package/src/DistributedSemaphore.ts +384 -0
- package/src/Errors.ts +3 -39
- package/src/RedisBacking.ts +167 -165
- package/src/index.ts +28 -17
- package/src/DistributedMutex.ts +0 -304
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { expect } from 'chai'
|
|
2
|
+
import { Redis } from 'ioredis'
|
|
3
|
+
import sinon from 'sinon'
|
|
4
|
+
import LostLockError from '../../src/errors/LostLockError'
|
|
5
|
+
import RedlockMutex from '../../src/RedlockMutex'
|
|
6
|
+
import { TimeoutOptions } from '../../src/types'
|
|
7
|
+
import { delay } from '../../src/utils/index'
|
|
8
|
+
import {
|
|
9
|
+
allClientMocks,
|
|
10
|
+
allClients,
|
|
11
|
+
client1,
|
|
12
|
+
client2,
|
|
13
|
+
client3
|
|
14
|
+
} from '../redisClient'
|
|
15
|
+
import { downRedisServer, upRedisServer } from '../shell'
|
|
16
|
+
import {
|
|
17
|
+
catchUnhandledRejection,
|
|
18
|
+
throwUnhandledRejection,
|
|
19
|
+
unhandledRejectionSpy
|
|
20
|
+
} from '../unhandledRejection'
|
|
21
|
+
|
|
22
|
+
const timeoutOptions: TimeoutOptions = {
|
|
23
|
+
lockTimeout: 300,
|
|
24
|
+
acquireTimeout: 100,
|
|
25
|
+
refreshInterval: 80,
|
|
26
|
+
retryInterval: 10
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function expectGetAll(
|
|
30
|
+
key: string,
|
|
31
|
+
value: string | null,
|
|
32
|
+
clients = allClients
|
|
33
|
+
) {
|
|
34
|
+
await expect(
|
|
35
|
+
Promise.all([clients[0].get(key), clients[1].get(key), clients[2].get(key)])
|
|
36
|
+
).to.become([value, value, value])
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('RedlockMutex', () => {
|
|
40
|
+
it('should fail on invalid arguments', () => {
|
|
41
|
+
expect(() => new RedlockMutex(null as unknown as Redis[], 'key')).to.throw(
|
|
42
|
+
'"clients" array is required'
|
|
43
|
+
)
|
|
44
|
+
expect(() => new RedlockMutex(allClients, '')).to.throw('"key" is required')
|
|
45
|
+
expect(() => new RedlockMutex(allClients, 1 as unknown as string)).to.throw(
|
|
46
|
+
'"key" must be a string'
|
|
47
|
+
)
|
|
48
|
+
})
|
|
49
|
+
it('should acquire and release lock', async () => {
|
|
50
|
+
const mutex = new RedlockMutex(allClients, 'key')
|
|
51
|
+
expect(mutex.isAcquired).to.be.false
|
|
52
|
+
|
|
53
|
+
await mutex.acquire()
|
|
54
|
+
expect(mutex.isAcquired).to.be.true
|
|
55
|
+
await expectGetAll('mutex:key', mutex.identifier)
|
|
56
|
+
|
|
57
|
+
await mutex.release()
|
|
58
|
+
expect(mutex.isAcquired).to.be.false
|
|
59
|
+
await expectGetAll('mutex:key', null)
|
|
60
|
+
})
|
|
61
|
+
it('should reject after timeout', async () => {
|
|
62
|
+
const mutex1 = new RedlockMutex(allClients, 'key', timeoutOptions)
|
|
63
|
+
const mutex2 = new RedlockMutex(allClients, 'key', timeoutOptions)
|
|
64
|
+
await mutex1.acquire()
|
|
65
|
+
await expect(mutex2.acquire()).to.be.rejectedWith(
|
|
66
|
+
'Acquire redlock-mutex mutex:key timeout'
|
|
67
|
+
)
|
|
68
|
+
await mutex1.release()
|
|
69
|
+
await expectGetAll('mutex:key', null)
|
|
70
|
+
})
|
|
71
|
+
it('should refresh lock every refreshInterval ms until release', async () => {
|
|
72
|
+
const mutex = new RedlockMutex(allClients, 'key', timeoutOptions)
|
|
73
|
+
await mutex.acquire()
|
|
74
|
+
await delay(400)
|
|
75
|
+
await expectGetAll('mutex:key', mutex.identifier)
|
|
76
|
+
await mutex.release()
|
|
77
|
+
await expectGetAll('mutex:key', null)
|
|
78
|
+
})
|
|
79
|
+
it('should stop refreshing if stopped', async () => {
|
|
80
|
+
const mutex = new RedlockMutex(allClients, 'key', timeoutOptions)
|
|
81
|
+
await mutex.acquire()
|
|
82
|
+
mutex.stopRefresh()
|
|
83
|
+
await delay(400)
|
|
84
|
+
await expectGetAll('mutex:key', null)
|
|
85
|
+
})
|
|
86
|
+
it('should support externally acquired mutex (deprecated interface)', async () => {
|
|
87
|
+
const externalMutex = new RedlockMutex(allClients, 'key', {
|
|
88
|
+
...timeoutOptions,
|
|
89
|
+
refreshInterval: 0
|
|
90
|
+
})
|
|
91
|
+
const localMutex = new RedlockMutex(allClients, 'key', {
|
|
92
|
+
...timeoutOptions,
|
|
93
|
+
externallyAcquiredIdentifier: externalMutex.identifier
|
|
94
|
+
})
|
|
95
|
+
await externalMutex.acquire()
|
|
96
|
+
await localMutex.acquire()
|
|
97
|
+
await delay(400)
|
|
98
|
+
await expectGetAll('mutex:key', localMutex.identifier)
|
|
99
|
+
await localMutex.release()
|
|
100
|
+
await expectGetAll('mutex:key', null)
|
|
101
|
+
})
|
|
102
|
+
it('should support externally acquired mutex', async () => {
|
|
103
|
+
const externalMutex = new RedlockMutex(allClients, 'key', {
|
|
104
|
+
...timeoutOptions,
|
|
105
|
+
refreshInterval: 0
|
|
106
|
+
})
|
|
107
|
+
const localMutex = new RedlockMutex(allClients, 'key', {
|
|
108
|
+
...timeoutOptions,
|
|
109
|
+
identifier: externalMutex.identifier,
|
|
110
|
+
acquiredExternally: true
|
|
111
|
+
})
|
|
112
|
+
await externalMutex.acquire()
|
|
113
|
+
await localMutex.acquire()
|
|
114
|
+
await delay(400)
|
|
115
|
+
await expectGetAll('mutex:key', localMutex.identifier)
|
|
116
|
+
await localMutex.release()
|
|
117
|
+
await expectGetAll('mutex:key', null)
|
|
118
|
+
})
|
|
119
|
+
describe('lost lock case', () => {
|
|
120
|
+
beforeEach(() => {
|
|
121
|
+
catchUnhandledRejection()
|
|
122
|
+
})
|
|
123
|
+
afterEach(() => {
|
|
124
|
+
throwUnhandledRejection()
|
|
125
|
+
})
|
|
126
|
+
it('should throw unhandled error if lock is lost between refreshes', async () => {
|
|
127
|
+
const mutex = new RedlockMutex(allClients, 'key', timeoutOptions)
|
|
128
|
+
await mutex.acquire()
|
|
129
|
+
await Promise.all([
|
|
130
|
+
client1.set('mutex:key', '222'), // another instance
|
|
131
|
+
client2.set('mutex:key', '222'), // another instance
|
|
132
|
+
client3.set('mutex:key', '222') // another instance
|
|
133
|
+
])
|
|
134
|
+
await delay(200)
|
|
135
|
+
expect(unhandledRejectionSpy).to.be.called
|
|
136
|
+
expect(unhandledRejectionSpy.firstCall.firstArg instanceof LostLockError)
|
|
137
|
+
.to.be.true
|
|
138
|
+
})
|
|
139
|
+
it('should call onLockLost callback if provided', async () => {
|
|
140
|
+
const onLockLostCallback = sinon.spy(function (this: RedlockMutex) {
|
|
141
|
+
expect(this.isAcquired).to.be.false
|
|
142
|
+
})
|
|
143
|
+
const mutex = new RedlockMutex(allClients, 'key', {
|
|
144
|
+
...timeoutOptions,
|
|
145
|
+
onLockLost: onLockLostCallback
|
|
146
|
+
})
|
|
147
|
+
await mutex.acquire()
|
|
148
|
+
expect(mutex.isAcquired).to.be.true
|
|
149
|
+
await Promise.all([
|
|
150
|
+
client1.set('mutex:key', '222'), // another instance
|
|
151
|
+
client2.set('mutex:key', '222'), // another instance
|
|
152
|
+
client3.set('mutex:key', '222') // another instance
|
|
153
|
+
])
|
|
154
|
+
await delay(200)
|
|
155
|
+
expect(mutex.isAcquired).to.be.false
|
|
156
|
+
expect(unhandledRejectionSpy).to.not.called
|
|
157
|
+
expect(onLockLostCallback).to.be.called
|
|
158
|
+
expect(onLockLostCallback.firstCall.firstArg instanceof LostLockError).to
|
|
159
|
+
.be.true
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
it('should be reusable', async () => {
|
|
163
|
+
const mutex = new RedlockMutex(allClients, 'key', timeoutOptions)
|
|
164
|
+
|
|
165
|
+
/* Lifecycle 1 */
|
|
166
|
+
await mutex.acquire()
|
|
167
|
+
await delay(100)
|
|
168
|
+
await expectGetAll('mutex:key', mutex.identifier)
|
|
169
|
+
await mutex.release()
|
|
170
|
+
await expectGetAll('mutex:key', null)
|
|
171
|
+
await delay(100)
|
|
172
|
+
await expectGetAll('mutex:key', null)
|
|
173
|
+
|
|
174
|
+
await delay(100)
|
|
175
|
+
|
|
176
|
+
/* Lifecycle 2 */
|
|
177
|
+
await mutex.acquire()
|
|
178
|
+
await delay(100)
|
|
179
|
+
await expectGetAll('mutex:key', mutex.identifier)
|
|
180
|
+
await mutex.release()
|
|
181
|
+
await expectGetAll('mutex:key', null)
|
|
182
|
+
await delay(100)
|
|
183
|
+
await expectGetAll('mutex:key', null)
|
|
184
|
+
|
|
185
|
+
await delay(100)
|
|
186
|
+
|
|
187
|
+
/* Lifecycle 3 */
|
|
188
|
+
await mutex.acquire()
|
|
189
|
+
await delay(100)
|
|
190
|
+
await expectGetAll('mutex:key', mutex.identifier)
|
|
191
|
+
await mutex.release()
|
|
192
|
+
await expectGetAll('mutex:key', null)
|
|
193
|
+
await delay(100)
|
|
194
|
+
await expectGetAll('mutex:key', null)
|
|
195
|
+
})
|
|
196
|
+
describe('[Node shutdown]', () => {
|
|
197
|
+
afterEach(async () => {
|
|
198
|
+
await Promise.all([upRedisServer(1), upRedisServer(2), upRedisServer(3)])
|
|
199
|
+
})
|
|
200
|
+
it('should handle server shutdown if quorum is alive', async function () {
|
|
201
|
+
this.timeout(60000)
|
|
202
|
+
const mutex1 = new RedlockMutex(allClients, 'key', timeoutOptions)
|
|
203
|
+
await mutex1.acquire()
|
|
204
|
+
|
|
205
|
+
// <Server1Failure>
|
|
206
|
+
await downRedisServer(1)
|
|
207
|
+
console.log('SHUT DOWN 1')
|
|
208
|
+
|
|
209
|
+
await delay(1000)
|
|
210
|
+
|
|
211
|
+
// lock survive in server2 and server3
|
|
212
|
+
// mutex2 will NOT be able to acquire the lock
|
|
213
|
+
|
|
214
|
+
const mutex2 = new RedlockMutex(allClients, 'key', timeoutOptions)
|
|
215
|
+
await expect(mutex2.acquire()).to.be.rejectedWith(
|
|
216
|
+
'Acquire redlock-mutex mutex:key timeout'
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
// key in server1 has expired now
|
|
220
|
+
|
|
221
|
+
await upRedisServer(1)
|
|
222
|
+
console.log('ONLINE 1')
|
|
223
|
+
|
|
224
|
+
// let mutex1 to refresh lock on server1
|
|
225
|
+
await delay(1000)
|
|
226
|
+
expect(await client1.get('mutex:key')).to.be.eql(mutex1.identifier)
|
|
227
|
+
// </Server1Failure>
|
|
228
|
+
|
|
229
|
+
// <Server2Failure>
|
|
230
|
+
await downRedisServer(2)
|
|
231
|
+
console.log('SHUT DOWN 2')
|
|
232
|
+
|
|
233
|
+
await delay(1000)
|
|
234
|
+
|
|
235
|
+
// lock survive in server1 and server3
|
|
236
|
+
// mutex3 will NOT be able to acquire the lock
|
|
237
|
+
|
|
238
|
+
const mutex3 = new RedlockMutex(allClients, 'key', timeoutOptions)
|
|
239
|
+
await expect(mutex3.acquire()).to.be.rejectedWith(
|
|
240
|
+
'Acquire redlock-mutex mutex:key timeout'
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
// key in server2 has expired now
|
|
244
|
+
|
|
245
|
+
await upRedisServer(2)
|
|
246
|
+
console.log('ONLINE 2')
|
|
247
|
+
|
|
248
|
+
// let mutex1 to refresh lock on server2
|
|
249
|
+
await delay(1000)
|
|
250
|
+
expect(await client2.get('mutex:key')).to.be.eql(mutex1.identifier)
|
|
251
|
+
// </Server2Failure>
|
|
252
|
+
|
|
253
|
+
// <Server3Failure>
|
|
254
|
+
await downRedisServer(3)
|
|
255
|
+
console.log('SHUT DOWN 3')
|
|
256
|
+
|
|
257
|
+
await delay(1000)
|
|
258
|
+
|
|
259
|
+
// lock survive in server1 and server2
|
|
260
|
+
// mutex4 will NOT be able to acquire the lock
|
|
261
|
+
|
|
262
|
+
const mutex4 = new RedlockMutex(allClients, 'key', timeoutOptions)
|
|
263
|
+
await expect(mutex4.acquire()).to.be.rejectedWith(
|
|
264
|
+
'Acquire redlock-mutex mutex:key timeout'
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
// key in server3 has expired now
|
|
268
|
+
|
|
269
|
+
await upRedisServer(3)
|
|
270
|
+
console.log('ONLINE 3')
|
|
271
|
+
|
|
272
|
+
// let mutex1 to refresh lock on server3
|
|
273
|
+
await delay(1000)
|
|
274
|
+
expect(await client3.get('mutex:key')).to.be.eql(mutex1.identifier)
|
|
275
|
+
// </Server3Failure>
|
|
276
|
+
|
|
277
|
+
await mutex1.release()
|
|
278
|
+
})
|
|
279
|
+
it('should fail and release when quorum is become dead', async function () {
|
|
280
|
+
this.timeout(60000)
|
|
281
|
+
const onLockLostCallback = sinon.spy(function (this: RedlockMutex) {
|
|
282
|
+
expect(this.isAcquired).to.be.false
|
|
283
|
+
})
|
|
284
|
+
const mutex1 = new RedlockMutex(allClients, 'key', {
|
|
285
|
+
...timeoutOptions,
|
|
286
|
+
onLockLost: onLockLostCallback
|
|
287
|
+
})
|
|
288
|
+
await mutex1.acquire()
|
|
289
|
+
|
|
290
|
+
await downRedisServer(1)
|
|
291
|
+
console.log('SHUT DOWN 1')
|
|
292
|
+
|
|
293
|
+
await downRedisServer(2)
|
|
294
|
+
console.log('SHUT DOWN 2')
|
|
295
|
+
|
|
296
|
+
await delay(1000)
|
|
297
|
+
|
|
298
|
+
expect(onLockLostCallback).to.be.called
|
|
299
|
+
expect(onLockLostCallback.firstCall.firstArg instanceof LostLockError).to
|
|
300
|
+
.be.true
|
|
301
|
+
|
|
302
|
+
// released lock on server3
|
|
303
|
+
expect(await client3.get('mutex:key')).to.be.eql(null)
|
|
304
|
+
|
|
305
|
+
// mutex2 will NOT be able to acquire the lock cause quorum is dead
|
|
306
|
+
|
|
307
|
+
const mutex2 = new RedlockMutex(allClients, 'key', timeoutOptions)
|
|
308
|
+
await expect(mutex2.acquire()).to.be.rejectedWith(
|
|
309
|
+
'Acquire redlock-mutex mutex:key timeout'
|
|
310
|
+
)
|
|
311
|
+
})
|
|
312
|
+
})
|
|
313
|
+
describe('ioredis-mock support', () => {
|
|
314
|
+
it('should acquire and release lock', async () => {
|
|
315
|
+
const mutex = new RedlockMutex(allClientMocks, 'key')
|
|
316
|
+
expect(mutex.isAcquired).to.be.false
|
|
317
|
+
|
|
318
|
+
await mutex.acquire()
|
|
319
|
+
console.log('acquired!')
|
|
320
|
+
expect(mutex.isAcquired).to.be.true
|
|
321
|
+
await expectGetAll('mutex:key', mutex.identifier, allClientMocks)
|
|
322
|
+
|
|
323
|
+
await mutex.release()
|
|
324
|
+
expect(mutex.isAcquired).to.be.false
|
|
325
|
+
await expectGetAll('mutex:key', null, allClientMocks)
|
|
326
|
+
})
|
|
327
|
+
})
|
|
328
|
+
})
|