cachette 4.0.14 → 4.0.15
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/dist/src/lib/CacheInstance.d.ts +14 -4
- package/dist/src/lib/CacheInstance.js +7 -4
- package/dist/src/lib/CacheInstance.js.map +1 -1
- package/dist/src/lib/LocalCache.d.ts +2 -7
- package/dist/src/lib/LocalCache.js +15 -26
- package/dist/src/lib/LocalCache.js.map +1 -1
- package/dist/src/lib/RedisCache.d.ts +1 -9
- package/dist/src/lib/RedisCache.js +3 -8
- package/dist/src/lib/RedisCache.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/CacheInstance.ts +18 -6
- package/src/lib/LocalCache.ts +20 -28
- package/src/lib/RedisCache.ts +6 -15
- package/test/LocalCache_test.ts +101 -0
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
export type CachableValue = any;
|
|
3
3
|
export type FetchingFunction = () => Promise<CachableValue>;
|
|
4
|
+
/**
|
|
5
|
+
* Represents a lock on a resource.
|
|
6
|
+
* The lock object must be passed to unlock() to release the lock.
|
|
7
|
+
*/
|
|
8
|
+
export interface Lock {
|
|
9
|
+
value: string | null;
|
|
10
|
+
unlock(): Promise<void>;
|
|
11
|
+
}
|
|
4
12
|
export declare abstract class CacheInstance extends EventEmitter {
|
|
13
|
+
static LOCK_RETRY_COUNT: number;
|
|
14
|
+
static LOCK_RETRY_DELAY_MS: number;
|
|
5
15
|
/**
|
|
6
16
|
* Will resolve when the cache instance connection is ready.
|
|
7
17
|
*/
|
|
@@ -76,15 +86,15 @@ export declare abstract class CacheInstance extends EventEmitter {
|
|
|
76
86
|
* @param ttlMs The time to live of the lock in ms
|
|
77
87
|
* @param retry Whether or not to retry attempts to lock
|
|
78
88
|
*
|
|
79
|
-
* @returns The lock
|
|
89
|
+
* @returns The lock object that must be passed to unlock()
|
|
80
90
|
*/
|
|
81
|
-
lock(resource: string, ttlMs: number, retry?: boolean): Promise<
|
|
91
|
+
lock(resource: string, ttlMs: number, retry?: boolean): Promise<Lock>;
|
|
82
92
|
/**
|
|
83
|
-
* Unlock a named resource
|
|
93
|
+
* Unlock a named resource acquired with lock()
|
|
84
94
|
*
|
|
85
95
|
* @param lock The lock object
|
|
86
96
|
*/
|
|
87
|
-
unlock(lock:
|
|
97
|
+
unlock(lock: Lock): Promise<void>;
|
|
88
98
|
/**
|
|
89
99
|
* Determine whether *at least one non-expired lock* starts with the given pattern.
|
|
90
100
|
*/
|
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CacheInstance = void 0;
|
|
4
4
|
const node_events_1 = require("node:events");
|
|
5
5
|
class CacheInstance extends node_events_1.EventEmitter {
|
|
6
|
+
// Shared lock configuration with generic env vars
|
|
7
|
+
static LOCK_RETRY_COUNT = parseInt(process.env.CACHETTE_LOCK_RETRY_COUNT, 10) || 20;
|
|
8
|
+
static LOCK_RETRY_DELAY_MS = parseInt(process.env.CACHETTE_LOCK_RETRY_DELAY_MS, 10) || 200;
|
|
6
9
|
/**
|
|
7
10
|
* Determines if locking is supported in the cache implementation
|
|
8
11
|
*/
|
|
@@ -16,18 +19,18 @@ class CacheInstance extends node_events_1.EventEmitter {
|
|
|
16
19
|
* @param ttlMs The time to live of the lock in ms
|
|
17
20
|
* @param retry Whether or not to retry attempts to lock
|
|
18
21
|
*
|
|
19
|
-
* @returns The lock
|
|
22
|
+
* @returns The lock object that must be passed to unlock()
|
|
20
23
|
*/
|
|
21
24
|
lock(resource, ttlMs, retry) {
|
|
22
25
|
throw new Error('unsupported');
|
|
23
26
|
}
|
|
24
27
|
/**
|
|
25
|
-
* Unlock a named resource
|
|
28
|
+
* Unlock a named resource acquired with lock()
|
|
26
29
|
*
|
|
27
30
|
* @param lock The lock object
|
|
28
31
|
*/
|
|
29
|
-
unlock(lock) {
|
|
30
|
-
|
|
32
|
+
async unlock(lock) {
|
|
33
|
+
await lock.unlock();
|
|
31
34
|
}
|
|
32
35
|
/**
|
|
33
36
|
* Determine whether *at least one non-expired lock* starts with the given pattern.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CacheInstance.js","sourceRoot":"","sources":["../../../src/lib/CacheInstance.ts"],"names":[],"mappings":";;;AAAA,6CAA2C;
|
|
1
|
+
{"version":3,"file":"CacheInstance.js","sourceRoot":"","sources":["../../../src/lib/CacheInstance.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { CachableValue, CacheInstance } from './CacheInstance';
|
|
1
|
+
import { CachableValue, CacheInstance, Lock } from './CacheInstance';
|
|
2
2
|
export declare class LocalCache extends CacheInstance {
|
|
3
3
|
static DEFAULT_MAX_ITEMS: number;
|
|
4
4
|
static DEFAULT_MAX_AGE: number;
|
|
5
|
-
static LOCK_ACQUIRE_TIMEOUT: number;
|
|
6
5
|
private cache;
|
|
7
6
|
/**
|
|
8
7
|
* @inheritdoc
|
|
@@ -52,11 +51,7 @@ export declare class LocalCache extends CacheInstance {
|
|
|
52
51
|
/**
|
|
53
52
|
* @inheritdoc
|
|
54
53
|
*/
|
|
55
|
-
lock(resource: string, ttlMs: number): Promise<
|
|
56
|
-
/**
|
|
57
|
-
* @inheritdoc
|
|
58
|
-
*/
|
|
59
|
-
unlock(lock: any): Promise<void>;
|
|
54
|
+
lock(resource: string, ttlMs: number, retry?: boolean): Promise<Lock>;
|
|
60
55
|
/**
|
|
61
56
|
* @inheritdoc
|
|
62
57
|
*
|
|
@@ -10,7 +10,6 @@ class LocalCache extends CacheInstance_1.CacheInstance {
|
|
|
10
10
|
static DEFAULT_MAX_ITEMS = 5000;
|
|
11
11
|
// Default maximum age for the items, in MS.
|
|
12
12
|
static DEFAULT_MAX_AGE = 30 * 60 * 1000;
|
|
13
|
-
static LOCK_ACQUIRE_TIMEOUT = 2000;
|
|
14
13
|
// See https://github.com/isaacs/node-lru-cache#options
|
|
15
14
|
// for options.
|
|
16
15
|
cache = new lru_cache_1.LRUCache({
|
|
@@ -108,36 +107,26 @@ class LocalCache extends CacheInstance_1.CacheInstance {
|
|
|
108
107
|
/**
|
|
109
108
|
* @inheritdoc
|
|
110
109
|
*/
|
|
111
|
-
async lock(resource, ttlMs) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
while (isLocked) {
|
|
115
|
-
if (Date.now() - startTimestamp > LocalCache.LOCK_ACQUIRE_TIMEOUT) {
|
|
116
|
-
throw new Error(`Abandoning locking ${resource} , as timed out while waiting for other lock to be released.`);
|
|
117
|
-
}
|
|
110
|
+
async lock(resource, ttlMs, retry = true) {
|
|
111
|
+
const maxAttempts = retry ? LocalCache.LOCK_RETRY_COUNT : 1;
|
|
112
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
118
113
|
this.cache.purgeStale();
|
|
119
114
|
if (!this.cache.has(resource)) {
|
|
120
|
-
|
|
115
|
+
// Lock acquired
|
|
116
|
+
const lockValue = `local-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
117
|
+
this.cache.set(resource, lockValue, { ttl: ttlMs });
|
|
118
|
+
return {
|
|
119
|
+
value: lockValue,
|
|
120
|
+
unlock: async () => {
|
|
121
|
+
this.cache.delete(resource);
|
|
122
|
+
},
|
|
123
|
+
};
|
|
121
124
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// Whatever, we just loop on waiting a bit and retrying.
|
|
125
|
-
await sleep(10);
|
|
125
|
+
if (attempt < maxAttempts - 1) {
|
|
126
|
+
await sleep(LocalCache.LOCK_RETRY_DELAY_MS);
|
|
126
127
|
}
|
|
127
128
|
}
|
|
128
|
-
|
|
129
|
-
return new Promise((resolve) => {
|
|
130
|
-
resolve(resource);
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* @inheritdoc
|
|
135
|
-
*/
|
|
136
|
-
async unlock(lock) {
|
|
137
|
-
this.cache.delete(lock);
|
|
138
|
-
return new Promise((resolve) => {
|
|
139
|
-
resolve();
|
|
140
|
-
});
|
|
129
|
+
throw new Error(`Failed to acquire lock on ${resource} after ${maxAttempts} attempts`);
|
|
141
130
|
}
|
|
142
131
|
/**
|
|
143
132
|
* @inheritdoc
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalCache.js","sourceRoot":"","sources":["../../../src/lib/LocalCache.ts"],"names":[],"mappings":";;;AAAA,yCAAqC;AAErC,
|
|
1
|
+
{"version":3,"file":"LocalCache.js","sourceRoot":"","sources":["../../../src/lib/LocalCache.ts"],"names":[],"mappings":";;;AAAA,yCAAqC;AAErC,mDAAqE;AAErE,KAAK,UAAU,KAAK,CAAC,EAAU;IAC7B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAa,UAAW,SAAQ,6BAAa;IACpC,MAAM,CAAC,iBAAiB,GAAG,IAAI,CAAC;IACvC,4CAA4C;IACrC,MAAM,CAAC,eAAe,GAAW,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAEvD,uDAAuD;IACvD,eAAe;IACP,KAAK,GAAG,IAAI,oBAAQ,CAAc;QACxC,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAA+B,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC,iBAAiB;QACrG,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,mBAA6B,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC,eAAe;KAClG,CAAC,CAAC;IAEH;;OAEG;IACI,KAAK,CAAC,OAAO;QAClB,OAAO;IACT,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,SAAS;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,QAAQ,CAAC,GAAW,EAAE,KAAoB,EAAE,GAAG,GAAG,CAAC;QAC9D,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAE7B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,GAAG,gBAAgB,CAAC,CAAC;YACrD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,oDAAoD;QACpD,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;YACd,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,QAAQ,CAAC,GAAW;QAC/B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7B,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,MAAM,CAAC,GAAW;QAC7B,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC3D,+CAA+C;QAC/C,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,yCAAyC;QACzC,IAAI,YAAY,GAAG,OAAO,EAAE,CAAC;YAC3B,OAAO,CAAC,CAAC;QACX,CAAC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,QAAQ,CAAC,GAAW;QAC/B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,kBAAkB,CAAC,QAAgB,EAAE,OAAe;QAC/D,OAAO,CAAC,CAAC;IACX,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,KAAK;QAChB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,WAAW;QACtB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED;;;OAGG;IACI,kBAAkB;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,IAAI,CAAC,QAAgB,EAAE,KAAa,EAAE,KAAK,GAAG,IAAI;QAC7D,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5D,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;YACvD,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9B,gBAAgB;gBAChB,MAAM,SAAS,GAAG,SAAS,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/E,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;gBAEpD,OAAO;oBACL,KAAK,EAAE,SAAS;oBAChB,MAAM,EAAE,KAAK,IAAI,EAAE;wBACjB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;oBAC9B,CAAC;iBACF,CAAC;YACJ,CAAC;YAED,IAAI,OAAO,GAAG,WAAW,GAAG,CAAC,EAAE,CAAC;gBAC9B,MAAM,KAAK,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,UAAU,WAAW,WAAW,CAAC,CAAC;IACzF,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,OAAO,CAAC,MAAc;QACjC,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACpD,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACxB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAChC,yEAAyE;YACzE,4EAA4E;YAC5E,qEAAqE;YACrE,4EAA4E;YAC5E,2DAA2D;YAC3D,IAAI,GAAG,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBACtC,KAAK,GAAG,IAAI,CAAC;YACf,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC;;AApKH,gCAqKC"}
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import { CachableValue, CacheInstance } from './CacheInstance';
|
|
1
|
+
import { CachableValue, CacheInstance, Lock } from './CacheInstance';
|
|
2
2
|
export declare const SIZE_THRESHOLD_WARNING_BYTES = 20000000;
|
|
3
|
-
export interface Lock {
|
|
4
|
-
value: string | null;
|
|
5
|
-
unlock(): Promise<void>;
|
|
6
|
-
}
|
|
7
3
|
/**
|
|
8
4
|
* Wrapper class for using Redis as a cache.
|
|
9
5
|
*
|
|
@@ -123,10 +119,6 @@ export declare class RedisCache extends CacheInstance {
|
|
|
123
119
|
* @inheritdoc
|
|
124
120
|
*/
|
|
125
121
|
lock(resource: string, ttlMs: number, retry?: boolean): Promise<Lock>;
|
|
126
|
-
/**
|
|
127
|
-
* @inheritdoc
|
|
128
|
-
*/
|
|
129
|
-
unlock(lock: Lock): Promise<void>;
|
|
130
122
|
/**
|
|
131
123
|
* @inheritdoc
|
|
132
124
|
*
|
|
@@ -24,8 +24,9 @@ class RedisCache extends CacheInstance_1.CacheInstance {
|
|
|
24
24
|
static ERROR_PREFIX = 'f405eed4-507c-4aa5-a6d2-c1813d584b8f-ERROR';
|
|
25
25
|
static NUMBER_PREFIX = 'f405eed4-507c-4aa5-a6d2-c1813d584b8f-NUMBER';
|
|
26
26
|
static REDIS_CONNECTION_TIMEOUT_MS = parseInt(process.env.REDIS_CONNECTION_TIMEOUT_MS, 10) || 5000;
|
|
27
|
-
|
|
28
|
-
static
|
|
27
|
+
// Backward compatible: REDLOCK_* takes precedence, then CACHETTE_LOCK_*
|
|
28
|
+
static REDLOCK_RETRY_COUNT = parseInt(process.env.REDLOCK_RETRY_COUNT, 10) || RedisCache.LOCK_RETRY_COUNT; // lib. default: 20
|
|
29
|
+
static REDLOCK_RETRY_DELAY_MS = parseInt(process.env.REDLOCK_RETRY_DELAY_MS, 10) || RedisCache.LOCK_RETRY_DELAY_MS; // lib. default: 200
|
|
29
30
|
static REDLOCK_CLOCK_DRIFT_FACTOR = parseInt(process.env.REDLOCK_CLOCK_DRIFT_FACTOR, 10) || 0.01; // lib. default: 0.01
|
|
30
31
|
static REDLOCK_JITTER_MS = parseInt(process.env.REDLOCK_JITTER_MS, 10) || 200; // lib. default: 200
|
|
31
32
|
redisClient;
|
|
@@ -341,12 +342,6 @@ class RedisCache extends CacheInstance_1.CacheInstance {
|
|
|
341
342
|
unlock: async () => internalLock.unlock(),
|
|
342
343
|
};
|
|
343
344
|
}
|
|
344
|
-
/**
|
|
345
|
-
* @inheritdoc
|
|
346
|
-
*/
|
|
347
|
-
async unlock(lock) {
|
|
348
|
-
await lock.unlock();
|
|
349
|
-
}
|
|
350
345
|
/**
|
|
351
346
|
* @inheritdoc
|
|
352
347
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RedisCache.js","sourceRoot":"","sources":["../../../src/lib/RedisCache.ts"],"names":[],"mappings":";;;AAAA,qCAA4B;AAC5B,mCAAmC;AAEnC,
|
|
1
|
+
{"version":3,"file":"RedisCache.js","sourceRoot":"","sources":["../../../src/lib/RedisCache.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/lib/CacheInstance.ts
CHANGED
|
@@ -3,7 +3,19 @@ import { EventEmitter } from 'node:events';
|
|
|
3
3
|
export type CachableValue = any;
|
|
4
4
|
export type FetchingFunction = () => Promise<CachableValue>;
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Represents a lock on a resource.
|
|
8
|
+
* The lock object must be passed to unlock() to release the lock.
|
|
9
|
+
*/
|
|
10
|
+
export interface Lock {
|
|
11
|
+
value: string | null;
|
|
12
|
+
unlock(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
export abstract class CacheInstance extends EventEmitter {
|
|
16
|
+
// Shared lock configuration with generic env vars
|
|
17
|
+
public static LOCK_RETRY_COUNT = parseInt(process.env.CACHETTE_LOCK_RETRY_COUNT as string, 10) || 20;
|
|
18
|
+
public static LOCK_RETRY_DELAY_MS = parseInt(process.env.CACHETTE_LOCK_RETRY_DELAY_MS as string, 10) || 200;
|
|
7
19
|
/**
|
|
8
20
|
* Will resolve when the cache instance connection is ready.
|
|
9
21
|
*/
|
|
@@ -90,19 +102,19 @@ export abstract class CacheInstance extends EventEmitter {
|
|
|
90
102
|
* @param ttlMs The time to live of the lock in ms
|
|
91
103
|
* @param retry Whether or not to retry attempts to lock
|
|
92
104
|
*
|
|
93
|
-
* @returns The lock
|
|
105
|
+
* @returns The lock object that must be passed to unlock()
|
|
94
106
|
*/
|
|
95
|
-
public lock(resource: string, ttlMs: number, retry?: boolean): Promise<
|
|
107
|
+
public lock(resource: string, ttlMs: number, retry?: boolean): Promise<Lock> {
|
|
96
108
|
throw new Error('unsupported');
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
/**
|
|
100
|
-
* Unlock a named resource
|
|
112
|
+
* Unlock a named resource acquired with lock()
|
|
101
113
|
*
|
|
102
114
|
* @param lock The lock object
|
|
103
115
|
*/
|
|
104
|
-
public unlock(lock:
|
|
105
|
-
|
|
116
|
+
public async unlock(lock: Lock): Promise<void> {
|
|
117
|
+
await lock.unlock();
|
|
106
118
|
}
|
|
107
119
|
|
|
108
120
|
/**
|
|
@@ -167,7 +179,7 @@ export abstract class CacheInstance extends EventEmitter {
|
|
|
167
179
|
}
|
|
168
180
|
|
|
169
181
|
// I'm the one fetching.
|
|
170
|
-
let lock:
|
|
182
|
+
let lock: Lock | undefined;
|
|
171
183
|
try {
|
|
172
184
|
// get the lock if needed
|
|
173
185
|
const lockName = `lock__${key}`;
|
package/src/lib/LocalCache.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LRUCache } from 'lru-cache';
|
|
2
2
|
|
|
3
|
-
import { CachableValue, CacheInstance } from './CacheInstance';
|
|
3
|
+
import { CachableValue, CacheInstance, Lock } from './CacheInstance';
|
|
4
4
|
|
|
5
5
|
async function sleep(ms: number): Promise<void> {
|
|
6
6
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -11,8 +11,6 @@ export class LocalCache extends CacheInstance {
|
|
|
11
11
|
// Default maximum age for the items, in MS.
|
|
12
12
|
public static DEFAULT_MAX_AGE: number = 30 * 60 * 1000;
|
|
13
13
|
|
|
14
|
-
public static LOCK_ACQUIRE_TIMEOUT = 2000;
|
|
15
|
-
|
|
16
14
|
// See https://github.com/isaacs/node-lru-cache#options
|
|
17
15
|
// for options.
|
|
18
16
|
private cache = new LRUCache<string, any>({
|
|
@@ -123,36 +121,30 @@ export class LocalCache extends CacheInstance {
|
|
|
123
121
|
/**
|
|
124
122
|
* @inheritdoc
|
|
125
123
|
*/
|
|
126
|
-
public async lock(resource: string, ttlMs: number): Promise<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (Date.now() - startTimestamp > LocalCache.LOCK_ACQUIRE_TIMEOUT) {
|
|
131
|
-
throw new Error(`Abandoning locking ${resource} , as timed out while waiting for other lock to be released.`);
|
|
132
|
-
}
|
|
124
|
+
public async lock(resource: string, ttlMs: number, retry = true): Promise<Lock> {
|
|
125
|
+
const maxAttempts = retry ? LocalCache.LOCK_RETRY_COUNT : 1;
|
|
126
|
+
|
|
127
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
133
128
|
this.cache.purgeStale();
|
|
134
129
|
if (!this.cache.has(resource)) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
130
|
+
// Lock acquired
|
|
131
|
+
const lockValue = `local-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
132
|
+
this.cache.set(resource, lockValue, { ttl: ttlMs });
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
value: lockValue,
|
|
136
|
+
unlock: async () => {
|
|
137
|
+
this.cache.delete(resource);
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (attempt < maxAttempts - 1) {
|
|
143
|
+
await sleep(LocalCache.LOCK_RETRY_DELAY_MS);
|
|
140
144
|
}
|
|
141
145
|
}
|
|
142
|
-
this.cache.set(resource, 1, { ttl: ttlMs });
|
|
143
|
-
return new Promise((resolve) => {
|
|
144
|
-
resolve(resource);
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
* @inheritdoc
|
|
150
|
-
*/
|
|
151
|
-
public async unlock(lock: any): Promise<void> {
|
|
152
|
-
this.cache.delete(lock);
|
|
153
|
-
return new Promise((resolve) => {
|
|
154
|
-
resolve();
|
|
155
|
-
});
|
|
147
|
+
throw new Error(`Failed to acquire lock on ${resource} after ${maxAttempts} attempts`);
|
|
156
148
|
}
|
|
157
149
|
|
|
158
150
|
/**
|
package/src/lib/RedisCache.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import Redis from 'ioredis';
|
|
2
2
|
import * as Redlock from 'redlock';
|
|
3
3
|
|
|
4
|
-
import { CachableValue, CacheInstance } from './CacheInstance';
|
|
4
|
+
import { CachableValue, CacheInstance, Lock } from './CacheInstance';
|
|
5
5
|
|
|
6
6
|
export const SIZE_THRESHOLD_WARNING_BYTES = 20_000_000;
|
|
7
7
|
|
|
8
|
-
export interface Lock {
|
|
9
|
-
value: string | null;
|
|
10
|
-
unlock(): Promise<void>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
8
|
/**
|
|
14
9
|
* Wrapper class for using Redis as a cache.
|
|
15
10
|
*
|
|
@@ -30,8 +25,11 @@ export class RedisCache extends CacheInstance {
|
|
|
30
25
|
public static NUMBER_PREFIX = 'f405eed4-507c-4aa5-a6d2-c1813d584b8f-NUMBER';
|
|
31
26
|
|
|
32
27
|
public static REDIS_CONNECTION_TIMEOUT_MS = parseInt(process.env.REDIS_CONNECTION_TIMEOUT_MS as string, 10) || 5000;
|
|
33
|
-
|
|
34
|
-
public static
|
|
28
|
+
// Backward compatible: REDLOCK_* takes precedence, then CACHETTE_LOCK_*
|
|
29
|
+
public static REDLOCK_RETRY_COUNT =
|
|
30
|
+
parseInt(process.env.REDLOCK_RETRY_COUNT as string, 10) || RedisCache.LOCK_RETRY_COUNT; // lib. default: 20
|
|
31
|
+
public static REDLOCK_RETRY_DELAY_MS =
|
|
32
|
+
parseInt(process.env.REDLOCK_RETRY_DELAY_MS as string, 10) || RedisCache.LOCK_RETRY_DELAY_MS; // lib. default: 200
|
|
35
33
|
public static REDLOCK_CLOCK_DRIFT_FACTOR = parseInt(process.env.REDLOCK_CLOCK_DRIFT_FACTOR as string, 10) || 0.01; // lib. default: 0.01
|
|
36
34
|
public static REDLOCK_JITTER_MS = parseInt(process.env.REDLOCK_JITTER_MS as string, 10) || 200; // lib. default: 200
|
|
37
35
|
|
|
@@ -390,13 +388,6 @@ export class RedisCache extends CacheInstance {
|
|
|
390
388
|
};
|
|
391
389
|
}
|
|
392
390
|
|
|
393
|
-
/**
|
|
394
|
-
* @inheritdoc
|
|
395
|
-
*/
|
|
396
|
-
public async unlock(lock: Lock): Promise<void> {
|
|
397
|
-
await lock.unlock();
|
|
398
|
-
}
|
|
399
|
-
|
|
400
391
|
/**
|
|
401
392
|
* @inheritdoc
|
|
402
393
|
*
|
package/test/LocalCache_test.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { expect } from 'chai';
|
|
2
|
+
import * as sinon from 'sinon';
|
|
2
3
|
|
|
3
4
|
import { LocalCache } from '../src/lib/LocalCache';
|
|
5
|
+
import { CacheInstance } from '../src/lib/CacheInstance';
|
|
4
6
|
|
|
5
7
|
describe('LocalCache', () => {
|
|
6
8
|
it('can set values', async () => {
|
|
@@ -115,6 +117,105 @@ describe('LocalCache', () => {
|
|
|
115
117
|
expect(wasSet).to.be.true;
|
|
116
118
|
expect(cacheTtl).to.equal(0);
|
|
117
119
|
});
|
|
120
|
+
|
|
121
|
+
describe('lock', () => {
|
|
122
|
+
it('returns a Lock object with value property and unlock method', async () => {
|
|
123
|
+
const cache = new LocalCache();
|
|
124
|
+
const lock = await cache.lock('test-resource', 10000);
|
|
125
|
+
|
|
126
|
+
expect(lock).to.have.property('value');
|
|
127
|
+
expect(lock.value).to.be.a('string');
|
|
128
|
+
expect(lock.value).to.match(/^local-\d+-[a-z0-9]+$/);
|
|
129
|
+
|
|
130
|
+
expect(lock).to.have.property('unlock');
|
|
131
|
+
expect(lock.unlock).to.be.a('function');
|
|
132
|
+
|
|
133
|
+
await cache.unlock(lock);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('releases lock via the returned Lock object unlock method', async () => {
|
|
137
|
+
const cache = new LocalCache();
|
|
138
|
+
const resource = `test-resource-${Math.random()}`;
|
|
139
|
+
|
|
140
|
+
const lock = await cache.lock(resource, 10000);
|
|
141
|
+
expect(await cache.hasLock(resource)).to.be.true;
|
|
142
|
+
|
|
143
|
+
await lock.unlock();
|
|
144
|
+
expect(await cache.hasLock(resource)).to.be.false;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('releases lock via cache.unlock()', async () => {
|
|
148
|
+
const cache = new LocalCache();
|
|
149
|
+
const resource = `test-resource-${Math.random()}`;
|
|
150
|
+
|
|
151
|
+
const lock = await cache.lock(resource, 10000);
|
|
152
|
+
expect(await cache.hasLock(resource)).to.be.true;
|
|
153
|
+
|
|
154
|
+
await cache.unlock(lock);
|
|
155
|
+
expect(await cache.hasLock(resource)).to.be.false;
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('with retry = true, retries until lock is available', async () => {
|
|
159
|
+
// Fake only setTimeout to avoid conflicts with LRU cache's TTL (which uses Date)
|
|
160
|
+
const clock = sinon.useFakeTimers({ toFake: ['setTimeout'] });
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const resource = 'test-resource';
|
|
164
|
+
const cache = new LocalCache();
|
|
165
|
+
const hasSpy = sinon.spy(cache['cache'], 'has');
|
|
166
|
+
|
|
167
|
+
const firstLock = await cache.lock(resource, 10000);
|
|
168
|
+
const hasCallsAfterFirstLock = hasSpy.callCount;
|
|
169
|
+
|
|
170
|
+
let secondLockAcquired = false;
|
|
171
|
+
const lockPromise = cache.lock(resource, 10000, true).then((lock) => {
|
|
172
|
+
secondLockAcquired = true;
|
|
173
|
+
return lock;
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Advance time to trigger retry, but lock still held
|
|
177
|
+
await clock.tickAsync(CacheInstance.LOCK_RETRY_DELAY_MS);
|
|
178
|
+
expect(secondLockAcquired).to.be.false;
|
|
179
|
+
expect(hasSpy.callCount).to.be.greaterThan(hasCallsAfterFirstLock);
|
|
180
|
+
|
|
181
|
+
// Release first lock
|
|
182
|
+
await cache.unlock(firstLock);
|
|
183
|
+
|
|
184
|
+
// Advance time for next retry - should acquire now
|
|
185
|
+
await clock.tickAsync(CacheInstance.LOCK_RETRY_DELAY_MS);
|
|
186
|
+
const secondLock = await lockPromise;
|
|
187
|
+
|
|
188
|
+
expect(secondLockAcquired).to.be.true;
|
|
189
|
+
expect(secondLock).to.have.property('value');
|
|
190
|
+
|
|
191
|
+
hasSpy.restore();
|
|
192
|
+
await cache.unlock(secondLock);
|
|
193
|
+
} finally {
|
|
194
|
+
clock.restore();
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('with retry = false, throws immediately if lock is held', async () => {
|
|
199
|
+
const cache = new LocalCache();
|
|
200
|
+
const resource = `test-resource-${Math.random()}`;
|
|
201
|
+
|
|
202
|
+
const firstLock = await cache.lock(resource, 10000);
|
|
203
|
+
expect(await cache.hasLock(resource)).to.be.true;
|
|
204
|
+
|
|
205
|
+
let error: Error | undefined;
|
|
206
|
+
try {
|
|
207
|
+
await cache.lock(resource, 10000, false);
|
|
208
|
+
} catch (err) {
|
|
209
|
+
error = err as Error;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
expect(error).to.exist;
|
|
213
|
+
expect(error!.message).to.include('Failed to acquire lock');
|
|
214
|
+
expect(error!.message).to.include('after 1 attempts');
|
|
215
|
+
|
|
216
|
+
await cache.unlock(firstLock);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
118
219
|
});
|
|
119
220
|
|
|
120
221
|
function sleep(ms: number): Promise<void> {
|