@quillsql/node 0.9.16 → 0.9.18
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/cjs/db/CachedConnection.d.ts +1 -5
- package/dist/cjs/db/CachedConnection.js +20 -92
- package/dist/cjs/db/CachedConnection.js.map +1 -1
- package/dist/cjs/index.d.ts +17 -3
- package/dist/cjs/index.js +188 -131
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/models/Cache.d.ts +7 -12
- package/dist/cjs/utils/Lock.d.ts +13 -0
- package/dist/cjs/utils/Lock.js +77 -0
- package/dist/cjs/utils/Lock.js.map +1 -0
- package/dist/esm/db/CachedConnection.d.ts +1 -5
- package/dist/esm/db/CachedConnection.js +20 -92
- package/dist/esm/db/CachedConnection.js.map +1 -1
- package/dist/esm/index.d.ts +17 -3
- package/dist/esm/index.js +188 -131
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/models/Cache.d.ts +7 -12
- package/dist/esm/utils/Lock.d.ts +13 -0
- package/dist/esm/utils/Lock.js +73 -0
- package/dist/esm/utils/Lock.js.map +1 -0
- package/package.json +2 -2
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
export interface Mappable {
|
|
2
2
|
get(key: string): Promise<string | null>;
|
|
3
3
|
set(key: string, value: string, type?: string, ttl?: number): Promise<string | null>;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
del(...keys: string[]): CacheTransaction;
|
|
12
|
-
rPush(key: string, elements: string[]): CacheTransaction;
|
|
13
|
-
expire(key: string, seconds: number): CacheTransaction;
|
|
14
|
-
set(key: string, value: string, type?: string, ttl?: number): CacheTransaction;
|
|
15
|
-
exec(): Promise<unknown>;
|
|
4
|
+
set(key: string, value: string, options: {
|
|
5
|
+
NX?: boolean;
|
|
6
|
+
XX?: boolean;
|
|
7
|
+
EX?: number;
|
|
8
|
+
PX?: number;
|
|
9
|
+
}): Promise<string | null>;
|
|
10
|
+
del(key: string): Promise<number>;
|
|
16
11
|
}
|
|
17
12
|
export interface CacheCredentials {
|
|
18
13
|
username: string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Mappable } from "../models/Cache";
|
|
2
|
+
export declare class Lock {
|
|
3
|
+
private readonly cacheLockKey;
|
|
4
|
+
private readonly lastCacheKey;
|
|
5
|
+
private readonly ttlSeconds;
|
|
6
|
+
private readonly cooldownMs;
|
|
7
|
+
constructor(clientId: string);
|
|
8
|
+
acquire(cache: Mappable): Promise<string>;
|
|
9
|
+
release(cache: Mappable, lockValue: string): Promise<void>;
|
|
10
|
+
releaseSafely(cache: Mappable, lockValue: string): Promise<void>;
|
|
11
|
+
persistCooldown(cache: Mappable): Promise<void>;
|
|
12
|
+
persistCooldownSafely(cache: Mappable): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// A 2-part lock:
|
|
2
|
+
// 1) Redis Distributed lock ( 1 machine can hold it at a time )
|
|
3
|
+
// 2) 6-hour Cooldown
|
|
4
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
5
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
6
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
7
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
8
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
9
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
10
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
export class Lock {
|
|
14
|
+
constructor(clientId) {
|
|
15
|
+
this.ttlSeconds = 90 * 60; // 90 minutes
|
|
16
|
+
this.cooldownMs = 6 * 60 * 60 * 1000; // 6 hours
|
|
17
|
+
this.cacheLockKey = `quill:sdk:cache:lock:${clientId}`;
|
|
18
|
+
this.lastCacheKey = `quill:sdk:cache:locktime:${clientId}`;
|
|
19
|
+
}
|
|
20
|
+
acquire(cache) {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
const lockValue = `${process.pid}:${Date.now()}:${Math.random().toString(36).slice(2)}`;
|
|
23
|
+
const acquired = yield cache.set(this.cacheLockKey, lockValue, { NX: true, EX: this.ttlSeconds });
|
|
24
|
+
if (!acquired) {
|
|
25
|
+
throw new Error(".cache() is being called by another instance already. Please try again later.");
|
|
26
|
+
}
|
|
27
|
+
const raw = yield cache.get(this.lastCacheKey);
|
|
28
|
+
const lastCacheRunAt = raw ? Number(raw) : NaN;
|
|
29
|
+
if (Number.isFinite(lastCacheRunAt) && Date.now() - lastCacheRunAt < this.cooldownMs) {
|
|
30
|
+
yield this.release(cache, lockValue);
|
|
31
|
+
throw new Error("Caching cooldown is active. Please try again later.");
|
|
32
|
+
}
|
|
33
|
+
return lockValue;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
release(cache, lockValue) {
|
|
37
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
var _a;
|
|
39
|
+
const acquiredAtMs = Number((_a = lockValue.split(":")[1]) !== null && _a !== void 0 ? _a : "0");
|
|
40
|
+
if (acquiredAtMs > 0 && Date.now() - acquiredAtMs >= (this.ttlSeconds - 60) * 1000)
|
|
41
|
+
return;
|
|
42
|
+
if ((yield cache.get(this.cacheLockKey)) !== lockValue)
|
|
43
|
+
return;
|
|
44
|
+
yield cache.del(this.cacheLockKey);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
releaseSafely(cache, lockValue) {
|
|
48
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
try {
|
|
50
|
+
yield this.release(cache, lockValue);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.warn("Failed to release cache lock", error);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
persistCooldown(cache) {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
yield cache.set(this.lastCacheKey, String(Date.now()), { EX: this.cooldownMs / 1000 });
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
persistCooldownSafely(cache) {
|
|
63
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
64
|
+
try {
|
|
65
|
+
yield this.persistCooldown(cache);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.warn("Caching complete but failed to persist last cache run time", error);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=Lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Lock.js","sourceRoot":"","sources":["../../../src/utils/Lock.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAClB,gEAAgE;AAChE,sBAAsB;;;;;;;;;;AAItB,MAAM,OAAO,IAAI;IAMf,YAAY,QAAgB;QAHX,eAAU,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,aAAa;QACnC,eAAU,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,UAAU;QAG1D,IAAI,CAAC,YAAY,GAAG,wBAAwB,QAAQ,EAAE,CAAC;QACvD,IAAI,CAAC,YAAY,GAAG,4BAA4B,QAAQ,EAAE,CAAC;IAC7D,CAAC;IAEY,OAAO,CAAC,KAAe;;YAClC,MAAM,SAAS,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAExF,MAAM,QAAQ,GAAkB,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;YAChH,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAC;YACpG,CAAC;YAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC/C,MAAM,cAAc,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAC/C,IAAI,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;gBACrF,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;gBACrC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;YACzE,CAAC;YAED,OAAO,SAAS,CAAC;QACnB,CAAC;KAAA;IAEY,OAAO,CAAC,KAAe,EAAE,SAAiB;;;YACrD,MAAM,YAAY,GAAG,MAAM,CAAC,MAAA,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,mCAAI,GAAG,CAAC,CAAC;YAC5D,IAAI,YAAY,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC,GAAG,IAAI;gBAAE,OAAO;YAC3F,IAAI,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,SAAS;gBAAE,OAAO;YAC/D,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACrC,CAAC;KAAA;IAEY,aAAa,CAAC,KAAe,EAAE,SAAiB;;YAC3D,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;YACvC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YACtD,CAAC;QACH,CAAC;KAAA;IAEY,eAAe,CAAC,KAAe;;YAC1C,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,UAAU,GAAG,IAAI,EAAE,CAAC,CAAC;QACzF,CAAC;KAAA;IAEY,qBAAqB,CAAC,KAAe;;YAChD,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;YACpC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,4DAA4D,EAAE,KAAK,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;KAAA;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quillsql/node",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.18",
|
|
4
4
|
"description": "Quill Server SDK for Node.js",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"mssql:dev": "nodemon ./examples/mssql-node/app.ts",
|
|
19
19
|
"databricks:dev": "nodemon ./examples/databricks-node/app.ts",
|
|
20
20
|
"integration-tests": "jest '.*\\.ispec\\.ts$'",
|
|
21
|
-
"test": "tsc
|
|
21
|
+
"test": "tsc -p tsconfig.json && jest --detectOpenHandles",
|
|
22
22
|
"style-check": "prettier --check \"**/*.{ts,tsx,md}\"",
|
|
23
23
|
"style-fix": "prettier --write \"**/*.{ts,tsx,md}\"",
|
|
24
24
|
"lint-check": "eslint \"**/*.{ts,tsx}\"",
|