Skip to content

Commit 364d1fc

Browse files
committed
feat: add forceRelease method
1 parent f73d5e8 commit 364d1fc

8 files changed

Lines changed: 77 additions & 2 deletions

File tree

packages/verrou/src/drivers/database.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ export class DatabaseStore implements LockStore {
130130
await this.#connection.table(this.#tableName).where('key', key).where('owner', owner).delete()
131131
}
132132

133+
/**
134+
* Force delete a lock
135+
*/
136+
async forceRelease(key: string) {
137+
await this.#connection.table(this.#tableName).where('key', key).delete()
138+
}
139+
133140
/**
134141
* Check if a lock exists
135142
*/

packages/verrou/src/drivers/dynamodb.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,25 @@ export class DynamoDBStore implements LockStore {
114114
* Delete a lock
115115
*/
116116
async delete(key: string, owner: string) {
117-
const currentOwner = await this.#getCurrentOwner(key)
118-
if (currentOwner !== owner) throw new E_RELEASE_NOT_OWNED()
117+
const command = new DeleteItemCommand({
118+
TableName: this.#tableName,
119+
Key: { key: { S: key } },
120+
ConditionExpression: '#owner = :owner',
121+
ExpressionAttributeNames: { '#owner': 'owner' },
122+
ExpressionAttributeValues: { ':owner': { S: owner } },
123+
})
124+
125+
try {
126+
await this.#client.send(command)
127+
} catch (err) {
128+
throw new E_RELEASE_NOT_OWNED()
129+
}
130+
}
119131

132+
/**
133+
* Force delete a lock
134+
*/
135+
async forceRelease(key: string) {
120136
const command = new DeleteItemCommand({
121137
TableName: this.#tableName,
122138
Key: { key: { S: key } },

packages/verrou/src/drivers/memory.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ export class MemoryStore implements LockStore {
8080
mutex.releaser()
8181
}
8282

83+
/**
84+
* Force delete a lock
85+
*/
86+
async forceRelease(key: string) {
87+
const lock = this.#locks.get(key)
88+
if (!lock) return
89+
90+
lock.releaser?.()
91+
}
92+
8393
/**
8494
* Check if a lock exists
8595
*/

packages/verrou/src/drivers/redis.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ export class RedisStore implements LockStore {
4444
if (result === 0) throw new E_RELEASE_NOT_OWNED()
4545
}
4646

47+
/**
48+
* Force delete a lock
49+
*/
50+
async forceRelease(key: string) {
51+
await this.#connection.del(key)
52+
}
53+
4754
/**
4855
* Check if a lock exists
4956
*/

packages/verrou/src/lock.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,27 @@ export class Lock {
7575
}
7676
}
7777

78+
/**
79+
* Force release the lock
80+
*/
81+
async forceRelease() {
82+
await this.lockStore.forceRelease(this.key)
83+
}
84+
7885
/**
7986
* Release the lock
8087
*/
8188
async release() {
8289
await this.lockStore.delete(this.key, this.#owner)
8390
}
8491

92+
/**
93+
* Returns true if the lock is expired
94+
*/
95+
async isExpired() {
96+
return false
97+
}
98+
8599
/**
86100
* Returns true if the lock is currently locked
87101
*/

packages/verrou/src/types/main.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ export interface LockStore {
5959
*/
6060
delete(key: string, owner: string): Promise<void>
6161

62+
/**
63+
* Force delete the lock from the store
64+
*/
65+
forceRelease(key: string): Promise<void>
66+
6267
/**
6368
* Check if the lock exists
6469
*/

packages/verrou/test_helpers/driver_test_suite.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,16 @@ export function registerStoreTestSuite<T extends { new (options: any): LockStore
292292

293293
assert.isTrue(await lock.isLocked())
294294
})
295+
296+
test('forceRelease allows to release a lock that is not acquired by you', async ({ assert }) => {
297+
const provider = new LockFactory(new store(config))
298+
const lock = provider.createLock('foo')
299+
const lock2 = provider.createLock('foo')
300+
301+
await lock.acquire()
302+
303+
await lock2.forceRelease()
304+
305+
assert.isFalse(await lock.isLocked())
306+
})
295307
}

packages/verrou/test_helpers/null_store.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ export class NullStore implements LockStore {
1313
return true
1414
}
1515

16+
async forceRelease(_key: string): Promise<void> {
17+
return
18+
}
19+
1620
async extend(_key: string, _duration: Duration): Promise<void> {
1721
return
1822
}

0 commit comments

Comments
 (0)