murlock 5.0.0 → 5.1.0
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 +5 -0
- package/dist/als/als-manager.d.ts +4 -17
- package/dist/als/als-manager.js +4 -49
- package/dist/als/als-manager.js.map +1 -1
- package/dist/als/als.module.js +1 -3
- package/dist/als/als.module.js.map +1 -1
- package/dist/als/als.service.d.ts +7 -5
- package/dist/als/als.service.js +4 -10
- package/dist/als/als.service.js.map +1 -1
- package/dist/decorators/murlock.decorator.js +6 -5
- package/dist/decorators/murlock.decorator.js.map +1 -1
- package/dist/exceptions/murlock-redis.exception.d.ts +2 -1
- package/dist/exceptions/murlock-redis.exception.js +2 -1
- package/dist/exceptions/murlock-redis.exception.js.map +1 -1
- package/dist/interfaces/murlock-options.interface.d.ts +3 -0
- package/dist/murlock.service.d.ts +6 -0
- package/dist/murlock.service.js +169 -43
- package/dist/murlock.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -166,6 +166,8 @@ async anotherFunction(user: User): Promise<void> {
|
|
|
166
166
|
|
|
167
167
|
When using `@MurLock` with other decorators (such as `@Transactional` from typeorm-transactional), you may encounter issues with parameter name extraction if the other decorator wraps the method before `@MurLock` is applied.
|
|
168
168
|
|
|
169
|
+
> **Note on minification / transpilation:** MurLock derives parameter names by parsing the method signature at runtime. Aggressive minifiers/bundlers (terser, esbuild, swc) may rename or strip parameter names, in which case **name-based** key params (e.g. `'userId'`, `'user.id'`) cannot be resolved and the decorator throws `Parameter ... not found`. If you minify your server build, either use **numeric index** key params (e.g. `'0'`, `'0.id'`) — which are always stable — or declare names explicitly with `@SetParamNames` (below).
|
|
170
|
+
|
|
169
171
|
### Problem
|
|
170
172
|
|
|
171
173
|
TypeScript decorators execute in bottom-up order. If another decorator wraps the method before `@MurLock`, the parameter names cannot be extracted from the wrapped function:
|
|
@@ -519,6 +521,9 @@ Here are the customizable options for `MurLockModule`, allowing you to tailor it
|
|
|
519
521
|
- **autoExtend (optional)**: When set to `true`, MurLock keeps the lock alive while the wrapped operation is still running by periodically extending its TTL (a "watchdog"). This prevents the lock from expiring mid-execution when an operation runs longer than `releaseTime`, which would otherwise let another instance acquire the same lock and break mutual exclusion. Defaults to `false`.
|
|
520
522
|
- **extendInterval (optional)**: Interval in milliseconds between watchdog TTL extensions. Only used when `autoExtend` is `true`. Defaults to one third of the lock's `releaseTime` (`Math.floor(releaseTime / 3)`), guaranteeing at least two refresh attempts before the TTL would expire.
|
|
521
523
|
- **onRedisError (optional)**: Callback `(error: Error) => void` invoked when the Redis client emits a runtime `error` event. Use it to plug in custom alerting or fail-fast behavior.
|
|
524
|
+
- **reentrant (optional)**: When set to `true`, locks become reentrant within the same async context: a method that locks a key and (directly or transitively) calls another method locking the **same** key reuses the existing lock instead of deadlocking. The underlying Redis lock is acquired once (outermost entry) and released when the outermost call completes. Defaults to `false`.
|
|
525
|
+
- **encodeKeyParts (optional)**: When set to `true`, each lock-key part derived from method arguments is URL-encoded so values containing the `:` separator cannot collide (e.g. `a:b` + `c` vs `a` + `b:c`). Changes the generated key format, so it is opt-in to avoid breaking existing keys across a rolling deploy. Defaults to `false`.
|
|
526
|
+
- **jitter (optional)**: When set to `true`, retry back-off delays use equal jitter (`delay/2 + random*delay/2`) to avoid a thundering herd when many workers wait on the same lock. Applies to both attempt-based and blocking modes. Defaults to `false`.
|
|
522
527
|
|
|
523
528
|
### MurLockService
|
|
524
529
|
|
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
3
|
-
export declare class AsyncStorageManager<T>
|
|
3
|
+
export declare class AsyncStorageManager<T> {
|
|
4
4
|
private readonly asyncLocalStorage;
|
|
5
|
-
constructor(asyncLocalStorage?: AsyncLocalStorage<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
runWithNewContext<R, TArgs extends any[]>(fn: (...args: TArgs) => R, ...args: TArgs): R;
|
|
9
|
-
set(key: string, value: T): this;
|
|
10
|
-
get(key: string): T | undefined;
|
|
11
|
-
clear(): void;
|
|
12
|
-
delete(key: string): boolean;
|
|
13
|
-
forEach(callbackfn: (value: T, key: string, map: Map<string, T>) => void, thisArg?: any): void;
|
|
14
|
-
has(key: string): boolean;
|
|
15
|
-
get size(): number;
|
|
16
|
-
entries(): IterableIterator<[string, T]>;
|
|
17
|
-
keys(): IterableIterator<string>;
|
|
18
|
-
values(): IterableIterator<T>;
|
|
19
|
-
[Symbol.iterator](): IterableIterator<[string, T]>;
|
|
20
|
-
[Symbol.toStringTag]: string;
|
|
5
|
+
constructor(asyncLocalStorage?: AsyncLocalStorage<T>);
|
|
6
|
+
run<R>(store: T, fn: () => R): R;
|
|
7
|
+
getStore(): T | undefined;
|
|
21
8
|
}
|
package/dist/als/als-manager.js
CHANGED
|
@@ -8,67 +8,22 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
|
-
var _a;
|
|
12
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
12
|
exports.AsyncStorageManager = void 0;
|
|
14
13
|
const common_1 = require("@nestjs/common");
|
|
15
14
|
const async_hooks_1 = require("async_hooks");
|
|
16
|
-
const exceptions_1 = require("../exceptions");
|
|
17
15
|
let AsyncStorageManager = class AsyncStorageManager {
|
|
18
16
|
constructor(asyncLocalStorage = new async_hooks_1.AsyncLocalStorage()) {
|
|
19
17
|
this.asyncLocalStorage = asyncLocalStorage;
|
|
20
|
-
this[_a] = '[object AsyncContext]';
|
|
21
18
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (!store) {
|
|
25
|
-
throw new exceptions_1.AsyncStorageManagerException('No active store found');
|
|
26
|
-
}
|
|
27
|
-
return store;
|
|
28
|
-
}
|
|
29
|
-
register() {
|
|
30
|
-
this.asyncLocalStorage.enterWith(new Map());
|
|
31
|
-
}
|
|
32
|
-
runWithNewContext(fn, ...args) {
|
|
33
|
-
return this.asyncLocalStorage.run(new Map(), fn, ...args);
|
|
34
|
-
}
|
|
35
|
-
set(key, value) {
|
|
36
|
-
this.getStore().set(key, value);
|
|
37
|
-
return this;
|
|
38
|
-
}
|
|
39
|
-
get(key) {
|
|
40
|
-
return this.getStore().get(key);
|
|
41
|
-
}
|
|
42
|
-
clear() {
|
|
43
|
-
return this.getStore().clear();
|
|
44
|
-
}
|
|
45
|
-
delete(key) {
|
|
46
|
-
return this.getStore().delete(key);
|
|
19
|
+
run(store, fn) {
|
|
20
|
+
return this.asyncLocalStorage.run(store, fn);
|
|
47
21
|
}
|
|
48
|
-
|
|
49
|
-
return this.getStore()
|
|
50
|
-
}
|
|
51
|
-
has(key) {
|
|
52
|
-
return this.getStore().has(key);
|
|
53
|
-
}
|
|
54
|
-
get size() {
|
|
55
|
-
return this.getStore().size;
|
|
56
|
-
}
|
|
57
|
-
entries() {
|
|
58
|
-
return this.getStore().entries();
|
|
59
|
-
}
|
|
60
|
-
keys() {
|
|
61
|
-
return this.getStore().keys();
|
|
62
|
-
}
|
|
63
|
-
values() {
|
|
64
|
-
return this.getStore().values();
|
|
65
|
-
}
|
|
66
|
-
[Symbol.iterator]() {
|
|
67
|
-
return this.getStore()[Symbol.iterator]();
|
|
22
|
+
getStore() {
|
|
23
|
+
return this.asyncLocalStorage.getStore();
|
|
68
24
|
}
|
|
69
25
|
};
|
|
70
26
|
exports.AsyncStorageManager = AsyncStorageManager;
|
|
71
|
-
_a = Symbol.toStringTag;
|
|
72
27
|
exports.AsyncStorageManager = AsyncStorageManager = __decorate([
|
|
73
28
|
(0, common_1.Injectable)(),
|
|
74
29
|
__metadata("design:paramtypes", [Object])
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"als-manager.js","sourceRoot":"","sources":["../../lib/als/als-manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"als-manager.js","sourceRoot":"","sources":["../../lib/als/als-manager.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6CAAgD;AAQzC,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,YACmB,oBAAoB,IAAI,+BAAiB,EAAK;QAA9C,sBAAiB,GAAjB,iBAAiB,CAA6B;IAC9D,CAAC;IAGJ,GAAG,CAAI,KAAQ,EAAE,EAAW;QAC1B,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC/C,CAAC;IAGD,QAAQ;QACN,OAAO,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,CAAC;IAC3C,CAAC;CACF,CAAA;AAdY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;;GACA,mBAAmB,CAc/B"}
|
package/dist/als/als.module.js
CHANGED
|
@@ -20,9 +20,7 @@ exports.AsyncStorageManagerModule = AsyncStorageManagerModule = __decorate([
|
|
|
20
20
|
providers: [
|
|
21
21
|
{
|
|
22
22
|
provide: als_manager_1.AsyncStorageManager,
|
|
23
|
-
useFactory: () =>
|
|
24
|
-
return new als_manager_1.AsyncStorageManager(new async_hooks_1.AsyncLocalStorage());
|
|
25
|
-
},
|
|
23
|
+
useFactory: () => new als_manager_1.AsyncStorageManager(new async_hooks_1.AsyncLocalStorage()),
|
|
26
24
|
},
|
|
27
25
|
als_service_1.AsyncStorageService,
|
|
28
26
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"als.module.js","sourceRoot":"","sources":["../../lib/als/als.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,+CAAoD;AACpD,+
|
|
1
|
+
{"version":3,"file":"als.module.js","sourceRoot":"","sources":["../../lib/als/als.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,+CAAoD;AACpD,+CAAoE;AACpE,6CAAgD;AAgBzC,IAAM,yBAAyB,GAA/B,MAAM,yBAAyB;CAAG,CAAA;AAA5B,8DAAyB;oCAAzB,yBAAyB;IAdrC,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,iCAAmB;gBAC5B,UAAU,EAAE,GAAG,EAAE,CACf,IAAI,iCAAmB,CACrB,IAAI,+BAAiB,EAAkB,CACxC;aACJ;YACD,iCAAmB;SACpB;QACD,OAAO,EAAE,CAAC,iCAAmB,CAAC;KAC/B,CAAC;GACW,yBAAyB,CAAG"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { AsyncStorageManager } from './als-manager';
|
|
2
|
+
export interface MurLockContext {
|
|
3
|
+
clientId: string;
|
|
4
|
+
holds: Map<string, number>;
|
|
5
|
+
}
|
|
2
6
|
export declare class AsyncStorageService {
|
|
3
7
|
private readonly asyncStorageManager;
|
|
4
|
-
constructor(asyncStorageManager: AsyncStorageManager<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
get(key: string): string;
|
|
8
|
-
setClientID(key: string, value: string): void;
|
|
8
|
+
constructor(asyncStorageManager: AsyncStorageManager<MurLockContext>);
|
|
9
|
+
run<R>(store: MurLockContext, fn: () => R): R;
|
|
10
|
+
getContext(): MurLockContext | undefined;
|
|
9
11
|
}
|
package/dist/als/als.service.js
CHANGED
|
@@ -16,17 +16,11 @@ let AsyncStorageService = class AsyncStorageService {
|
|
|
16
16
|
constructor(asyncStorageManager) {
|
|
17
17
|
this.asyncStorageManager = asyncStorageManager;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
return this.asyncStorageManager.
|
|
19
|
+
run(store, fn) {
|
|
20
|
+
return this.asyncStorageManager.run(store, fn);
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
this.asyncStorageManager.
|
|
24
|
-
}
|
|
25
|
-
get(key) {
|
|
26
|
-
return this.asyncStorageManager.get(key);
|
|
27
|
-
}
|
|
28
|
-
setClientID(key, value) {
|
|
29
|
-
this.asyncStorageManager.set(key, value);
|
|
22
|
+
getContext() {
|
|
23
|
+
return this.asyncStorageManager.getStore();
|
|
30
24
|
}
|
|
31
25
|
};
|
|
32
26
|
exports.AsyncStorageService = AsyncStorageService;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"als.service.js","sourceRoot":"","sources":["../../lib/als/als.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,+CAAoD;
|
|
1
|
+
{"version":3,"file":"als.service.js","sourceRoot":"","sources":["../../lib/als/als.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,+CAAoD;AAa7C,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,YACmB,mBAAwD;QAAxD,wBAAmB,GAAnB,mBAAmB,CAAqC;IACxE,CAAC;IAGJ,GAAG,CAAI,KAAqB,EAAE,EAAW;QACvC,OAAO,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACjD,CAAC;IAGD,UAAU;QACR,OAAO,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,CAAC;IAC7C,CAAC;CACF,CAAA;AAdY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;qCAG6B,iCAAmB;GAFhD,mBAAmB,CAc/B"}
|
|
@@ -121,7 +121,8 @@ function MurLock(releaseTime, waitOrKeyParam, ...keyParams) {
|
|
|
121
121
|
}
|
|
122
122
|
return methodParameterNames.indexOf(source);
|
|
123
123
|
}
|
|
124
|
-
function constructLockKey(args, lockKeyPrefix = 'default') {
|
|
124
|
+
function constructLockKey(args, lockKeyPrefix = 'default', encodeKeyParts = false) {
|
|
125
|
+
const encodePart = (value) => encodeKeyParts ? encodeURIComponent(String(value)) : value;
|
|
125
126
|
const lockKeyElements = [];
|
|
126
127
|
if (lockKeyPrefix != 'custom') {
|
|
127
128
|
lockKeyElements.push(target.constructor.name);
|
|
@@ -145,11 +146,11 @@ function MurLock(releaseTime, waitOrKeyParam, ...keyParams) {
|
|
|
145
146
|
typeof parameterValue === 'object' &&
|
|
146
147
|
parameterValue !== null &&
|
|
147
148
|
path in parameterValue) {
|
|
148
|
-
return parameterValue[path];
|
|
149
|
+
return encodePart(parameterValue[path]);
|
|
149
150
|
}
|
|
150
|
-
return parameterValue instanceof Object
|
|
151
|
+
return encodePart(parameterValue instanceof Object
|
|
151
152
|
? parameterValue.toString()
|
|
152
|
-
: parameterValue;
|
|
153
|
+
: parameterValue);
|
|
153
154
|
}
|
|
154
155
|
if (lockKeyPrefix == 'custom') {
|
|
155
156
|
return source;
|
|
@@ -161,7 +162,7 @@ function MurLock(releaseTime, waitOrKeyParam, ...keyParams) {
|
|
|
161
162
|
const wrapped = function (...args) {
|
|
162
163
|
return __awaiter(this, void 0, void 0, function* () {
|
|
163
164
|
const murLockService = this.murlockServiceDecorator;
|
|
164
|
-
const lockKey = constructLockKey(args, murLockService.options.lockKeyPrefix);
|
|
165
|
+
const lockKey = constructLockKey(args, murLockService.options.lockKeyPrefix, murLockService.options.encodeKeyParts);
|
|
165
166
|
if (!murLockService) {
|
|
166
167
|
throw new exceptions_1.MurLockException('MurLockService is not available.');
|
|
167
168
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"murlock.decorator.js","sourceRoot":"","sources":["../../lib/decorators/murlock.decorator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"murlock.decorator.js","sourceRoot":"","sources":["../../lib/decorators/murlock.decorator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MurLockRedisException = void 0;
|
|
4
|
-
|
|
4
|
+
const murlock_exception_1 = require("./murlock.exception");
|
|
5
|
+
class MurLockRedisException extends murlock_exception_1.MurLockException {
|
|
5
6
|
constructor(message) {
|
|
6
7
|
super(message);
|
|
7
8
|
this.name = "MurLockRedisException";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"murlock-redis.exception.js","sourceRoot":"","sources":["../../lib/exceptions/murlock-redis.exception.ts"],"names":[],"mappings":";;;AAAA,MAAa,qBAAsB,SAAQ,
|
|
1
|
+
{"version":3,"file":"murlock-redis.exception.js","sourceRoot":"","sources":["../../lib/exceptions/murlock-redis.exception.ts"],"names":[],"mappings":";;;AAAA,2DAAuD;AAQvD,MAAa,qBAAsB,SAAQ,oCAAgB;IACzD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AALD,sDAKC"}
|
|
@@ -11,6 +11,9 @@ export interface MurLockModuleOptions {
|
|
|
11
11
|
autoExtend?: boolean;
|
|
12
12
|
extendInterval?: number;
|
|
13
13
|
onRedisError?: (error: Error) => void;
|
|
14
|
+
reentrant?: boolean;
|
|
15
|
+
encodeKeyParts?: boolean;
|
|
16
|
+
jitter?: boolean;
|
|
14
17
|
}
|
|
15
18
|
export interface MurLockModuleAsyncOptions {
|
|
16
19
|
imports?: any[];
|
|
@@ -9,11 +9,16 @@ export declare class MurLockService implements OnModuleInit, OnApplicationShutdo
|
|
|
9
9
|
private lockScript;
|
|
10
10
|
private unlockScript;
|
|
11
11
|
private extendScript;
|
|
12
|
+
private readonly scriptShas;
|
|
12
13
|
constructor(options: MurLockModuleOptions, asyncStorageService: AsyncStorageService);
|
|
13
14
|
onModuleInit(): Promise<void>;
|
|
14
15
|
onApplicationShutdown(signal?: string): Promise<void>;
|
|
16
|
+
private validateOptions;
|
|
15
17
|
private sleep;
|
|
18
|
+
private withJitter;
|
|
16
19
|
private log;
|
|
20
|
+
private loadScripts;
|
|
21
|
+
private evalScript;
|
|
17
22
|
private lock;
|
|
18
23
|
private unlock;
|
|
19
24
|
private extendLock;
|
|
@@ -22,6 +27,7 @@ export declare class MurLockService implements OnModuleInit, OnApplicationShutdo
|
|
|
22
27
|
private releaseLock;
|
|
23
28
|
runWithLock<R>(lockKey: string, releaseTime: number, fn: () => Promise<R>): Promise<R>;
|
|
24
29
|
runWithLock<R>(lockKey: string, releaseTime: number, wait: number | ((retries: number) => number), fn: () => Promise<R>): Promise<R>;
|
|
30
|
+
private runReentrant;
|
|
25
31
|
private registerRedisErrorHandlers;
|
|
26
32
|
private blockingLock;
|
|
27
33
|
}
|
package/dist/murlock.service.js
CHANGED
|
@@ -35,9 +35,11 @@ let MurLockService = MurLockService_1 = class MurLockService {
|
|
|
35
35
|
this.options = options;
|
|
36
36
|
this.asyncStorageService = asyncStorageService;
|
|
37
37
|
this.logger = new common_1.Logger(MurLockService_1.name);
|
|
38
|
+
this.scriptShas = {};
|
|
38
39
|
}
|
|
39
40
|
onModuleInit() {
|
|
40
41
|
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
this.validateOptions();
|
|
41
43
|
try {
|
|
42
44
|
this.lockScript = yield (0, promises_1.readFile)((0, path_1.join)(__dirname, './lua/lock.lua'), 'utf8');
|
|
43
45
|
this.unlockScript = yield (0, promises_1.readFile)((0, path_1.join)(__dirname, './lua/unlock.lua'), 'utf8');
|
|
@@ -54,6 +56,7 @@ let MurLockService = MurLockService_1 = class MurLockService {
|
|
|
54
56
|
this.registerRedisErrorHandlers();
|
|
55
57
|
try {
|
|
56
58
|
yield this.redisClient.connect();
|
|
59
|
+
yield this.loadScripts();
|
|
57
60
|
}
|
|
58
61
|
catch (error) {
|
|
59
62
|
this.log('error', `Failed to connect to Redis: ${error.message}`);
|
|
@@ -71,9 +74,28 @@ let MurLockService = MurLockService_1 = class MurLockService {
|
|
|
71
74
|
}
|
|
72
75
|
});
|
|
73
76
|
}
|
|
77
|
+
validateOptions() {
|
|
78
|
+
const { maxAttempts, wait, extendInterval } = this.options;
|
|
79
|
+
if (!Number.isFinite(maxAttempts) || maxAttempts < 1) {
|
|
80
|
+
throw new exceptions_1.MurLockException(`Invalid MurLock option 'maxAttempts': ${maxAttempts} (must be an integer >= 1).`);
|
|
81
|
+
}
|
|
82
|
+
if (!Number.isFinite(wait) || wait < 0) {
|
|
83
|
+
throw new exceptions_1.MurLockException(`Invalid MurLock option 'wait': ${wait} (must be a number >= 0).`);
|
|
84
|
+
}
|
|
85
|
+
if (extendInterval !== undefined &&
|
|
86
|
+
(!Number.isFinite(extendInterval) || extendInterval <= 0)) {
|
|
87
|
+
throw new exceptions_1.MurLockException(`Invalid MurLock option 'extendInterval': ${extendInterval} (must be a number > 0).`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
74
90
|
sleep(ms) {
|
|
75
91
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
76
92
|
}
|
|
93
|
+
withJitter(delay) {
|
|
94
|
+
if (!this.options.jitter) {
|
|
95
|
+
return delay;
|
|
96
|
+
}
|
|
97
|
+
return Math.floor(delay / 2 + Math.random() * (delay / 2));
|
|
98
|
+
}
|
|
77
99
|
log(level, message, context) {
|
|
78
100
|
if (this.options.logLevel === 'none') {
|
|
79
101
|
return;
|
|
@@ -88,6 +110,76 @@ let MurLockService = MurLockService_1 = class MurLockService {
|
|
|
88
110
|
this.logger[level](message, context);
|
|
89
111
|
}
|
|
90
112
|
}
|
|
113
|
+
loadScripts() {
|
|
114
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
115
|
+
try {
|
|
116
|
+
this.scriptShas.lock = (yield this.redisClient.sendCommand([
|
|
117
|
+
'SCRIPT',
|
|
118
|
+
'LOAD',
|
|
119
|
+
this.lockScript,
|
|
120
|
+
]));
|
|
121
|
+
this.scriptShas.unlock = (yield this.redisClient.sendCommand([
|
|
122
|
+
'SCRIPT',
|
|
123
|
+
'LOAD',
|
|
124
|
+
this.unlockScript,
|
|
125
|
+
]));
|
|
126
|
+
this.scriptShas.extend = (yield this.redisClient.sendCommand([
|
|
127
|
+
'SCRIPT',
|
|
128
|
+
'LOAD',
|
|
129
|
+
this.extendScript,
|
|
130
|
+
]));
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
this.log('warn', `MurLock could not preload Lua scripts (will fall back to EVAL): ${error.message}`);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
evalScript(name, script, keys, args) {
|
|
138
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
139
|
+
const numkeys = keys.length.toString();
|
|
140
|
+
const sha = this.scriptShas[name];
|
|
141
|
+
if (sha) {
|
|
142
|
+
try {
|
|
143
|
+
return yield this.redisClient.sendCommand([
|
|
144
|
+
'EVALSHA',
|
|
145
|
+
sha,
|
|
146
|
+
numkeys,
|
|
147
|
+
...keys,
|
|
148
|
+
...args,
|
|
149
|
+
]);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
if (!/NOSCRIPT/i.test(String(error === null || error === void 0 ? void 0 : error.message))) {
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const fresh = (yield this.redisClient.sendCommand([
|
|
157
|
+
'SCRIPT',
|
|
158
|
+
'LOAD',
|
|
159
|
+
script,
|
|
160
|
+
]));
|
|
161
|
+
this.scriptShas[name] = fresh;
|
|
162
|
+
return yield this.redisClient.sendCommand([
|
|
163
|
+
'EVALSHA',
|
|
164
|
+
fresh,
|
|
165
|
+
numkeys,
|
|
166
|
+
...keys,
|
|
167
|
+
...args,
|
|
168
|
+
]);
|
|
169
|
+
}
|
|
170
|
+
catch (_a) {
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return yield this.redisClient.sendCommand([
|
|
175
|
+
'EVAL',
|
|
176
|
+
script,
|
|
177
|
+
numkeys,
|
|
178
|
+
...keys,
|
|
179
|
+
...args,
|
|
180
|
+
]);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
91
183
|
lock(lockKey, releaseTime, clientId, wait) {
|
|
92
184
|
return __awaiter(this, void 0, void 0, function* () {
|
|
93
185
|
this.log('debug', `MurLock Client ID is ${clientId}`);
|
|
@@ -99,32 +191,29 @@ let MurLockService = MurLockService_1 = class MurLockService {
|
|
|
99
191
|
throw new exceptions_1.MurLockException(`Failed to obtain lock for key ${lockKey} after ${this.options.maxAttempts} attempts.`);
|
|
100
192
|
}
|
|
101
193
|
try {
|
|
102
|
-
const isLockSuccessful = yield this.
|
|
103
|
-
'EVAL',
|
|
104
|
-
this.lockScript,
|
|
105
|
-
'1',
|
|
106
|
-
lockKey,
|
|
107
|
-
clientId,
|
|
108
|
-
releaseTime.toString(),
|
|
109
|
-
]);
|
|
194
|
+
const isLockSuccessful = yield this.evalScript('lock', this.lockScript, [lockKey], [clientId, releaseTime.toString()]);
|
|
110
195
|
if (isLockSuccessful === 1) {
|
|
111
196
|
this.log('log', `Successfully obtained lock for key ${lockKey}`);
|
|
112
197
|
return true;
|
|
113
198
|
}
|
|
114
199
|
else {
|
|
115
|
-
const
|
|
200
|
+
const baseDelay = wait
|
|
116
201
|
? typeof wait === 'function'
|
|
117
202
|
? wait(this.options.maxAttempts - attemptsRemaining + 1)
|
|
118
203
|
: wait
|
|
119
204
|
: this.options.wait *
|
|
120
205
|
(this.options.maxAttempts - attemptsRemaining + 1);
|
|
206
|
+
const delay = this.withJitter(baseDelay);
|
|
121
207
|
this.log('warn', `Failed to obtain lock for key ${lockKey}, retrying in ${delay} ms...`);
|
|
122
208
|
yield this.sleep(delay);
|
|
123
209
|
return attemptLock(attemptsRemaining - 1);
|
|
124
210
|
}
|
|
125
211
|
}
|
|
126
212
|
catch (error) {
|
|
127
|
-
|
|
213
|
+
if (error instanceof exceptions_1.MurLockException) {
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
throw new exceptions_1.MurLockRedisException(`Unexpected error when trying to obtain lock for key ${lockKey}: ${error.message}`);
|
|
128
217
|
}
|
|
129
218
|
});
|
|
130
219
|
return attemptLock(this.options.maxAttempts);
|
|
@@ -132,13 +221,7 @@ let MurLockService = MurLockService_1 = class MurLockService {
|
|
|
132
221
|
}
|
|
133
222
|
unlock(lockKey, clientId) {
|
|
134
223
|
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
-
const result = yield this.
|
|
136
|
-
'EVAL',
|
|
137
|
-
this.unlockScript,
|
|
138
|
-
'1',
|
|
139
|
-
lockKey,
|
|
140
|
-
clientId,
|
|
141
|
-
]);
|
|
224
|
+
const result = yield this.evalScript('unlock', this.unlockScript, [lockKey], [clientId]);
|
|
142
225
|
if (result === 0) {
|
|
143
226
|
if (!this.options.ignoreUnlockFail) {
|
|
144
227
|
throw new exceptions_1.MurLockException(`Failed to release lock for key ${lockKey}`);
|
|
@@ -151,14 +234,7 @@ let MurLockService = MurLockService_1 = class MurLockService {
|
|
|
151
234
|
}
|
|
152
235
|
extendLock(lockKey, clientId, releaseTime) {
|
|
153
236
|
return __awaiter(this, void 0, void 0, function* () {
|
|
154
|
-
const result = yield this.
|
|
155
|
-
'EVAL',
|
|
156
|
-
this.extendScript,
|
|
157
|
-
'1',
|
|
158
|
-
lockKey,
|
|
159
|
-
clientId,
|
|
160
|
-
releaseTime.toString(),
|
|
161
|
-
]);
|
|
237
|
+
const result = yield this.evalScript('extend', this.extendScript, [lockKey], [clientId, releaseTime.toString()]);
|
|
162
238
|
return result === 1;
|
|
163
239
|
});
|
|
164
240
|
}
|
|
@@ -196,6 +272,9 @@ let MurLockService = MurLockService_1 = class MurLockService {
|
|
|
196
272
|
isLockSuccessful = yield this.lock(lockKey, releaseTime, clientId, wait);
|
|
197
273
|
}
|
|
198
274
|
catch (error) {
|
|
275
|
+
if (error instanceof exceptions_1.MurLockException) {
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
199
278
|
throw new exceptions_1.MurLockException(`Failed to acquire lock for key ${lockKey}: ${error.message}`);
|
|
200
279
|
}
|
|
201
280
|
if (!isLockSuccessful) {
|
|
@@ -209,6 +288,9 @@ let MurLockService = MurLockService_1 = class MurLockService {
|
|
|
209
288
|
yield this.unlock(lockKey, clientId);
|
|
210
289
|
}
|
|
211
290
|
catch (error) {
|
|
291
|
+
if (error instanceof exceptions_1.MurLockException) {
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
212
294
|
throw new exceptions_1.MurLockException(`Failed to release lock for key ${lockKey}: ${error.message}`);
|
|
213
295
|
}
|
|
214
296
|
});
|
|
@@ -224,17 +306,67 @@ let MurLockService = MurLockService_1 = class MurLockService {
|
|
|
224
306
|
wait = waitOrFn;
|
|
225
307
|
operation = fn;
|
|
226
308
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
309
|
+
if (!Number.isFinite(releaseTime) || releaseTime <= 0) {
|
|
310
|
+
throw new exceptions_1.MurLockException(`Invalid releaseTime for key ${lockKey}: ${releaseTime} (must be > 0).`);
|
|
311
|
+
}
|
|
312
|
+
if (!this.options.reentrant) {
|
|
313
|
+
const clientId = (0, utils_1.generateUuid)();
|
|
314
|
+
yield this.acquireLock(lockKey, clientId, releaseTime, wait);
|
|
315
|
+
const stopWatchdog = this.startWatchdog(lockKey, clientId, releaseTime);
|
|
316
|
+
try {
|
|
317
|
+
return yield operation();
|
|
318
|
+
}
|
|
319
|
+
finally {
|
|
320
|
+
stopWatchdog();
|
|
321
|
+
yield this.releaseLock(lockKey, clientId);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const existing = this.asyncStorageService.getContext();
|
|
325
|
+
if (existing) {
|
|
326
|
+
return this.runReentrant(existing, lockKey, releaseTime, wait, operation);
|
|
327
|
+
}
|
|
328
|
+
const context = {
|
|
329
|
+
clientId: (0, utils_1.generateUuid)(),
|
|
330
|
+
holds: new Map(),
|
|
331
|
+
};
|
|
332
|
+
return this.asyncStorageService.run(context, () => this.runReentrant(context, lockKey, releaseTime, wait, operation));
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
runReentrant(context, lockKey, releaseTime, wait, operation) {
|
|
336
|
+
var _a, _b, _c;
|
|
337
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
338
|
+
const depth = (_a = context.holds.get(lockKey)) !== null && _a !== void 0 ? _a : 0;
|
|
339
|
+
if (depth > 0) {
|
|
340
|
+
context.holds.set(lockKey, depth + 1);
|
|
341
|
+
try {
|
|
342
|
+
return yield operation();
|
|
343
|
+
}
|
|
344
|
+
finally {
|
|
345
|
+
const current = (_b = context.holds.get(lockKey)) !== null && _b !== void 0 ? _b : 1;
|
|
346
|
+
if (current <= 1) {
|
|
347
|
+
context.holds.delete(lockKey);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
context.holds.set(lockKey, current - 1);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
yield this.acquireLock(lockKey, context.clientId, releaseTime, wait);
|
|
355
|
+
context.holds.set(lockKey, 1);
|
|
356
|
+
const stopWatchdog = this.startWatchdog(lockKey, context.clientId, releaseTime);
|
|
232
357
|
try {
|
|
233
358
|
return yield operation();
|
|
234
359
|
}
|
|
235
360
|
finally {
|
|
236
|
-
|
|
237
|
-
|
|
361
|
+
const current = (_c = context.holds.get(lockKey)) !== null && _c !== void 0 ? _c : 1;
|
|
362
|
+
if (current <= 1) {
|
|
363
|
+
context.holds.delete(lockKey);
|
|
364
|
+
stopWatchdog();
|
|
365
|
+
yield this.releaseLock(lockKey, context.clientId);
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
context.holds.set(lockKey, current - 1);
|
|
369
|
+
}
|
|
238
370
|
}
|
|
239
371
|
});
|
|
240
372
|
}
|
|
@@ -264,26 +396,20 @@ let MurLockService = MurLockService_1 = class MurLockService {
|
|
|
264
396
|
return __awaiter(this, void 0, void 0, function* () {
|
|
265
397
|
while (true) {
|
|
266
398
|
try {
|
|
267
|
-
const isLockSuccessful = yield this.
|
|
268
|
-
'EVAL',
|
|
269
|
-
this.lockScript,
|
|
270
|
-
'1',
|
|
271
|
-
lockKey,
|
|
272
|
-
clientId,
|
|
273
|
-
releaseTime.toString(),
|
|
274
|
-
]);
|
|
399
|
+
const isLockSuccessful = yield this.evalScript('lock', this.lockScript, [lockKey], [clientId, releaseTime.toString()]);
|
|
275
400
|
if (isLockSuccessful === 1) {
|
|
276
401
|
this.log('log', `Successfully obtained lock for key ${lockKey} in blocking mode`);
|
|
277
402
|
return true;
|
|
278
403
|
}
|
|
279
404
|
else {
|
|
280
|
-
|
|
281
|
-
|
|
405
|
+
const delay = this.withJitter(this.options.wait);
|
|
406
|
+
this.log('warn', `Lock busy for key ${lockKey}, waiting ${delay} ms before next attempt (blocking mode)...`);
|
|
407
|
+
yield this.sleep(delay);
|
|
282
408
|
}
|
|
283
409
|
}
|
|
284
410
|
catch (error) {
|
|
285
411
|
this.log('error', `Unexpected error in blocking lock for key ${lockKey}: ${error.message}`);
|
|
286
|
-
yield this.sleep(this.options.wait);
|
|
412
|
+
yield this.sleep(this.withJitter(this.options.wait));
|
|
287
413
|
}
|
|
288
414
|
}
|
|
289
415
|
});
|