effect-distributed-lock 0.0.5 → 0.0.7

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.
Files changed (97) hide show
  1. package/README.md +44 -6
  2. package/examples/concurrent.ts +111 -0
  3. package/examples/{index.ts → kitchen-sink.ts} +3 -1
  4. package/package.json +1 -1
  5. package/src/Backing.ts +21 -17
  6. package/src/DistributedSemaphore.ts +71 -16
  7. package/src/RedisBacking.ts +97 -7
  8. package/redis-semaphore/.codeclimate.yml +0 -5
  9. package/redis-semaphore/.fossa.yml +0 -14
  10. package/redis-semaphore/.github/dependabot.yml +0 -6
  11. package/redis-semaphore/.github/workflows/branches.yml +0 -39
  12. package/redis-semaphore/.github/workflows/pull-requests.yml +0 -35
  13. package/redis-semaphore/.mocharc.yaml +0 -6
  14. package/redis-semaphore/.prettierrc +0 -6
  15. package/redis-semaphore/.snyk +0 -4
  16. package/redis-semaphore/.yarnrc.yml +0 -2
  17. package/redis-semaphore/CHANGELOG.md +0 -70
  18. package/redis-semaphore/Dockerfile +0 -5
  19. package/redis-semaphore/LICENSE +0 -21
  20. package/redis-semaphore/README.md +0 -445
  21. package/redis-semaphore/docker-compose.yml +0 -31
  22. package/redis-semaphore/eslint.config.mjs +0 -73
  23. package/redis-semaphore/package.json +0 -79
  24. package/redis-semaphore/setup-redis-servers.sh +0 -2
  25. package/redis-semaphore/src/Lock.ts +0 -172
  26. package/redis-semaphore/src/RedisMultiSemaphore.ts +0 -56
  27. package/redis-semaphore/src/RedisMutex.ts +0 -45
  28. package/redis-semaphore/src/RedisSemaphore.ts +0 -49
  29. package/redis-semaphore/src/RedlockMultiSemaphore.ts +0 -56
  30. package/redis-semaphore/src/RedlockMutex.ts +0 -52
  31. package/redis-semaphore/src/RedlockSemaphore.ts +0 -49
  32. package/redis-semaphore/src/errors/LostLockError.ts +0 -1
  33. package/redis-semaphore/src/errors/TimeoutError.ts +0 -1
  34. package/redis-semaphore/src/index.ts +0 -23
  35. package/redis-semaphore/src/misc.ts +0 -12
  36. package/redis-semaphore/src/multiSemaphore/acquire/index.ts +0 -53
  37. package/redis-semaphore/src/multiSemaphore/acquire/lua.ts +0 -31
  38. package/redis-semaphore/src/multiSemaphore/refresh/index.ts +0 -32
  39. package/redis-semaphore/src/multiSemaphore/refresh/lua.ts +0 -31
  40. package/redis-semaphore/src/multiSemaphore/release/index.ts +0 -22
  41. package/redis-semaphore/src/multiSemaphore/release/lua.ts +0 -17
  42. package/redis-semaphore/src/mutex/acquire.ts +0 -42
  43. package/redis-semaphore/src/mutex/refresh.ts +0 -37
  44. package/redis-semaphore/src/mutex/release.ts +0 -30
  45. package/redis-semaphore/src/redlockMultiSemaphore/acquire.ts +0 -56
  46. package/redis-semaphore/src/redlockMultiSemaphore/refresh.ts +0 -68
  47. package/redis-semaphore/src/redlockMultiSemaphore/release.ts +0 -19
  48. package/redis-semaphore/src/redlockMutex/acquire.ts +0 -54
  49. package/redis-semaphore/src/redlockMutex/refresh.ts +0 -53
  50. package/redis-semaphore/src/redlockMutex/release.ts +0 -19
  51. package/redis-semaphore/src/redlockSemaphore/acquire.ts +0 -55
  52. package/redis-semaphore/src/redlockSemaphore/refresh.ts +0 -60
  53. package/redis-semaphore/src/redlockSemaphore/release.ts +0 -18
  54. package/redis-semaphore/src/semaphore/acquire/index.ts +0 -52
  55. package/redis-semaphore/src/semaphore/acquire/lua.ts +0 -25
  56. package/redis-semaphore/src/semaphore/refresh/index.ts +0 -31
  57. package/redis-semaphore/src/semaphore/refresh/lua.ts +0 -25
  58. package/redis-semaphore/src/semaphore/release.ts +0 -14
  59. package/redis-semaphore/src/types.ts +0 -63
  60. package/redis-semaphore/src/utils/createEval.ts +0 -45
  61. package/redis-semaphore/src/utils/index.ts +0 -13
  62. package/redis-semaphore/src/utils/redlock.ts +0 -7
  63. package/redis-semaphore/test/init.test.ts +0 -9
  64. package/redis-semaphore/test/redisClient.ts +0 -82
  65. package/redis-semaphore/test/setup.ts +0 -6
  66. package/redis-semaphore/test/shell.test.ts +0 -15
  67. package/redis-semaphore/test/shell.ts +0 -48
  68. package/redis-semaphore/test/src/Lock.test.ts +0 -37
  69. package/redis-semaphore/test/src/RedisMultiSemaphore.test.ts +0 -425
  70. package/redis-semaphore/test/src/RedisMutex.test.ts +0 -334
  71. package/redis-semaphore/test/src/RedisSemaphore.test.ts +0 -367
  72. package/redis-semaphore/test/src/RedlockMultiSemaphore.test.ts +0 -671
  73. package/redis-semaphore/test/src/RedlockMutex.test.ts +0 -328
  74. package/redis-semaphore/test/src/RedlockSemaphore.test.ts +0 -579
  75. package/redis-semaphore/test/src/index.test.ts +0 -22
  76. package/redis-semaphore/test/src/multiSemaphore/acquire/index.test.ts +0 -51
  77. package/redis-semaphore/test/src/multiSemaphore/acquire/internal.test.ts +0 -67
  78. package/redis-semaphore/test/src/multiSemaphore/refresh/index.test.ts +0 -52
  79. package/redis-semaphore/test/src/multiSemaphore/release/index.test.ts +0 -18
  80. package/redis-semaphore/test/src/mutex/acquire.test.ts +0 -78
  81. package/redis-semaphore/test/src/mutex/refresh.test.ts +0 -22
  82. package/redis-semaphore/test/src/mutex/release.test.ts +0 -17
  83. package/redis-semaphore/test/src/redlockMutex/acquire.test.ts +0 -90
  84. package/redis-semaphore/test/src/redlockMutex/refresh.test.ts +0 -27
  85. package/redis-semaphore/test/src/redlockMutex/release.test.ts +0 -17
  86. package/redis-semaphore/test/src/semaphore/acquire/index.test.ts +0 -49
  87. package/redis-semaphore/test/src/semaphore/acquire/internal.test.ts +0 -65
  88. package/redis-semaphore/test/src/semaphore/refresh/index.test.ts +0 -44
  89. package/redis-semaphore/test/src/semaphore/release.test.ts +0 -18
  90. package/redis-semaphore/test/src/utils/eval.test.ts +0 -22
  91. package/redis-semaphore/test/src/utils/index.test.ts +0 -19
  92. package/redis-semaphore/test/src/utils/redlock.test.ts +0 -31
  93. package/redis-semaphore/test/unhandledRejection.ts +0 -28
  94. package/redis-semaphore/tsconfig.build-commonjs.json +0 -9
  95. package/redis-semaphore/tsconfig.build-es.json +0 -9
  96. package/redis-semaphore/tsconfig.json +0 -11
  97. package/redis-semaphore/yarn.lock +0 -5338
@@ -1,671 +0,0 @@
1
- import { expect } from 'chai'
2
- import { Redis } from 'ioredis'
3
- import sinon from 'sinon'
4
- import LostLockError from '../../src/errors/LostLockError'
5
- import RedlockMultiSemaphore from '../../src/RedlockMultiSemaphore'
6
- import RedlockSemaphore from '../../src/RedlockSemaphore'
7
- import { TimeoutOptions } from '../../src/types'
8
- import { delay } from '../../src/utils/index'
9
- import {
10
- allClientMocks,
11
- allClients,
12
- client1,
13
- client2,
14
- client3
15
- } from '../redisClient'
16
- import { downRedisServer, upRedisServer } from '../shell'
17
- import {
18
- catchUnhandledRejection,
19
- throwUnhandledRejection,
20
- unhandledRejectionSpy
21
- } from '../unhandledRejection'
22
-
23
- const timeoutOptions: TimeoutOptions = {
24
- lockTimeout: 300,
25
- acquireTimeout: 100,
26
- refreshInterval: 80,
27
- retryInterval: 10
28
- }
29
-
30
- async function expectZRangeAllEql(key: string, values: string[]) {
31
- const results = await Promise.all([
32
- client1.zrange(key, 0, -1),
33
- client2.zrange(key, 0, -1),
34
- client3.zrange(key, 0, -1)
35
- ])
36
- expect(results).to.be.eql([values, values, values])
37
- }
38
-
39
- async function expectZRangeAllHaveMembers(key: string, values: string[]) {
40
- const results = await Promise.all([
41
- client1.zrange(key, 0, -1),
42
- client2.zrange(key, 0, -1),
43
- client3.zrange(key, 0, -1)
44
- ])
45
- for (const result of results) {
46
- expect(result).to.have.members(values)
47
- }
48
- }
49
-
50
- async function expectZCardAllEql(key: string, count: number) {
51
- const results = await Promise.all([
52
- client1.zcard(key),
53
- client2.zcard(key),
54
- client3.zcard(key)
55
- ])
56
- expect(results).to.be.eql([count, count, count])
57
- }
58
-
59
- describe('RedlockMultiSemaphore', () => {
60
- it('should fail on invalid arguments', () => {
61
- expect(
62
- () => new RedlockMultiSemaphore(null as unknown as Redis[], 'key', 5, 2)
63
- ).to.throw('"clients" array is required')
64
- expect(() => new RedlockMultiSemaphore(allClients, '', 5, 2)).to.throw(
65
- '"key" is required'
66
- )
67
- expect(
68
- () => new RedlockMultiSemaphore(allClients, 1 as unknown as string, 5, 2)
69
- ).to.throw('"key" must be a string')
70
- expect(() => new RedlockMultiSemaphore(allClients, 'key', 0, 2)).to.throw(
71
- '"limit" is required'
72
- )
73
- expect(
74
- () =>
75
- new RedlockMultiSemaphore(
76
- allClients,
77
- 'key',
78
- '10' as unknown as number,
79
- 2
80
- )
81
- ).to.throw('"limit" must be a number')
82
- expect(() => new RedlockMultiSemaphore(allClients, 'key', 5, 0)).to.throw(
83
- '"permits" is required'
84
- )
85
- expect(
86
- () =>
87
- new RedlockMultiSemaphore(
88
- allClients,
89
- 'key',
90
- 5,
91
- '2' as unknown as number
92
- )
93
- ).to.throw('"permits" must be a number')
94
- })
95
- it('should acquire and release semaphore', async () => {
96
- const semaphore1 = new RedlockMultiSemaphore(allClients, 'key', 3, 2)
97
- const semaphore2 = new RedlockMultiSemaphore(allClients, 'key', 3, 1)
98
- expect(semaphore1.isAcquired).to.be.false
99
- expect(semaphore2.isAcquired).to.be.false
100
-
101
- await semaphore1.acquire()
102
- expect(semaphore1.isAcquired).to.be.true
103
- await semaphore2.acquire()
104
- expect(semaphore2.isAcquired).to.be.true
105
- await expectZRangeAllHaveMembers('semaphore:key', [
106
- semaphore1.identifier + '_0',
107
- semaphore1.identifier + '_1',
108
- semaphore2.identifier + '_0'
109
- ])
110
-
111
- await semaphore1.release()
112
- expect(semaphore1.isAcquired).to.be.false
113
- await expectZRangeAllEql('semaphore:key', [semaphore2.identifier + '_0'])
114
- await semaphore2.release()
115
- expect(semaphore2.isAcquired).to.be.false
116
- await expectZCardAllEql('semaphore:key', 0)
117
- })
118
- it('should reject after timeout', async () => {
119
- const semaphore1 = new RedlockMultiSemaphore(
120
- allClients,
121
- 'key',
122
- 3,
123
- 3,
124
- timeoutOptions
125
- )
126
- const semaphore2 = new RedlockMultiSemaphore(
127
- allClients,
128
- 'key',
129
- 3,
130
- 1,
131
- timeoutOptions
132
- )
133
- await semaphore1.acquire()
134
- await expect(semaphore2.acquire()).to.be.rejectedWith(
135
- 'Acquire redlock-multi-semaphore semaphore:key timeout'
136
- )
137
- await semaphore1.release()
138
- await expectZCardAllEql('semaphore:key', 0)
139
- })
140
- it('should refresh lock every refreshInterval ms until release', async () => {
141
- const semaphore1 = new RedlockMultiSemaphore(
142
- allClients,
143
- 'key',
144
- 3,
145
- 2,
146
- timeoutOptions
147
- )
148
- const semaphore2 = new RedlockMultiSemaphore(
149
- allClients,
150
- 'key',
151
- 3,
152
- 1,
153
- timeoutOptions
154
- )
155
- await semaphore1.acquire()
156
- await semaphore2.acquire()
157
- await delay(400)
158
- await expectZRangeAllHaveMembers('semaphore:key', [
159
- semaphore1.identifier + '_0',
160
- semaphore1.identifier + '_1',
161
- semaphore2.identifier + '_0'
162
- ])
163
- await semaphore1.release()
164
- await expectZRangeAllEql('semaphore:key', [semaphore2.identifier + '_0'])
165
- await semaphore2.release()
166
- await expectZCardAllEql('semaphore:key', 0)
167
- })
168
- it('should stop refreshing lock if stopped', async () => {
169
- const semaphore1 = new RedlockMultiSemaphore(
170
- allClients,
171
- 'key',
172
- 3,
173
- 2,
174
- timeoutOptions
175
- )
176
- const semaphore2 = new RedlockMultiSemaphore(
177
- allClients,
178
- 'key',
179
- 3,
180
- 1,
181
- timeoutOptions
182
- )
183
- await semaphore1.acquire()
184
- await semaphore2.acquire()
185
- semaphore1.stopRefresh()
186
- await delay(400)
187
- await expectZRangeAllEql('semaphore:key', [semaphore2.identifier + '_0'])
188
- semaphore2.stopRefresh()
189
- await delay(400)
190
- await expectZCardAllEql('semaphore:key', 0)
191
- })
192
- it('should acquire maximum LIMIT semaphores', async () => {
193
- const s = () =>
194
- new RedlockMultiSemaphore(allClients, 'key', 3, 1, {
195
- acquireTimeout: 1000,
196
- lockTimeout: 50,
197
- retryInterval: 10,
198
- refreshInterval: 0 // disable refresh
199
- })
200
- const set1 = [s(), s(), s()]
201
- const pr1 = Promise.all(set1.map(sem => sem.acquire()))
202
- await delay(5)
203
- const set2 = [s(), s(), s()]
204
- const pr2 = Promise.all(set2.map(sem => sem.acquire()))
205
- await pr1
206
- await expectZRangeAllHaveMembers('semaphore:key', [
207
- set1[0].identifier + '_0',
208
- set1[1].identifier + '_0',
209
- set1[2].identifier + '_0'
210
- ])
211
- await expectZCardAllEql('semaphore:key', 3)
212
- await pr2
213
- await expectZRangeAllHaveMembers('semaphore:key', [
214
- set2[0].identifier + '_0',
215
- set2[1].identifier + '_0',
216
- set2[2].identifier + '_0'
217
- ])
218
- await expectZCardAllEql('semaphore:key', 3)
219
- })
220
- it('should support externally acquired semaphore (deprecated interface)', async () => {
221
- const externalSemaphore = new RedlockMultiSemaphore(
222
- allClients,
223
- 'key',
224
- 3,
225
- 2,
226
- {
227
- ...timeoutOptions,
228
- refreshInterval: 0
229
- }
230
- )
231
- const localSemaphore = new RedlockMultiSemaphore(allClients, 'key', 3, 2, {
232
- ...timeoutOptions,
233
- externallyAcquiredIdentifier: externalSemaphore.identifier
234
- })
235
- await externalSemaphore.acquire()
236
- await localSemaphore.acquire()
237
- await delay(400)
238
- await expectZRangeAllHaveMembers('semaphore:key', [
239
- localSemaphore.identifier + '_0',
240
- localSemaphore.identifier + '_1'
241
- ])
242
- await localSemaphore.release()
243
- await expectZCardAllEql('semaphore:key', 0)
244
- })
245
- it('should support externally acquired semaphore', async () => {
246
- const externalSemaphore = new RedlockMultiSemaphore(
247
- allClients,
248
- 'key',
249
- 3,
250
- 2,
251
- {
252
- ...timeoutOptions,
253
- refreshInterval: 0
254
- }
255
- )
256
- const localSemaphore = new RedlockMultiSemaphore(allClients, 'key', 3, 2, {
257
- ...timeoutOptions,
258
- identifier: externalSemaphore.identifier,
259
- acquiredExternally: true
260
- })
261
- await externalSemaphore.acquire()
262
- await localSemaphore.acquire()
263
- await delay(400)
264
- await expectZRangeAllHaveMembers('semaphore:key', [
265
- localSemaphore.identifier + '_0',
266
- localSemaphore.identifier + '_1'
267
- ])
268
- await localSemaphore.release()
269
- await expectZCardAllEql('semaphore:key', 0)
270
- })
271
- describe('lost lock case', () => {
272
- beforeEach(() => {
273
- catchUnhandledRejection()
274
- })
275
- afterEach(() => {
276
- throwUnhandledRejection()
277
- })
278
- it('should throw unhandled error if lock is lost between refreshes', async () => {
279
- const semaphore = new RedlockMultiSemaphore(
280
- allClients,
281
- 'key',
282
- 3,
283
- 2,
284
- timeoutOptions
285
- )
286
- await semaphore.acquire()
287
- await Promise.all(allClients.map(client => client.del('semaphore:key')))
288
- await Promise.all(
289
- allClients.map(client =>
290
- client.zadd(
291
- 'semaphore:key',
292
- Date.now(),
293
- 'aaa',
294
- Date.now(),
295
- 'bbb',
296
- Date.now(),
297
- 'ccc'
298
- )
299
- )
300
- )
301
- await delay(200)
302
- expect(unhandledRejectionSpy).to.be.called
303
- expect(unhandledRejectionSpy.firstCall.firstArg instanceof LostLockError)
304
- .to.be.true
305
- })
306
- it('should call onLockLost callback if provided', async () => {
307
- const onLockLostCallback = sinon.spy(function (
308
- this: RedlockMultiSemaphore
309
- ) {
310
- expect(this.isAcquired).to.be.false
311
- })
312
- const semaphore = new RedlockMultiSemaphore(allClients, 'key', 3, 2, {
313
- ...timeoutOptions,
314
- onLockLost: onLockLostCallback
315
- })
316
- await semaphore.acquire()
317
- expect(semaphore.isAcquired).to.be.true
318
- await Promise.all(allClients.map(client => client.del('semaphore:key')))
319
- await Promise.all(
320
- allClients.map(client =>
321
- client.zadd(
322
- 'semaphore:key',
323
- Date.now(),
324
- 'aaa',
325
- Date.now(),
326
- 'bbb',
327
- Date.now(),
328
- 'ccc'
329
- )
330
- )
331
- )
332
- await delay(200)
333
- expect(semaphore.isAcquired).to.be.false
334
- expect(unhandledRejectionSpy).to.not.called
335
- expect(onLockLostCallback).to.be.called
336
- expect(onLockLostCallback.firstCall.firstArg instanceof LostLockError).to
337
- .be.true
338
- })
339
- })
340
- describe('reusable', () => {
341
- it('autorefresh enabled', async function () {
342
- this.timeout(10000)
343
- const semaphore1 = new RedlockMultiSemaphore(
344
- allClients,
345
- 'key',
346
- 4,
347
- 2,
348
- timeoutOptions
349
- )
350
- const semaphore2 = new RedlockMultiSemaphore(
351
- allClients,
352
- 'key',
353
- 4,
354
- 2,
355
- timeoutOptions
356
- )
357
-
358
- await semaphore1.acquire()
359
- await semaphore2.acquire()
360
- await delay(300)
361
- await semaphore1.release()
362
- await semaphore2.release()
363
-
364
- await delay(300)
365
-
366
- await semaphore1.acquire()
367
- await semaphore2.acquire()
368
- await delay(300)
369
- await semaphore1.release()
370
- await semaphore2.release()
371
-
372
- await delay(300)
373
-
374
- await semaphore1.acquire()
375
- await semaphore2.acquire()
376
- await delay(300)
377
- await semaphore1.release()
378
- await semaphore2.release()
379
- })
380
-
381
- it('autorefresh disabled', async () => {
382
- const noRefreshOptions = {
383
- ...timeoutOptions,
384
- refreshInterval: 0,
385
- acquireTimeout: 10
386
- }
387
- const semaphore1 = new RedlockMultiSemaphore(
388
- allClients,
389
- 'key',
390
- 4,
391
- 2,
392
- noRefreshOptions
393
- )
394
- const semaphore2 = new RedlockMultiSemaphore(
395
- allClients,
396
- 'key',
397
- 4,
398
- 2,
399
- noRefreshOptions
400
- )
401
- const semaphore3 = new RedlockMultiSemaphore(
402
- allClients,
403
- 'key',
404
- 4,
405
- 2,
406
- noRefreshOptions
407
- )
408
-
409
- await semaphore1.acquire()
410
- await semaphore2.acquire()
411
- await delay(300)
412
- await semaphore1.release()
413
- await semaphore2.release()
414
-
415
- await delay(300)
416
-
417
- // [0/2]
418
- await semaphore1.acquire()
419
- // [1/2]
420
- await delay(80)
421
- await semaphore2.acquire()
422
- // [2/2]
423
- await expect(semaphore3.acquire()).to.be.rejectedWith(
424
- 'Acquire redlock-multi-semaphore semaphore:key timeout'
425
- ) // rejectes after 10ms
426
-
427
- // since semaphore1.acquire() elapsed 80ms (delay) + 10ms (semaphore3 timeout)
428
- // semaphore1 will expire after 300 - 90 = 210ms
429
- await delay(210)
430
-
431
- // [1/2]
432
- await semaphore3.acquire()
433
- })
434
- })
435
- describe('Compatibility with Semaphore', () => {
436
- it('should work with Semaphore', async () => {
437
- const multiSemaphore1 = new RedlockMultiSemaphore(
438
- allClients,
439
- 'key',
440
- 3,
441
- 2,
442
- timeoutOptions
443
- )
444
- const multiSemaphore2 = new RedlockMultiSemaphore(
445
- allClients,
446
- 'key',
447
- 3,
448
- 2,
449
- timeoutOptions
450
- )
451
- const semaphore1 = new RedlockSemaphore(
452
- allClients,
453
- 'key',
454
- 3,
455
- timeoutOptions
456
- )
457
- const semaphore2 = new RedlockSemaphore(
458
- allClients,
459
- 'key',
460
- 3,
461
- timeoutOptions
462
- )
463
- await multiSemaphore1.acquire()
464
- await semaphore1.acquire()
465
- await expectZRangeAllHaveMembers('semaphore:key', [
466
- multiSemaphore1.identifier + '_0',
467
- multiSemaphore1.identifier + '_1',
468
- semaphore1.identifier
469
- ])
470
- await expect(multiSemaphore2.acquire()).to.be.rejectedWith(
471
- 'Acquire redlock-multi-semaphore semaphore:key timeout'
472
- )
473
- await expect(semaphore2.acquire()).to.be.rejectedWith(
474
- 'Acquire redlock-semaphore semaphore:key timeout'
475
- )
476
- await multiSemaphore1.release()
477
- await expectZRangeAllEql('semaphore:key', [semaphore1.identifier])
478
- await semaphore1.release()
479
- await expectZCardAllEql('semaphore:key', 0)
480
- })
481
- })
482
- describe('[Node shutdown]', () => {
483
- afterEach(async () => {
484
- await Promise.all([upRedisServer(1), upRedisServer(2), upRedisServer(3)])
485
- })
486
- it('should handle server shutdown if quorum is alive', async function () {
487
- this.timeout(60000)
488
- const semaphore11 = new RedlockMultiSemaphore(
489
- allClients,
490
- 'key',
491
- 3,
492
- 2,
493
- timeoutOptions
494
- )
495
- const semaphore12 = new RedlockMultiSemaphore(
496
- allClients,
497
- 'key',
498
- 3,
499
- 1,
500
- timeoutOptions
501
- )
502
- await Promise.all([semaphore11.acquire(), semaphore12.acquire()])
503
-
504
- // <Server1Failure>
505
- await downRedisServer(1)
506
- console.log('SHUT DOWN 1')
507
-
508
- await delay(1000)
509
-
510
- // lock survive in server2 and server3
511
- // semaphore2 will NOT be able to acquire the lock
512
-
513
- const semaphore2 = new RedlockSemaphore(
514
- allClients,
515
- 'key',
516
- 3,
517
- timeoutOptions
518
- )
519
- await expect(semaphore2.acquire()).to.be.rejectedWith(
520
- 'Acquire redlock-semaphore semaphore:key timeout'
521
- )
522
-
523
- // key in server1 has expired now
524
-
525
- await upRedisServer(1)
526
- console.log('ONLINE 1')
527
-
528
- // let semaphore1[1-3] to refresh lock on server1
529
- await delay(1000)
530
- expect(await client1.zrange('semaphore:key', 0, -1)).to.have.members([
531
- semaphore11.identifier + '_0',
532
- semaphore11.identifier + '_1',
533
- semaphore12.identifier + '_0'
534
- ])
535
- // </Server1Failure>
536
-
537
- // <Server2Failure>
538
- await downRedisServer(2)
539
- console.log('SHUT DOWN 2')
540
-
541
- await delay(1000)
542
-
543
- // lock survive in server1 and server3
544
- // semaphore3 will NOT be able to acquire the lock
545
-
546
- const semaphore3 = new RedlockSemaphore(
547
- allClients,
548
- 'key',
549
- 3,
550
- timeoutOptions
551
- )
552
- await expect(semaphore3.acquire()).to.be.rejectedWith(
553
- 'Acquire redlock-semaphore semaphore:key timeout'
554
- )
555
-
556
- // key in server2 has expired now
557
-
558
- await upRedisServer(2)
559
- console.log('ONLINE 2')
560
-
561
- // let semaphore1[1-3] to refresh lock on server1
562
- await delay(1000)
563
- expect(await client2.zrange('semaphore:key', 0, -1)).to.have.members([
564
- semaphore11.identifier + '_0',
565
- semaphore11.identifier + '_1',
566
- semaphore12.identifier + '_0'
567
- ])
568
- // </Server2Failure>
569
-
570
- // <Server3Failure>
571
- await downRedisServer(3)
572
- console.log('SHUT DOWN 3')
573
-
574
- await delay(1000)
575
-
576
- // lock survive in server1 and server2
577
- // semaphore4 will NOT be able to acquire the lock
578
-
579
- const semaphore4 = new RedlockSemaphore(
580
- allClients,
581
- 'key',
582
- 3,
583
- timeoutOptions
584
- )
585
- await expect(semaphore4.acquire()).to.be.rejectedWith(
586
- 'Acquire redlock-semaphore semaphore:key timeout'
587
- )
588
-
589
- // key in server1 has expired now
590
-
591
- await upRedisServer(3)
592
- console.log('ONLINE 3')
593
-
594
- // let semaphore1[1-3] to refresh lock on server1
595
- await delay(1000)
596
- expect(await client3.zrange('semaphore:key', 0, -1)).to.have.members([
597
- semaphore11.identifier + '_0',
598
- semaphore11.identifier + '_1',
599
- semaphore12.identifier + '_0'
600
- ])
601
- // </Server3Failure>
602
-
603
- await Promise.all([semaphore11.release(), semaphore12.release()])
604
- })
605
- it('should fail and release if quorum become dead', async function () {
606
- this.timeout(60000)
607
- const onLockLostCallbacks = [1, 2].map(() =>
608
- sinon.spy(function (this: RedlockMultiSemaphore) {
609
- expect(this.isAcquired).to.be.false
610
- })
611
- )
612
-
613
- const semaphore11 = new RedlockMultiSemaphore(allClients, 'key', 3, 2, {
614
- ...timeoutOptions,
615
- onLockLost: onLockLostCallbacks[0]
616
- })
617
- const semaphore12 = new RedlockMultiSemaphore(allClients, 'key', 3, 1, {
618
- ...timeoutOptions,
619
- onLockLost: onLockLostCallbacks[1]
620
- })
621
- await Promise.all([semaphore11.acquire(), semaphore12.acquire()])
622
-
623
- await downRedisServer(1)
624
- console.log('SHUT DOWN 1')
625
-
626
- await downRedisServer(2)
627
- console.log('SHUT DOWN 2')
628
-
629
- await delay(1000)
630
-
631
- for (const lostCb of onLockLostCallbacks) {
632
- expect(lostCb).to.be.called
633
- expect(lostCb.firstCall.firstArg instanceof LostLockError).to.be.true
634
- }
635
-
636
- // released lock on server3
637
- expect(await client3.zrange('semaphore:key', 0, -1)).to.be.eql([])
638
-
639
- // semaphore2 will NOT be able to acquire the lock
640
-
641
- const semaphore2 = new RedlockMultiSemaphore(
642
- allClients,
643
- 'key',
644
- 3,
645
- 1,
646
- timeoutOptions
647
- )
648
- await expect(semaphore2.acquire()).to.be.rejectedWith(
649
- 'Acquire redlock-multi-semaphore semaphore:key timeout'
650
- )
651
- })
652
- })
653
- describe('ioredis-mock support', () => {
654
- it('should acquire and release semaphore', async () => {
655
- const semaphore1 = new RedlockMultiSemaphore(allClientMocks, 'key', 3, 2)
656
- const semaphore2 = new RedlockMultiSemaphore(allClientMocks, 'key', 3, 1)
657
- expect(semaphore1.isAcquired).to.be.false
658
- expect(semaphore2.isAcquired).to.be.false
659
-
660
- await semaphore1.acquire()
661
- expect(semaphore1.isAcquired).to.be.true
662
- await semaphore2.acquire()
663
- expect(semaphore2.isAcquired).to.be.true
664
-
665
- await semaphore1.release()
666
- expect(semaphore1.isAcquired).to.be.false
667
- await semaphore2.release()
668
- expect(semaphore2.isAcquired).to.be.false
669
- })
670
- })
671
- })