Skip to content

Commit e06cd78

Browse files
authored
Merge pull request #128 from kibertoad/feat/stop-restart-refresh
Expose stopRefresh method
2 parents f74f440 + 2ca8ce6 commit e06cd78

File tree

8 files changed

+119
-10
lines changed

8 files changed

+119
-10
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,27 @@ async function doSomething() {
107107
}
108108
```
109109

110+
#### Example with temporary refresh
111+
112+
```javascript
113+
async function doSomething() {
114+
const mutex = new Mutex(redisClient, 'lockingResource', {
115+
lockTimeout: 120000,
116+
refreshInterval: 15000
117+
})
118+
const lockAcquired = await mutex.tryAcquire()
119+
if (!lockAcquired) {
120+
return
121+
}
122+
try {
123+
// critical cycle iteration
124+
} finally {
125+
// We want to let lock expire over time after operation is finished
126+
await mutex.stopRefresh()
127+
}
128+
}
129+
```
130+
110131
### Semaphore
111132

112133
> See [RedisLabs: Basic counting sempahore](https://redislabs.com/ebook/part-2-core-concepts/chapter-6-application-components-in-redis/6-3-counting-semaphores/6-3-1-building-a-basic-counting-semaphore/)

src/Lock.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export abstract class Lock {
6262
this._refreshInterval.unref()
6363
}
6464

65-
private _stopRefresh() {
65+
stopRefresh() {
6666
if (this._refreshInterval) {
6767
debug(
6868
`clear refresh interval ${this._kind} (key: ${this._key}, identifier: ${this._identifier})`
@@ -86,7 +86,7 @@ export abstract class Lock {
8686
const refreshed = await this._refresh()
8787
if (!refreshed) {
8888
this._acquired = false
89-
this._stopRefresh()
89+
this.stopRefresh()
9090
const lockLostError = new LostLockError(
9191
`Lost ${this._kind} for key ${this._key}`
9292
)
@@ -127,7 +127,7 @@ export abstract class Lock {
127127
`release ${this._kind} (key: ${this._key}, identifier: ${this._identifier})`
128128
)
129129
if (this._refreshTimeInterval > 0) {
130-
this._stopRefresh()
130+
this.stopRefresh()
131131
}
132132
if (this._acquired) {
133133
await this._release()

test/src/RedisMultiSemaphore.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ describe('MultiSemaphore', () => {
8787
const semaphore2 = new MultiSemaphore(client, 'key', 3, 1, timeoutOptions)
8888
await semaphore1.acquire()
8989
await semaphore2.acquire()
90-
await delay(100)
90+
await delay(400)
9191
expect(await client.zrange('semaphore:key', 0, -1)).to.have.members([
9292
semaphore1.identifier + '_0',
9393
semaphore1.identifier + '_1',
@@ -100,6 +100,20 @@ describe('MultiSemaphore', () => {
100100
await semaphore2.release()
101101
expect(await client.zcard('semaphore:key')).to.be.eql(0)
102102
})
103+
it('should stop refreshing lock if stopped', async () => {
104+
const semaphore1 = new MultiSemaphore(client, 'key', 3, 2, timeoutOptions)
105+
const semaphore2 = new MultiSemaphore(client, 'key', 3, 1, timeoutOptions)
106+
await semaphore1.acquire()
107+
await semaphore2.acquire()
108+
await semaphore1.stopRefresh()
109+
await delay(400)
110+
expect(await client.zrange('semaphore:key', 0, -1)).to.be.eql([
111+
semaphore2.identifier + '_0'
112+
])
113+
await semaphore2.stopRefresh()
114+
await delay(400)
115+
expect(await client.zcard('semaphore:key')).to.be.eql(0)
116+
})
103117
it('should acquire maximum LIMIT semaphores', async () => {
104118
const s = () =>
105119
new MultiSemaphore(client, 'key', 3, 1, {

test/src/RedisMutex.test.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,23 @@ describe('Mutex', () => {
7171
it('should refresh lock every refreshInterval ms until release', async () => {
7272
const mutex = new Mutex(client, 'key', timeoutOptions)
7373
await mutex.acquire()
74-
await delay(100)
74+
await delay(400)
7575
expect(await client.get('mutex:key')).to.be.eql(mutex.identifier)
7676
await mutex.release()
7777
expect(await client.get('mutex:key')).to.be.eql(null)
7878
})
79+
it('should stop refreshing lock every refreshInterval ms if stopped', async () => {
80+
const mutex = new Mutex(client, 'key', timeoutOptions)
81+
await mutex.acquire()
82+
mutex.stopRefresh()
83+
await delay(400)
84+
expect(await client.get('mutex:key')).to.be.eql(null)
85+
})
7986
it('should re-acquire lock if lock was expired between refreshes, but was not acquired by another instance', async () => {
8087
const mutex = new Mutex(client, 'key', timeoutOptions)
8188
await mutex.acquire()
8289
await client.del('mutex:key') // "expired"
83-
await delay(100)
90+
await delay(400)
8491
expect(await client.get('mutex:key')).to.be.eql(mutex.identifier)
8592
await mutex.release()
8693
expect(await client.get('mutex:key')).to.be.eql(null)

test/src/RedisSemaphore.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe('Semaphore', () => {
7777
const semaphore2 = new Semaphore(client, 'key', 2, timeoutOptions)
7878
await semaphore1.acquire()
7979
await semaphore2.acquire()
80-
await delay(100)
80+
await delay(400)
8181
expect(await client.zrange('semaphore:key', 0, -1)).to.have.members([
8282
semaphore1.identifier,
8383
semaphore2.identifier
@@ -89,6 +89,20 @@ describe('Semaphore', () => {
8989
await semaphore2.release()
9090
expect(await client.zcard('semaphore:key')).to.be.eql(0)
9191
})
92+
it('should stop refreshing lock if stopped', async () => {
93+
const semaphore1 = new Semaphore(client, 'key', 2, timeoutOptions)
94+
const semaphore2 = new Semaphore(client, 'key', 2, timeoutOptions)
95+
await semaphore1.acquire()
96+
await semaphore2.acquire()
97+
await semaphore1.stopRefresh()
98+
await delay(400)
99+
expect(await client.zrange('semaphore:key', 0, -1)).to.be.eql([
100+
semaphore2.identifier
101+
])
102+
await semaphore2.stopRefresh()
103+
await delay(400)
104+
expect(await client.zcard('semaphore:key')).to.be.eql(0)
105+
})
92106
it('should acquire maximum LIMIT semaphores', async () => {
93107
const s = () =>
94108
new Semaphore(client, 'key', 3, {

test/src/RedlockMultiSemaphore.test.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ describe('RedlockMultiSemaphore', () => {
151151
)
152152
await semaphore1.acquire()
153153
await semaphore2.acquire()
154-
await delay(100)
154+
await delay(400)
155155
await expectZRangeAllHaveMembers('semaphore:key', [
156156
semaphore1.identifier + '_0',
157157
semaphore1.identifier + '_1',
@@ -162,6 +162,30 @@ describe('RedlockMultiSemaphore', () => {
162162
await semaphore2.release()
163163
await expectZCardAllEql('semaphore:key', 0)
164164
})
165+
it('should stop refreshing lock if stopped', async () => {
166+
const semaphore1 = new RedlockMultiSemaphore(
167+
allClients,
168+
'key',
169+
3,
170+
2,
171+
timeoutOptions
172+
)
173+
const semaphore2 = new RedlockMultiSemaphore(
174+
allClients,
175+
'key',
176+
3,
177+
1,
178+
timeoutOptions
179+
)
180+
await semaphore1.acquire()
181+
await semaphore2.acquire()
182+
semaphore1.stopRefresh()
183+
await delay(400)
184+
await expectZRangeAllEql('semaphore:key', [semaphore2.identifier + '_0'])
185+
semaphore2.stopRefresh()
186+
await delay(400)
187+
await expectZCardAllEql('semaphore:key', 0)
188+
})
165189
it('should acquire maximum LIMIT semaphores', async () => {
166190
const s = () =>
167191
new RedlockMultiSemaphore(allClients, 'key', 3, 1, {

test/src/RedlockMutex.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,18 @@ describe('RedlockMutex', () => {
6363
it('should refresh lock every refreshInterval ms until release', async () => {
6464
const mutex = new RedlockMutex(allClients, 'key', timeoutOptions)
6565
await mutex.acquire()
66-
await delay(100)
66+
await delay(400)
6767
await expectGetAll('mutex:key', mutex.identifier)
6868
await mutex.release()
6969
await expectGetAll('mutex:key', null)
7070
})
71+
it('should stop refreshing if stopped', async () => {
72+
const mutex = new RedlockMutex(allClients, 'key', timeoutOptions)
73+
await mutex.acquire()
74+
mutex.stopRefresh()
75+
await delay(400)
76+
await expectGetAll('mutex:key', null)
77+
})
7178
describe('lost lock case', () => {
7279
beforeEach(() => {
7380
catchUnhandledRejection()

test/src/RedlockSemaphore.test.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ describe('RedlockSemaphore', () => {
118118
)
119119
await semaphore1.acquire()
120120
await semaphore2.acquire()
121-
await delay(100)
121+
await delay(400)
122122
await expectZRangeAllHaveMembers('semaphore:key', [
123123
semaphore1.identifier,
124124
semaphore2.identifier
@@ -128,6 +128,28 @@ describe('RedlockSemaphore', () => {
128128
await semaphore2.release()
129129
await expectZCardAllEql('semaphore:key', 0)
130130
})
131+
it('should stop refreshing lock if stopped', async () => {
132+
const semaphore1 = new RedlockSemaphore(
133+
allClients,
134+
'key',
135+
2,
136+
timeoutOptions
137+
)
138+
const semaphore2 = new RedlockSemaphore(
139+
allClients,
140+
'key',
141+
2,
142+
timeoutOptions
143+
)
144+
await semaphore1.acquire()
145+
await semaphore2.acquire()
146+
semaphore1.stopRefresh()
147+
await delay(400)
148+
await expectZRangeAllEql('semaphore:key', [semaphore2.identifier])
149+
semaphore2.stopRefresh()
150+
await delay(400)
151+
await expectZCardAllEql('semaphore:key', 0)
152+
})
131153
it('should acquire maximum LIMIT semaphores', async () => {
132154
const s = () =>
133155
new RedlockSemaphore(allClients, 'key', 3, {

0 commit comments

Comments
 (0)