effect-distributed-lock 0.0.6 → 0.0.8
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/examples/push.ts +109 -0
- package/package.json +1 -1
- package/src/DistributedSemaphore.ts +7 -8
- package/src/RedisBacking.ts +4 -4
- package/redis-semaphore/.codeclimate.yml +0 -5
- package/redis-semaphore/.fossa.yml +0 -14
- package/redis-semaphore/.github/dependabot.yml +0 -6
- package/redis-semaphore/.github/workflows/branches.yml +0 -39
- package/redis-semaphore/.github/workflows/pull-requests.yml +0 -35
- package/redis-semaphore/.mocharc.yaml +0 -6
- package/redis-semaphore/.prettierrc +0 -6
- package/redis-semaphore/.snyk +0 -4
- package/redis-semaphore/.yarnrc.yml +0 -2
- package/redis-semaphore/CHANGELOG.md +0 -70
- package/redis-semaphore/Dockerfile +0 -5
- package/redis-semaphore/LICENSE +0 -21
- package/redis-semaphore/README.md +0 -445
- package/redis-semaphore/docker-compose.yml +0 -31
- package/redis-semaphore/eslint.config.mjs +0 -73
- package/redis-semaphore/package.json +0 -79
- package/redis-semaphore/setup-redis-servers.sh +0 -2
- package/redis-semaphore/src/Lock.ts +0 -172
- package/redis-semaphore/src/RedisMultiSemaphore.ts +0 -56
- package/redis-semaphore/src/RedisMutex.ts +0 -45
- package/redis-semaphore/src/RedisSemaphore.ts +0 -49
- package/redis-semaphore/src/RedlockMultiSemaphore.ts +0 -56
- package/redis-semaphore/src/RedlockMutex.ts +0 -52
- package/redis-semaphore/src/RedlockSemaphore.ts +0 -49
- package/redis-semaphore/src/errors/LostLockError.ts +0 -1
- package/redis-semaphore/src/errors/TimeoutError.ts +0 -1
- package/redis-semaphore/src/index.ts +0 -23
- package/redis-semaphore/src/misc.ts +0 -12
- package/redis-semaphore/src/multiSemaphore/acquire/index.ts +0 -53
- package/redis-semaphore/src/multiSemaphore/acquire/lua.ts +0 -31
- package/redis-semaphore/src/multiSemaphore/refresh/index.ts +0 -32
- package/redis-semaphore/src/multiSemaphore/refresh/lua.ts +0 -31
- package/redis-semaphore/src/multiSemaphore/release/index.ts +0 -22
- package/redis-semaphore/src/multiSemaphore/release/lua.ts +0 -17
- package/redis-semaphore/src/mutex/acquire.ts +0 -42
- package/redis-semaphore/src/mutex/refresh.ts +0 -37
- package/redis-semaphore/src/mutex/release.ts +0 -30
- package/redis-semaphore/src/redlockMultiSemaphore/acquire.ts +0 -56
- package/redis-semaphore/src/redlockMultiSemaphore/refresh.ts +0 -68
- package/redis-semaphore/src/redlockMultiSemaphore/release.ts +0 -19
- package/redis-semaphore/src/redlockMutex/acquire.ts +0 -54
- package/redis-semaphore/src/redlockMutex/refresh.ts +0 -53
- package/redis-semaphore/src/redlockMutex/release.ts +0 -19
- package/redis-semaphore/src/redlockSemaphore/acquire.ts +0 -55
- package/redis-semaphore/src/redlockSemaphore/refresh.ts +0 -60
- package/redis-semaphore/src/redlockSemaphore/release.ts +0 -18
- package/redis-semaphore/src/semaphore/acquire/index.ts +0 -52
- package/redis-semaphore/src/semaphore/acquire/lua.ts +0 -25
- package/redis-semaphore/src/semaphore/refresh/index.ts +0 -31
- package/redis-semaphore/src/semaphore/refresh/lua.ts +0 -25
- package/redis-semaphore/src/semaphore/release.ts +0 -14
- package/redis-semaphore/src/types.ts +0 -63
- package/redis-semaphore/src/utils/createEval.ts +0 -45
- package/redis-semaphore/src/utils/index.ts +0 -13
- package/redis-semaphore/src/utils/redlock.ts +0 -7
- package/redis-semaphore/test/init.test.ts +0 -9
- package/redis-semaphore/test/redisClient.ts +0 -82
- package/redis-semaphore/test/setup.ts +0 -6
- package/redis-semaphore/test/shell.test.ts +0 -15
- package/redis-semaphore/test/shell.ts +0 -48
- package/redis-semaphore/test/src/Lock.test.ts +0 -37
- package/redis-semaphore/test/src/RedisMultiSemaphore.test.ts +0 -425
- package/redis-semaphore/test/src/RedisMutex.test.ts +0 -334
- package/redis-semaphore/test/src/RedisSemaphore.test.ts +0 -367
- package/redis-semaphore/test/src/RedlockMultiSemaphore.test.ts +0 -671
- package/redis-semaphore/test/src/RedlockMutex.test.ts +0 -328
- package/redis-semaphore/test/src/RedlockSemaphore.test.ts +0 -579
- package/redis-semaphore/test/src/index.test.ts +0 -22
- package/redis-semaphore/test/src/multiSemaphore/acquire/index.test.ts +0 -51
- package/redis-semaphore/test/src/multiSemaphore/acquire/internal.test.ts +0 -67
- package/redis-semaphore/test/src/multiSemaphore/refresh/index.test.ts +0 -52
- package/redis-semaphore/test/src/multiSemaphore/release/index.test.ts +0 -18
- package/redis-semaphore/test/src/mutex/acquire.test.ts +0 -78
- package/redis-semaphore/test/src/mutex/refresh.test.ts +0 -22
- package/redis-semaphore/test/src/mutex/release.test.ts +0 -17
- package/redis-semaphore/test/src/redlockMutex/acquire.test.ts +0 -90
- package/redis-semaphore/test/src/redlockMutex/refresh.test.ts +0 -27
- package/redis-semaphore/test/src/redlockMutex/release.test.ts +0 -17
- package/redis-semaphore/test/src/semaphore/acquire/index.test.ts +0 -49
- package/redis-semaphore/test/src/semaphore/acquire/internal.test.ts +0 -65
- package/redis-semaphore/test/src/semaphore/refresh/index.test.ts +0 -44
- package/redis-semaphore/test/src/semaphore/release.test.ts +0 -18
- package/redis-semaphore/test/src/utils/eval.test.ts +0 -22
- package/redis-semaphore/test/src/utils/index.test.ts +0 -19
- package/redis-semaphore/test/src/utils/redlock.test.ts +0 -31
- package/redis-semaphore/test/unhandledRejection.ts +0 -28
- package/redis-semaphore/tsconfig.build-commonjs.json +0 -9
- package/redis-semaphore/tsconfig.build-es.json +0 -9
- package/redis-semaphore/tsconfig.json +0 -11
- package/redis-semaphore/yarn.lock +0 -5338
- /package/examples/{index.ts → kitchen-sink.ts} +0 -0
|
@@ -1,425 +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 MultiSemaphore from '../../src/RedisMultiSemaphore'
|
|
6
|
-
import Semaphore from '../../src/RedisSemaphore'
|
|
7
|
-
import { TimeoutOptions } from '../../src/types'
|
|
8
|
-
import { delay } from '../../src/utils/index'
|
|
9
|
-
import { client1 as client, clientMock1 as clientMock } from '../redisClient'
|
|
10
|
-
import { downRedisServer, upRedisServer } from '../shell'
|
|
11
|
-
import {
|
|
12
|
-
catchUnhandledRejection,
|
|
13
|
-
throwUnhandledRejection,
|
|
14
|
-
unhandledRejectionSpy
|
|
15
|
-
} from '../unhandledRejection'
|
|
16
|
-
|
|
17
|
-
const timeoutOptions: TimeoutOptions = {
|
|
18
|
-
lockTimeout: 300,
|
|
19
|
-
acquireTimeout: 100,
|
|
20
|
-
refreshInterval: 80,
|
|
21
|
-
retryInterval: 10
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe('MultiSemaphore', () => {
|
|
25
|
-
it('should fail on invalid arguments', () => {
|
|
26
|
-
expect(
|
|
27
|
-
() => new MultiSemaphore(null as unknown as Redis, 'key', 5, 2)
|
|
28
|
-
).to.throw('"client" is required')
|
|
29
|
-
expect(() => new MultiSemaphore(client, '', 5, 2)).to.throw(
|
|
30
|
-
'"key" is required'
|
|
31
|
-
)
|
|
32
|
-
expect(
|
|
33
|
-
() => new MultiSemaphore(client, 1 as unknown as string, 5, 2)
|
|
34
|
-
).to.throw('"key" must be a string')
|
|
35
|
-
expect(() => new MultiSemaphore(client, 'key', 0, 2)).to.throw(
|
|
36
|
-
'"limit" is required'
|
|
37
|
-
)
|
|
38
|
-
expect(
|
|
39
|
-
() => new MultiSemaphore(client, 'key', '10' as unknown as number, 2)
|
|
40
|
-
).to.throw('"limit" must be a number')
|
|
41
|
-
expect(() => new MultiSemaphore(client, 'key', 5, 0)).to.throw(
|
|
42
|
-
'"permits" is required'
|
|
43
|
-
)
|
|
44
|
-
expect(
|
|
45
|
-
() => new MultiSemaphore(client, 'key', 5, '2' as unknown as number)
|
|
46
|
-
).to.throw('"permits" must be a number')
|
|
47
|
-
})
|
|
48
|
-
it('should acquire and release semaphore', async () => {
|
|
49
|
-
const semaphore1 = new MultiSemaphore(client, 'key', 3, 2)
|
|
50
|
-
const semaphore2 = new MultiSemaphore(client, 'key', 3, 1)
|
|
51
|
-
expect(semaphore1.isAcquired).to.be.false
|
|
52
|
-
expect(semaphore2.isAcquired).to.be.false
|
|
53
|
-
|
|
54
|
-
await semaphore1.acquire()
|
|
55
|
-
expect(semaphore1.isAcquired).to.be.true
|
|
56
|
-
await semaphore2.acquire()
|
|
57
|
-
expect(semaphore2.isAcquired).to.be.true
|
|
58
|
-
expect(await client.zrange('semaphore:key', 0, -1)).to.have.members([
|
|
59
|
-
semaphore1.identifier + '_0',
|
|
60
|
-
semaphore1.identifier + '_1',
|
|
61
|
-
semaphore2.identifier + '_0'
|
|
62
|
-
])
|
|
63
|
-
|
|
64
|
-
await semaphore1.release()
|
|
65
|
-
expect(semaphore1.isAcquired).to.be.false
|
|
66
|
-
expect(await client.zrange('semaphore:key', 0, -1)).to.be.eql([
|
|
67
|
-
semaphore2.identifier + '_0'
|
|
68
|
-
])
|
|
69
|
-
await semaphore2.release()
|
|
70
|
-
expect(semaphore2.isAcquired).to.be.false
|
|
71
|
-
expect(await client.zcard('semaphore:key')).to.be.eql(0)
|
|
72
|
-
})
|
|
73
|
-
it('should reject after timeout', async () => {
|
|
74
|
-
const semaphore1 = new MultiSemaphore(client, 'key', 3, 3, timeoutOptions)
|
|
75
|
-
const semaphore2 = new MultiSemaphore(client, 'key', 3, 1, timeoutOptions)
|
|
76
|
-
await semaphore1.acquire()
|
|
77
|
-
await expect(semaphore2.acquire()).to.be.rejectedWith(
|
|
78
|
-
'Acquire multi-semaphore semaphore:key timeout'
|
|
79
|
-
)
|
|
80
|
-
await semaphore1.release()
|
|
81
|
-
expect(await client.get('semaphore:key')).to.be.eql(null)
|
|
82
|
-
})
|
|
83
|
-
it('should refresh lock every refreshInterval ms until release', async () => {
|
|
84
|
-
const semaphore1 = new MultiSemaphore(client, 'key', 3, 2, timeoutOptions)
|
|
85
|
-
const semaphore2 = new MultiSemaphore(client, 'key', 3, 1, timeoutOptions)
|
|
86
|
-
await semaphore1.acquire()
|
|
87
|
-
await semaphore2.acquire()
|
|
88
|
-
await delay(400)
|
|
89
|
-
expect(await client.zrange('semaphore:key', 0, -1)).to.have.members([
|
|
90
|
-
semaphore1.identifier + '_0',
|
|
91
|
-
semaphore1.identifier + '_1',
|
|
92
|
-
semaphore2.identifier + '_0'
|
|
93
|
-
])
|
|
94
|
-
await semaphore1.release()
|
|
95
|
-
expect(await client.zrange('semaphore:key', 0, -1)).to.be.eql([
|
|
96
|
-
semaphore2.identifier + '_0'
|
|
97
|
-
])
|
|
98
|
-
await semaphore2.release()
|
|
99
|
-
expect(await client.zcard('semaphore:key')).to.be.eql(0)
|
|
100
|
-
})
|
|
101
|
-
it('should stop refreshing lock if stopped', async () => {
|
|
102
|
-
const semaphore1 = new MultiSemaphore(client, 'key', 3, 2, timeoutOptions)
|
|
103
|
-
const semaphore2 = new MultiSemaphore(client, 'key', 3, 1, timeoutOptions)
|
|
104
|
-
await semaphore1.acquire()
|
|
105
|
-
await semaphore2.acquire()
|
|
106
|
-
await semaphore1.stopRefresh()
|
|
107
|
-
await delay(400)
|
|
108
|
-
expect(await client.zrange('semaphore:key', 0, -1)).to.be.eql([
|
|
109
|
-
semaphore2.identifier + '_0'
|
|
110
|
-
])
|
|
111
|
-
await semaphore2.stopRefresh()
|
|
112
|
-
await delay(400)
|
|
113
|
-
expect(await client.zcard('semaphore:key')).to.be.eql(0)
|
|
114
|
-
})
|
|
115
|
-
it('should acquire maximum LIMIT semaphores', async () => {
|
|
116
|
-
const s = () =>
|
|
117
|
-
new MultiSemaphore(client, 'key', 3, 1, {
|
|
118
|
-
acquireTimeout: 1000,
|
|
119
|
-
lockTimeout: 50,
|
|
120
|
-
retryInterval: 10,
|
|
121
|
-
refreshInterval: 0 // disable refresh
|
|
122
|
-
})
|
|
123
|
-
const pr1 = Promise.all([s().acquire(), s().acquire(), s().acquire()])
|
|
124
|
-
await delay(5)
|
|
125
|
-
const pr2 = Promise.all([s().acquire(), s().acquire(), s().acquire()])
|
|
126
|
-
await pr1
|
|
127
|
-
const ids1 = await client.zrange('semaphore:key', 0, -1)
|
|
128
|
-
expect(ids1.length).to.be.eql(3)
|
|
129
|
-
await pr2
|
|
130
|
-
const ids2 = await client.zrange('semaphore:key', 0, -1)
|
|
131
|
-
expect(ids2.length).to.be.eql(3)
|
|
132
|
-
expect(ids2)
|
|
133
|
-
.to.not.include(ids1[0])
|
|
134
|
-
.and.not.include(ids1[1])
|
|
135
|
-
.and.not.include(ids1[2])
|
|
136
|
-
})
|
|
137
|
-
it('should support externally acquired semaphore (deprecated interface)', async () => {
|
|
138
|
-
const externalSemaphore = new MultiSemaphore(client, 'key', 3, 2, {
|
|
139
|
-
...timeoutOptions,
|
|
140
|
-
refreshInterval: 0
|
|
141
|
-
})
|
|
142
|
-
const localSemaphore = new MultiSemaphore(client, 'key', 3, 2, {
|
|
143
|
-
...timeoutOptions,
|
|
144
|
-
externallyAcquiredIdentifier: externalSemaphore.identifier
|
|
145
|
-
})
|
|
146
|
-
await externalSemaphore.acquire()
|
|
147
|
-
await localSemaphore.acquire()
|
|
148
|
-
await delay(400)
|
|
149
|
-
expect(await client.zrange('semaphore:key', 0, -1)).to.be.eql([
|
|
150
|
-
localSemaphore.identifier + '_0',
|
|
151
|
-
localSemaphore.identifier + '_1'
|
|
152
|
-
])
|
|
153
|
-
await localSemaphore.release()
|
|
154
|
-
expect(await client.zcard('semaphore:key')).to.be.eql(0)
|
|
155
|
-
})
|
|
156
|
-
it('should support externally acquired semaphore', async () => {
|
|
157
|
-
const externalSemaphore = new MultiSemaphore(client, 'key', 3, 2, {
|
|
158
|
-
...timeoutOptions,
|
|
159
|
-
refreshInterval: 0
|
|
160
|
-
})
|
|
161
|
-
const localSemaphore = new MultiSemaphore(client, 'key', 3, 2, {
|
|
162
|
-
...timeoutOptions,
|
|
163
|
-
identifier: externalSemaphore.identifier,
|
|
164
|
-
acquiredExternally: true
|
|
165
|
-
})
|
|
166
|
-
await externalSemaphore.acquire()
|
|
167
|
-
await localSemaphore.acquire()
|
|
168
|
-
await delay(400)
|
|
169
|
-
expect(await client.zrange('semaphore:key', 0, -1)).to.be.eql([
|
|
170
|
-
localSemaphore.identifier + '_0',
|
|
171
|
-
localSemaphore.identifier + '_1'
|
|
172
|
-
])
|
|
173
|
-
await localSemaphore.release()
|
|
174
|
-
expect(await client.zcard('semaphore:key')).to.be.eql(0)
|
|
175
|
-
})
|
|
176
|
-
describe('lost lock case', () => {
|
|
177
|
-
beforeEach(() => {
|
|
178
|
-
catchUnhandledRejection()
|
|
179
|
-
})
|
|
180
|
-
afterEach(() => {
|
|
181
|
-
throwUnhandledRejection()
|
|
182
|
-
})
|
|
183
|
-
it('should throw unhandled error if lock is lost between refreshes', async () => {
|
|
184
|
-
const semaphore = new MultiSemaphore(client, 'key', 3, 2, timeoutOptions)
|
|
185
|
-
await semaphore.acquire()
|
|
186
|
-
await client.del('semaphore:key')
|
|
187
|
-
await client.zadd(
|
|
188
|
-
'semaphore:key',
|
|
189
|
-
Date.now(),
|
|
190
|
-
'aaa',
|
|
191
|
-
Date.now(),
|
|
192
|
-
'bbb',
|
|
193
|
-
Date.now(),
|
|
194
|
-
'ccc'
|
|
195
|
-
)
|
|
196
|
-
await delay(200)
|
|
197
|
-
expect(unhandledRejectionSpy).to.be.called
|
|
198
|
-
expect(unhandledRejectionSpy.firstCall.firstArg instanceof LostLockError)
|
|
199
|
-
.to.be.true
|
|
200
|
-
})
|
|
201
|
-
it('should call onLockLost callback if provided', async () => {
|
|
202
|
-
const onLockLostCallback = sinon.spy(function (this: MultiSemaphore) {
|
|
203
|
-
expect(this.isAcquired).to.be.false
|
|
204
|
-
})
|
|
205
|
-
const semaphore = new MultiSemaphore(client, 'key', 3, 2, {
|
|
206
|
-
...timeoutOptions,
|
|
207
|
-
onLockLost: onLockLostCallback
|
|
208
|
-
})
|
|
209
|
-
await semaphore.acquire()
|
|
210
|
-
expect(semaphore.isAcquired).to.be.true
|
|
211
|
-
await client.del('semaphore:key')
|
|
212
|
-
await client.zadd(
|
|
213
|
-
'semaphore:key',
|
|
214
|
-
Date.now(),
|
|
215
|
-
'aaa',
|
|
216
|
-
Date.now(),
|
|
217
|
-
'bbb',
|
|
218
|
-
Date.now(),
|
|
219
|
-
'ccc'
|
|
220
|
-
)
|
|
221
|
-
await delay(200)
|
|
222
|
-
expect(semaphore.isAcquired).to.be.false
|
|
223
|
-
expect(unhandledRejectionSpy).to.not.called
|
|
224
|
-
expect(onLockLostCallback).to.be.called
|
|
225
|
-
expect(onLockLostCallback.firstCall.firstArg instanceof LostLockError).to
|
|
226
|
-
.be.true
|
|
227
|
-
})
|
|
228
|
-
})
|
|
229
|
-
describe('reusable', () => {
|
|
230
|
-
it('autorefresh enabled', async function () {
|
|
231
|
-
this.timeout(10000)
|
|
232
|
-
const semaphore1 = new MultiSemaphore(client, 'key', 4, 2, timeoutOptions)
|
|
233
|
-
const semaphore2 = new MultiSemaphore(client, 'key', 4, 2, timeoutOptions)
|
|
234
|
-
|
|
235
|
-
await semaphore1.acquire()
|
|
236
|
-
await semaphore2.acquire()
|
|
237
|
-
await delay(300)
|
|
238
|
-
await semaphore1.release()
|
|
239
|
-
await semaphore2.release()
|
|
240
|
-
|
|
241
|
-
await delay(300)
|
|
242
|
-
|
|
243
|
-
await semaphore1.acquire()
|
|
244
|
-
await semaphore2.acquire()
|
|
245
|
-
await delay(300)
|
|
246
|
-
await semaphore1.release()
|
|
247
|
-
await semaphore2.release()
|
|
248
|
-
|
|
249
|
-
await delay(300)
|
|
250
|
-
|
|
251
|
-
await semaphore1.acquire()
|
|
252
|
-
await semaphore2.acquire()
|
|
253
|
-
await delay(300)
|
|
254
|
-
await semaphore1.release()
|
|
255
|
-
await semaphore2.release()
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
it('autorefresh disabled', async () => {
|
|
259
|
-
const noRefreshOptions = {
|
|
260
|
-
...timeoutOptions,
|
|
261
|
-
refreshInterval: 0,
|
|
262
|
-
acquireTimeout: 10
|
|
263
|
-
}
|
|
264
|
-
const semaphore1 = new MultiSemaphore(
|
|
265
|
-
client,
|
|
266
|
-
'key',
|
|
267
|
-
4,
|
|
268
|
-
2,
|
|
269
|
-
noRefreshOptions
|
|
270
|
-
)
|
|
271
|
-
const semaphore2 = new MultiSemaphore(
|
|
272
|
-
client,
|
|
273
|
-
'key',
|
|
274
|
-
4,
|
|
275
|
-
2,
|
|
276
|
-
noRefreshOptions
|
|
277
|
-
)
|
|
278
|
-
const semaphore3 = new MultiSemaphore(
|
|
279
|
-
client,
|
|
280
|
-
'key',
|
|
281
|
-
4,
|
|
282
|
-
2,
|
|
283
|
-
noRefreshOptions
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
await semaphore1.acquire()
|
|
287
|
-
await semaphore2.acquire()
|
|
288
|
-
await delay(300)
|
|
289
|
-
await semaphore1.release()
|
|
290
|
-
await semaphore2.release()
|
|
291
|
-
|
|
292
|
-
await delay(300)
|
|
293
|
-
|
|
294
|
-
// [0/2]
|
|
295
|
-
await semaphore1.acquire()
|
|
296
|
-
// [1/2]
|
|
297
|
-
await delay(80)
|
|
298
|
-
await semaphore2.acquire()
|
|
299
|
-
// [2/2]
|
|
300
|
-
await expect(semaphore3.acquire()).to.be.rejectedWith(
|
|
301
|
-
'Acquire multi-semaphore semaphore:key timeout'
|
|
302
|
-
) // rejectes after 10ms
|
|
303
|
-
|
|
304
|
-
// since semaphore1.acquire() elapsed 80ms (delay) + 10ms (semaphore3 timeout)
|
|
305
|
-
// semaphore1 will expire after 300 - 90 = 210ms
|
|
306
|
-
await delay(210)
|
|
307
|
-
|
|
308
|
-
// [1/2]
|
|
309
|
-
await semaphore3.acquire()
|
|
310
|
-
})
|
|
311
|
-
})
|
|
312
|
-
describe('Compatibility with Semaphore', () => {
|
|
313
|
-
it('should work with Semaphore', async () => {
|
|
314
|
-
const multiSemaphore1 = new MultiSemaphore(
|
|
315
|
-
client,
|
|
316
|
-
'key',
|
|
317
|
-
3,
|
|
318
|
-
2,
|
|
319
|
-
timeoutOptions
|
|
320
|
-
)
|
|
321
|
-
const multiSemaphore2 = new MultiSemaphore(
|
|
322
|
-
client,
|
|
323
|
-
'key',
|
|
324
|
-
3,
|
|
325
|
-
2,
|
|
326
|
-
timeoutOptions
|
|
327
|
-
)
|
|
328
|
-
const semaphore1 = new Semaphore(client, 'key', 3, timeoutOptions)
|
|
329
|
-
const semaphore2 = new Semaphore(client, 'key', 3, timeoutOptions)
|
|
330
|
-
await multiSemaphore1.acquire()
|
|
331
|
-
await semaphore1.acquire()
|
|
332
|
-
expect(await client.zrange('semaphore:key', 0, -1)).to.have.members([
|
|
333
|
-
multiSemaphore1.identifier + '_0',
|
|
334
|
-
multiSemaphore1.identifier + '_1',
|
|
335
|
-
semaphore1.identifier
|
|
336
|
-
])
|
|
337
|
-
await expect(multiSemaphore2.acquire()).to.be.rejectedWith(
|
|
338
|
-
'Acquire multi-semaphore semaphore:key timeout'
|
|
339
|
-
)
|
|
340
|
-
await expect(semaphore2.acquire()).to.be.rejectedWith(
|
|
341
|
-
'Acquire semaphore semaphore:key timeout'
|
|
342
|
-
)
|
|
343
|
-
await multiSemaphore1.release()
|
|
344
|
-
expect(await client.zrange('semaphore:key', 0, -1)).to.be.eql([
|
|
345
|
-
semaphore1.identifier
|
|
346
|
-
])
|
|
347
|
-
await semaphore1.release()
|
|
348
|
-
expect(await client.zcard('semaphore:key')).to.be.eql(0)
|
|
349
|
-
})
|
|
350
|
-
})
|
|
351
|
-
describe('[Node shutdown]', () => {
|
|
352
|
-
beforeEach(() => {
|
|
353
|
-
catchUnhandledRejection()
|
|
354
|
-
})
|
|
355
|
-
afterEach(async () => {
|
|
356
|
-
throwUnhandledRejection()
|
|
357
|
-
await upRedisServer(1)
|
|
358
|
-
})
|
|
359
|
-
it('should lost lock when node become alive', async function () {
|
|
360
|
-
this.timeout(60000)
|
|
361
|
-
const onLockLostCallback = sinon.spy(function (this: Semaphore) {
|
|
362
|
-
expect(this.isAcquired).to.be.false
|
|
363
|
-
})
|
|
364
|
-
const semaphore1 = new MultiSemaphore(client, 'key', 3, 2, {
|
|
365
|
-
...timeoutOptions,
|
|
366
|
-
onLockLost: onLockLostCallback
|
|
367
|
-
})
|
|
368
|
-
await semaphore1.acquire()
|
|
369
|
-
|
|
370
|
-
await downRedisServer(1)
|
|
371
|
-
console.log('SHUT DOWN')
|
|
372
|
-
|
|
373
|
-
await delay(1000)
|
|
374
|
-
|
|
375
|
-
await upRedisServer(1)
|
|
376
|
-
console.log('ONLINE')
|
|
377
|
-
|
|
378
|
-
// semaphore was expired, key was deleted in redis
|
|
379
|
-
// give refresh mechanism time to detect lock lost
|
|
380
|
-
// (includes reconnection time)
|
|
381
|
-
await delay(1000)
|
|
382
|
-
|
|
383
|
-
const data1 = await client.zrange('semaphore:key', 0, -1, 'WITHSCORES')
|
|
384
|
-
// console.log(data)
|
|
385
|
-
expect(data1).to.be.eql([])
|
|
386
|
-
|
|
387
|
-
// lock was not refreshed by semaphore1, so semaphore2 can acquire the lock
|
|
388
|
-
|
|
389
|
-
const semaphore2 = new MultiSemaphore(client, 'key', 3, 2, timeoutOptions)
|
|
390
|
-
await semaphore2.acquire()
|
|
391
|
-
const data2 = await client.zrange('semaphore:key', 0, -1, 'WITHSCORES')
|
|
392
|
-
expect(data2).to.include(semaphore2.identifier + '_0')
|
|
393
|
-
expect(data2).to.include(semaphore2.identifier + '_1')
|
|
394
|
-
|
|
395
|
-
await Promise.all([semaphore1.release(), semaphore2.release()])
|
|
396
|
-
})
|
|
397
|
-
})
|
|
398
|
-
describe('ioredis-mock support', () => {
|
|
399
|
-
it('should acquire and release semaphore', async () => {
|
|
400
|
-
const semaphore1 = new MultiSemaphore(clientMock, 'key', 3, 2)
|
|
401
|
-
const semaphore2 = new MultiSemaphore(clientMock, 'key', 3, 1)
|
|
402
|
-
expect(semaphore1.isAcquired).to.be.false
|
|
403
|
-
expect(semaphore2.isAcquired).to.be.false
|
|
404
|
-
|
|
405
|
-
await semaphore1.acquire()
|
|
406
|
-
expect(semaphore1.isAcquired).to.be.true
|
|
407
|
-
await semaphore2.acquire()
|
|
408
|
-
expect(semaphore2.isAcquired).to.be.true
|
|
409
|
-
expect(await clientMock.zrange('semaphore:key', 0, -1)).to.have.members([
|
|
410
|
-
semaphore1.identifier + '_0',
|
|
411
|
-
semaphore1.identifier + '_1',
|
|
412
|
-
semaphore2.identifier + '_0'
|
|
413
|
-
])
|
|
414
|
-
|
|
415
|
-
await semaphore1.release()
|
|
416
|
-
expect(semaphore1.isAcquired).to.be.false
|
|
417
|
-
expect(await clientMock.zrange('semaphore:key', 0, -1)).to.be.eql([
|
|
418
|
-
semaphore2.identifier + '_0'
|
|
419
|
-
])
|
|
420
|
-
await semaphore2.release()
|
|
421
|
-
expect(semaphore2.isAcquired).to.be.false
|
|
422
|
-
expect(await clientMock.zcard('semaphore:key')).to.be.eql(0)
|
|
423
|
-
})
|
|
424
|
-
})
|
|
425
|
-
})
|