lemon-core 4.1.14 → 4.2.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/dist/common/test-helper.d.ts +2 -2
- package/dist/common/test-helper.js +24 -26
- package/dist/common/test-helper.js.map +1 -1
- package/dist/controllers/dummy-controller.js +39 -46
- package/dist/controllers/dummy-controller.js.map +1 -1
- package/dist/controllers/general-api-controller.js +95 -100
- package/dist/controllers/general-api-controller.js.map +1 -1
- package/dist/controllers/general-controller.js +81 -82
- package/dist/controllers/general-controller.js.map +1 -1
- package/dist/cores/api/api-service.d.ts +1 -1
- package/dist/cores/api/api-service.js +228 -269
- package/dist/cores/api/api-service.js.map +1 -1
- package/dist/cores/aws/aws-kms-service.d.ts +1 -2
- package/dist/cores/aws/aws-kms-service.js +143 -153
- package/dist/cores/aws/aws-kms-service.js.map +1 -1
- package/dist/cores/aws/aws-s3-service.d.ts +2 -4
- package/dist/cores/aws/aws-s3-service.js +306 -330
- package/dist/cores/aws/aws-s3-service.js.map +1 -1
- package/dist/cores/aws/aws-sns-service.js +147 -153
- package/dist/cores/aws/aws-sns-service.js.map +1 -1
- package/dist/cores/aws/aws-sqs-service.js +149 -170
- package/dist/cores/aws/aws-sqs-service.js.map +1 -1
- package/dist/cores/aws/index.js +10 -20
- package/dist/cores/aws/index.js.map +1 -1
- package/dist/cores/cache/cache-service.d.ts +2 -2
- package/dist/cores/cache/cache-service.js +435 -499
- package/dist/cores/cache/cache-service.js.map +1 -1
- package/dist/cores/config/config-service.d.ts +1 -1
- package/dist/cores/config/config-service.js +56 -63
- package/dist/cores/config/config-service.js.map +1 -1
- package/dist/cores/config/index.js +14 -24
- package/dist/cores/config/index.js.map +1 -1
- package/dist/cores/core-services.d.ts +1 -1
- package/dist/cores/dynamo/dynamo-query-service.js +37 -51
- package/dist/cores/dynamo/dynamo-query-service.js.map +1 -1
- package/dist/cores/dynamo/dynamo-scan-service.d.ts +2 -2
- package/dist/cores/dynamo/dynamo-scan-service.js +29 -40
- package/dist/cores/dynamo/dynamo-scan-service.js.map +1 -1
- package/dist/cores/dynamo/dynamo-service.d.ts +2 -2
- package/dist/cores/dynamo/dynamo-service.js +528 -601
- package/dist/cores/dynamo/dynamo-service.js.map +1 -1
- package/dist/cores/dynamo/tools/expressions.js +17 -7
- package/dist/cores/dynamo/tools/expressions.js.map +1 -1
- package/dist/cores/dynamo/tools/query.js +142 -127
- package/dist/cores/dynamo/tools/query.js.map +1 -1
- package/dist/cores/dynamo/tools/scan.js +111 -97
- package/dist/cores/dynamo/tools/scan.js.map +1 -1
- package/dist/cores/dynamo/tools/serializer.js +17 -7
- package/dist/cores/dynamo/tools/serializer.js.map +1 -1
- package/dist/cores/dynamo/tools/utils.d.ts +0 -2
- package/dist/cores/dynamo/tools/utils.js.map +1 -1
- package/dist/cores/elastic/elastic6-query-service.js +307 -324
- package/dist/cores/elastic/elastic6-query-service.js.map +1 -1
- package/dist/cores/elastic/elastic6-service.d.ts +3 -3
- package/dist/cores/elastic/elastic6-service.js +568 -647
- package/dist/cores/elastic/elastic6-service.js.map +1 -1
- package/dist/cores/elastic/hangul-service.js +52 -54
- package/dist/cores/elastic/hangul-service.js.map +1 -1
- package/dist/cores/lambda/index.js +42 -36
- package/dist/cores/lambda/index.js.map +1 -1
- package/dist/cores/lambda/lambda-alb-handler.d.ts +2 -2
- package/dist/cores/lambda/lambda-alb-handler.js +59 -72
- package/dist/cores/lambda/lambda-alb-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-cognito-handler.js +10 -19
- package/dist/cores/lambda/lambda-cognito-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-cron-handler.d.ts +1 -1
- package/dist/cores/lambda/lambda-cron-handler.js +14 -23
- package/dist/cores/lambda/lambda-cron-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-dynamo-stream-handler.d.ts +2 -2
- package/dist/cores/lambda/lambda-dynamo-stream-handler.js +57 -67
- package/dist/cores/lambda/lambda-dynamo-stream-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-handler.d.ts +22 -22
- package/dist/cores/lambda/lambda-handler.js +93 -106
- package/dist/cores/lambda/lambda-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-notification-handler.d.ts +1 -1
- package/dist/cores/lambda/lambda-notification-handler.js +39 -50
- package/dist/cores/lambda/lambda-notification-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-sns-handler.js +79 -88
- package/dist/cores/lambda/lambda-sns-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-sqs-handler.js +79 -88
- package/dist/cores/lambda/lambda-sqs-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-web-handler.d.ts +21 -43
- package/dist/cores/lambda/lambda-web-handler.js +392 -482
- package/dist/cores/lambda/lambda-web-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-wss-handler.js +23 -32
- package/dist/cores/lambda/lambda-wss-handler.js.map +1 -1
- package/dist/cores/protocol/index.js +10 -20
- package/dist/cores/protocol/index.js.map +1 -1
- package/dist/cores/protocol/protocol-service.d.ts +3 -3
- package/dist/cores/protocol/protocol-service.js +235 -227
- package/dist/cores/protocol/protocol-service.js.map +1 -1
- package/dist/cores/storage/http-storage-service.js +65 -85
- package/dist/cores/storage/http-storage-service.js.map +1 -1
- package/dist/cores/storage/model-manager.js +66 -85
- package/dist/cores/storage/model-manager.js.map +1 -1
- package/dist/cores/storage/proxy-storage-service.d.ts +2 -2
- package/dist/cores/storage/proxy-storage-service.js +562 -599
- package/dist/cores/storage/proxy-storage-service.js.map +1 -1
- package/dist/cores/storage/redis-storage-service.js +163 -177
- package/dist/cores/storage/redis-storage-service.js.map +1 -1
- package/dist/cores/storage/storage-service.js +281 -322
- package/dist/cores/storage/storage-service.js.map +1 -1
- package/dist/engine/builder.d.ts +1 -1
- package/dist/engine/builder.js +59 -63
- package/dist/engine/builder.js.map +1 -1
- package/dist/engine/engine.d.ts +4 -4
- package/dist/engine/engine.js +9 -18
- package/dist/engine/engine.js.map +1 -1
- package/dist/engine/types.d.ts +1 -1
- package/dist/engine/utilities.d.ts +14 -10
- package/dist/engine/utilities.js +301 -280
- package/dist/engine/utilities.js.map +1 -1
- package/dist/environ.js +4 -6
- package/dist/environ.js.map +1 -1
- package/dist/extended/abstract-service.js +595 -645
- package/dist/extended/abstract-service.js.map +1 -1
- package/dist/extended/libs/sig-v4.js.map +1 -1
- package/dist/generated/field-registry.d.ts +10 -0
- package/dist/generated/field-registry.js +17 -0
- package/dist/generated/field-registry.js.map +1 -0
- package/dist/helpers/helpers.d.ts +17 -9
- package/dist/helpers/helpers.js +88 -78
- package/dist/helpers/helpers.js.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/lib/dynamodb-value.js +2 -3
- package/dist/lib/dynamodb-value.js.map +1 -1
- package/dist/tools/express.js +4 -5
- package/dist/tools/express.js.map +1 -1
- package/dist/tools/tools.d.ts +3 -1
- package/dist/tools/tools.js +14 -21
- package/dist/tools/tools.js.map +1 -1
- package/package.json +19 -18
- package/dist/exec-cli.d.ts +0 -2
- package/dist/exec-cli.js +0 -211
- package/dist/exec-cli.js.map +0 -1
- package/dist/lib/dynamo/expressions.d.ts +0 -14
- package/dist/lib/dynamo/expressions.js +0 -212
- package/dist/lib/dynamo/expressions.js.map +0 -1
- package/dist/lib/dynamo/query.d.ts +0 -43
- package/dist/lib/dynamo/query.js +0 -246
- package/dist/lib/dynamo/query.js.map +0 -1
- package/dist/lib/dynamo/scan.d.ts +0 -33
- package/dist/lib/dynamo/scan.js +0 -172
- package/dist/lib/dynamo/scan.js.map +0 -1
- package/dist/lib/dynamo/serializer.d.ts +0 -12
- package/dist/lib/dynamo/serializer.js +0 -243
- package/dist/lib/dynamo/serializer.js.map +0 -1
- package/dist/lib/dynamo/utils.d.ts +0 -15
- package/dist/lib/dynamo/utils.js +0 -129
- package/dist/lib/dynamo/utils.js.map +0 -1
- package/dist/tools/shared.d.ts +0 -37
- package/dist/tools/shared.js +0 -130
- package/dist/tools/shared.js.map +0 -1
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
14
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.
|
|
6
|
+
exports.DummyCacheService = exports.CacheService = void 0;
|
|
7
|
+
exports.sleep = sleep;
|
|
8
|
+
exports.toTTL = toTTL;
|
|
9
|
+
exports.fromTTL = fromTTL;
|
|
16
10
|
/**
|
|
17
11
|
* `cache-services.ts`
|
|
18
12
|
* - common service for remote cache
|
|
@@ -36,23 +30,43 @@ const NS = engine_1.$U.NS('CCHS', 'green'); // NAMESPACE TO BE PRINTED.
|
|
|
36
30
|
*/
|
|
37
31
|
class CacheService {
|
|
38
32
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* @param backend cache backend object
|
|
43
|
-
* @param params params to create service.
|
|
44
|
-
* @protected
|
|
33
|
+
* Environment variable name for cache server endpoint
|
|
34
|
+
* @static
|
|
45
35
|
*/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
36
|
+
static ENV_CACHE_ENDPOINT = 'CACHE_ENDPOINT';
|
|
37
|
+
/**
|
|
38
|
+
* Environment variable name for default cache timeout
|
|
39
|
+
* @static
|
|
40
|
+
*/
|
|
41
|
+
static ENV_CACHE_DEFAULT_TIMEOUT = 'CACHE_DEFAULT_TIMEOUT';
|
|
42
|
+
/**
|
|
43
|
+
* Default cache timeout
|
|
44
|
+
* @static
|
|
45
|
+
*/
|
|
46
|
+
static DEF_CACHE_DEFAULT_TIMEOUT = 24 * 60 * 60; // 1-day
|
|
47
|
+
/**
|
|
48
|
+
* Namespace delimiter
|
|
49
|
+
* @private
|
|
50
|
+
* @static
|
|
51
|
+
*/
|
|
52
|
+
static NAMESPACE_DELIMITER = '::';
|
|
53
|
+
/**
|
|
54
|
+
* Namespace of cache key
|
|
55
|
+
*/
|
|
56
|
+
ns;
|
|
57
|
+
/**
|
|
58
|
+
* the final options to create this service.
|
|
59
|
+
*/
|
|
60
|
+
options;
|
|
61
|
+
/**
|
|
62
|
+
* maker of cache-key
|
|
63
|
+
*/
|
|
64
|
+
maker;
|
|
65
|
+
/**
|
|
66
|
+
* Cache backend instance
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
69
|
+
backend;
|
|
56
70
|
/**
|
|
57
71
|
* Factory method
|
|
58
72
|
*
|
|
@@ -61,11 +75,11 @@ class CacheService {
|
|
|
61
75
|
* @static
|
|
62
76
|
*/
|
|
63
77
|
static create(options, maker) {
|
|
64
|
-
const type =
|
|
65
|
-
const endpoint =
|
|
66
|
-
const ns =
|
|
67
|
-
const defTimeout = engine_1.$U.N(options
|
|
68
|
-
options =
|
|
78
|
+
const type = options?.type || 'redis';
|
|
79
|
+
const endpoint = options?.endpoint || engine_1.$U.env(CacheService.ENV_CACHE_ENDPOINT);
|
|
80
|
+
const ns = options?.ns || '';
|
|
81
|
+
const defTimeout = engine_1.$U.N(options?.defTimeout, engine_1.$U.N(engine_1.$U.env(CacheService.ENV_CACHE_DEFAULT_TIMEOUT), CacheService.DEF_CACHE_DEFAULT_TIMEOUT));
|
|
82
|
+
options = { ...options, type, endpoint, ns, defTimeout };
|
|
69
83
|
(0, engine_1._log)(NS, `constructing [${type}] cache ...`);
|
|
70
84
|
(0, engine_1._log)(NS, ` > endpoint =`, endpoint);
|
|
71
85
|
(0, engine_1._log)(NS, ` > ns =`, ns);
|
|
@@ -83,6 +97,24 @@ class CacheService {
|
|
|
83
97
|
}
|
|
84
98
|
return new CacheService(backend, { ns, options, maker });
|
|
85
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Protected constructor -> use CacheService.create()
|
|
102
|
+
* WARN! - do not create directly.˜
|
|
103
|
+
*
|
|
104
|
+
* @param backend cache backend object
|
|
105
|
+
* @param params params to create service.
|
|
106
|
+
* @protected
|
|
107
|
+
*/
|
|
108
|
+
constructor(backend, params) {
|
|
109
|
+
if (!backend)
|
|
110
|
+
throw new Error(`@backend (cache-backend) is required!`);
|
|
111
|
+
const ns = params?.ns || '';
|
|
112
|
+
(0, engine_1._inf)(NS, `! cache-service instantiated with [${backend.name}] backend. [ns=${ns}]`);
|
|
113
|
+
this.backend = backend;
|
|
114
|
+
this.ns = ns;
|
|
115
|
+
this.options = params.options;
|
|
116
|
+
this.maker = params?.maker;
|
|
117
|
+
}
|
|
86
118
|
/**
|
|
87
119
|
* Say hello
|
|
88
120
|
*/
|
|
@@ -105,41 +137,35 @@ class CacheService {
|
|
|
105
137
|
/**
|
|
106
138
|
* Close backend and connection
|
|
107
139
|
*/
|
|
108
|
-
close() {
|
|
109
|
-
|
|
110
|
-
yield this.backend.close();
|
|
111
|
-
});
|
|
140
|
+
async close() {
|
|
141
|
+
await this.backend.close();
|
|
112
142
|
}
|
|
113
143
|
/**
|
|
114
144
|
* Check whether the key is cached
|
|
115
145
|
*
|
|
116
146
|
* @return true if the key is cached
|
|
117
147
|
*/
|
|
118
|
-
exists(key) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return ret;
|
|
124
|
-
});
|
|
148
|
+
async exists(key) {
|
|
149
|
+
const namespacedKey = this.asNamespacedKey(key);
|
|
150
|
+
const ret = await this.backend.has(namespacedKey);
|
|
151
|
+
(0, engine_1._log)(NS, `.exists ${namespacedKey} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
152
|
+
return ret;
|
|
125
153
|
}
|
|
126
154
|
/**
|
|
127
155
|
* List all keys
|
|
128
156
|
*
|
|
129
157
|
* @return list of keys
|
|
130
158
|
*/
|
|
131
|
-
keys() {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return ret;
|
|
142
|
-
});
|
|
159
|
+
async keys() {
|
|
160
|
+
const namespacedKeys = await this.backend.keys();
|
|
161
|
+
const ret = namespacedKeys.reduce((keys, namespacedKey) => {
|
|
162
|
+
const [ns, key] = namespacedKey.split(CacheService.NAMESPACE_DELIMITER);
|
|
163
|
+
if (ns === this.ns)
|
|
164
|
+
keys.push(key);
|
|
165
|
+
return keys;
|
|
166
|
+
}, []);
|
|
167
|
+
(0, engine_1._log)(NS, `.keys / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
168
|
+
return ret;
|
|
143
169
|
}
|
|
144
170
|
/**
|
|
145
171
|
* Store a key
|
|
@@ -149,18 +175,16 @@ class CacheService {
|
|
|
149
175
|
* @param timeout (optional) TTL in seconds or Timeout object
|
|
150
176
|
* @return true on success
|
|
151
177
|
*/
|
|
152
|
-
set(key, val, timeout) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
return ret;
|
|
163
|
-
});
|
|
178
|
+
async set(key, val, timeout) {
|
|
179
|
+
if (!key)
|
|
180
|
+
throw new Error(`@key (CacheKey) is required.`);
|
|
181
|
+
if (val === undefined)
|
|
182
|
+
throw new Error(`@val (CacheValue) cannot be undefined.`);
|
|
183
|
+
const namespacedKey = this.asNamespacedKey(key);
|
|
184
|
+
const ttl = timeout && toTTL(timeout);
|
|
185
|
+
const ret = await this.backend.set(namespacedKey, val, ttl);
|
|
186
|
+
(0, engine_1._log)(NS, `.set ${namespacedKey} ${val} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
187
|
+
return ret;
|
|
164
188
|
}
|
|
165
189
|
/**
|
|
166
190
|
* Store multiple keys
|
|
@@ -168,61 +192,55 @@ class CacheService {
|
|
|
168
192
|
* @param entries
|
|
169
193
|
* @return true on success
|
|
170
194
|
*/
|
|
171
|
-
setMulti(entries) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
};
|
|
183
|
-
});
|
|
184
|
-
const ret = yield this.backend.mset(param);
|
|
185
|
-
(0, engine_1._log)(NS, `.setMulti ${entries.map(entry => entry.key)} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
186
|
-
return ret;
|
|
195
|
+
async setMulti(entries) {
|
|
196
|
+
const param = entries.map(({ key, val, timeout }, idx) => {
|
|
197
|
+
if (!key)
|
|
198
|
+
throw new Error(`.key (CacheKey) is required (at @entries[${idx}]).`);
|
|
199
|
+
if (val === undefined)
|
|
200
|
+
throw new Error(`.val (CacheValue) cannot be undefined (at @entries[${idx}]).`);
|
|
201
|
+
return {
|
|
202
|
+
key: this.asNamespacedKey(key),
|
|
203
|
+
val,
|
|
204
|
+
ttl: timeout && toTTL(timeout),
|
|
205
|
+
};
|
|
187
206
|
});
|
|
207
|
+
const ret = await this.backend.mset(param);
|
|
208
|
+
(0, engine_1._log)(NS, `.setMulti ${entries.map(entry => entry.key)} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
209
|
+
return ret;
|
|
188
210
|
}
|
|
189
211
|
/**
|
|
190
212
|
* Retrieve a key
|
|
191
213
|
*
|
|
192
214
|
* @param key
|
|
193
215
|
*/
|
|
194
|
-
get(key) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return ret;
|
|
202
|
-
});
|
|
216
|
+
async get(key) {
|
|
217
|
+
if (!key)
|
|
218
|
+
throw new Error(`@key (CacheKey) is required.`);
|
|
219
|
+
const namespacedKey = this.asNamespacedKey(key);
|
|
220
|
+
const ret = await this.backend.get(namespacedKey);
|
|
221
|
+
(0, engine_1._log)(NS, `.get ${namespacedKey} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
222
|
+
return ret;
|
|
203
223
|
}
|
|
204
224
|
/**
|
|
205
225
|
* Get multiple keys
|
|
206
226
|
*
|
|
207
227
|
* @param keys
|
|
208
228
|
*/
|
|
209
|
-
getMulti(keys) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return this.asNamespacedKey(key);
|
|
215
|
-
});
|
|
216
|
-
const map = yield this.backend.mget(namespacedKeys);
|
|
217
|
-
// Remove namespace prefix from keys
|
|
218
|
-
const ret = Object.entries(map).reduce((newMap, [namespacedKey, val]) => {
|
|
219
|
-
const key = namespacedKey.split(CacheService.NAMESPACE_DELIMITER)[1];
|
|
220
|
-
newMap[key] = val;
|
|
221
|
-
return newMap;
|
|
222
|
-
}, {});
|
|
223
|
-
(0, engine_1._log)(NS, `.getMulti ${namespacedKeys} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
224
|
-
return ret;
|
|
229
|
+
async getMulti(keys) {
|
|
230
|
+
const namespacedKeys = keys.map((key, idx) => {
|
|
231
|
+
if (!key)
|
|
232
|
+
throw new Error(`@key (CacheKey) is required (at @keys[${idx}]).`);
|
|
233
|
+
return this.asNamespacedKey(key);
|
|
225
234
|
});
|
|
235
|
+
const map = await this.backend.mget(namespacedKeys);
|
|
236
|
+
// Remove namespace prefix from keys
|
|
237
|
+
const ret = Object.entries(map).reduce((newMap, [namespacedKey, val]) => {
|
|
238
|
+
const key = namespacedKey.split(CacheService.NAMESPACE_DELIMITER)[1];
|
|
239
|
+
newMap[key] = val;
|
|
240
|
+
return newMap;
|
|
241
|
+
}, {});
|
|
242
|
+
(0, engine_1._log)(NS, `.getMulti ${namespacedKeys} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
243
|
+
return ret;
|
|
226
244
|
}
|
|
227
245
|
/**
|
|
228
246
|
* Increment the integer value of a key
|
|
@@ -230,17 +248,15 @@ class CacheService {
|
|
|
230
248
|
* @param key
|
|
231
249
|
* @param inc number to increment
|
|
232
250
|
*/
|
|
233
|
-
increment(key, inc) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return ret;
|
|
243
|
-
});
|
|
251
|
+
async increment(key, inc) {
|
|
252
|
+
if (!key)
|
|
253
|
+
throw new Error(`@key (CacheKey) is required.`);
|
|
254
|
+
if (inc === undefined)
|
|
255
|
+
throw new Error(`@inc (number) cannot be undefined.`);
|
|
256
|
+
const namespacedKey = this.asNamespacedKey(key);
|
|
257
|
+
const ret = await this.backend.incr(namespacedKey, inc);
|
|
258
|
+
(0, engine_1._log)(NS, `.increment ${namespacedKey} ${inc} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
259
|
+
return ret;
|
|
244
260
|
}
|
|
245
261
|
/**
|
|
246
262
|
* same as increment()
|
|
@@ -251,52 +267,48 @@ class CacheService {
|
|
|
251
267
|
/**
|
|
252
268
|
* Set the value of a key and return its old value
|
|
253
269
|
*/
|
|
254
|
-
getAndSet(key, val) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
ttl = Math.ceil(ttl / 1000);
|
|
271
|
-
}
|
|
272
|
-
if (!(yield this.backend.set(namespacedKey, val, ttl)))
|
|
273
|
-
throw new Error(`getAndSet() failed`);
|
|
270
|
+
async getAndSet(key, val) {
|
|
271
|
+
if (!key)
|
|
272
|
+
throw new Error(`@key (CacheKey) is required.`);
|
|
273
|
+
if (val === undefined)
|
|
274
|
+
throw new Error(`@val (CacheValue) cannot be undefined.`);
|
|
275
|
+
const namespacedKey = this.asNamespacedKey(key);
|
|
276
|
+
let ret;
|
|
277
|
+
if (this.backend.getset) {
|
|
278
|
+
ret = await this.backend.getset(namespacedKey, val);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
ret = await this.backend.get(namespacedKey);
|
|
282
|
+
// Best effort to keep remaining TTL
|
|
283
|
+
let ttl = await this.backend.ttl(namespacedKey);
|
|
284
|
+
if (ttl !== undefined) {
|
|
285
|
+
ttl = Math.ceil(ttl / 1000);
|
|
274
286
|
}
|
|
275
|
-
(
|
|
276
|
-
|
|
277
|
-
}
|
|
287
|
+
if (!(await this.backend.set(namespacedKey, val, ttl)))
|
|
288
|
+
throw new Error(`getAndSet() failed`);
|
|
289
|
+
}
|
|
290
|
+
(0, engine_1._log)(NS, `.getAndSet ${namespacedKey} ${val} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
291
|
+
return ret;
|
|
278
292
|
}
|
|
279
293
|
/**
|
|
280
294
|
* Get and delete the key
|
|
281
295
|
*
|
|
282
296
|
* @param key
|
|
283
297
|
*/
|
|
284
|
-
getAndDelete(key) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
return ret;
|
|
299
|
-
});
|
|
298
|
+
async getAndDelete(key) {
|
|
299
|
+
if (!key)
|
|
300
|
+
throw new Error(`@key (CacheKey) is required.`);
|
|
301
|
+
const namespacedKey = this.asNamespacedKey(key);
|
|
302
|
+
let ret;
|
|
303
|
+
if (this.backend.pop) {
|
|
304
|
+
ret = await this.backend.pop(namespacedKey);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
ret = await this.backend.get(namespacedKey);
|
|
308
|
+
await this.backend.del(namespacedKey);
|
|
309
|
+
}
|
|
310
|
+
(0, engine_1._log)(NS, `.getAndDelete ${namespacedKey} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
311
|
+
return ret;
|
|
300
312
|
}
|
|
301
313
|
/**
|
|
302
314
|
* Delete a key
|
|
@@ -304,15 +316,13 @@ class CacheService {
|
|
|
304
316
|
* @param key
|
|
305
317
|
* @return true on success
|
|
306
318
|
*/
|
|
307
|
-
delete(key) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return ret;
|
|
315
|
-
});
|
|
319
|
+
async delete(key) {
|
|
320
|
+
if (!key)
|
|
321
|
+
throw new Error(`@key (CacheKey) is required.`);
|
|
322
|
+
const namespacedKey = this.asNamespacedKey(key);
|
|
323
|
+
const ret = await this.backend.del(namespacedKey);
|
|
324
|
+
(0, engine_1._log)(NS, `.delete ${namespacedKey} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
325
|
+
return ret;
|
|
316
326
|
}
|
|
317
327
|
/**
|
|
318
328
|
* Delete multiple keys
|
|
@@ -320,18 +330,16 @@ class CacheService {
|
|
|
320
330
|
* @param keys
|
|
321
331
|
* @return number of deleted entries
|
|
322
332
|
*/
|
|
323
|
-
deleteMulti(keys) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
return this.asNamespacedKey(key);
|
|
329
|
-
});
|
|
330
|
-
const promises = namespacedKeys.map(namespacedKey => this.backend.del(namespacedKey));
|
|
331
|
-
const ret = yield Promise.all(promises);
|
|
332
|
-
(0, engine_1._log)(NS, `.deleteMulti ${namespacedKeys} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
333
|
-
return ret;
|
|
333
|
+
async deleteMulti(keys) {
|
|
334
|
+
const namespacedKeys = keys.map((key, idx) => {
|
|
335
|
+
if (!key)
|
|
336
|
+
throw new Error(`@key (CacheKey) is required (at @keys[${idx}]).`);
|
|
337
|
+
return this.asNamespacedKey(key);
|
|
334
338
|
});
|
|
339
|
+
const promises = namespacedKeys.map(namespacedKey => this.backend.del(namespacedKey));
|
|
340
|
+
const ret = await Promise.all(promises);
|
|
341
|
+
(0, engine_1._log)(NS, `.deleteMulti ${namespacedKeys} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
342
|
+
return ret;
|
|
335
343
|
}
|
|
336
344
|
/**
|
|
337
345
|
* Set or update the timeout of a key
|
|
@@ -340,15 +348,13 @@ class CacheService {
|
|
|
340
348
|
* @param timeout TTL in seconds or Timeout object
|
|
341
349
|
* @return true on success
|
|
342
350
|
*/
|
|
343
|
-
setTimeout(key, timeout) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
return ret;
|
|
351
|
-
});
|
|
351
|
+
async setTimeout(key, timeout) {
|
|
352
|
+
if (!key)
|
|
353
|
+
throw new Error(`@key (CacheKey) is required.`);
|
|
354
|
+
const namespacedKey = this.asNamespacedKey(key);
|
|
355
|
+
const ret = await this.backend.expire(namespacedKey, toTTL(timeout));
|
|
356
|
+
(0, engine_1._log)(NS, `.setTimeout ${namespacedKey} ${timeout} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
357
|
+
return ret;
|
|
352
358
|
}
|
|
353
359
|
/**
|
|
354
360
|
* Get remaining time to live in milliseconds
|
|
@@ -358,30 +364,26 @@ class CacheService {
|
|
|
358
364
|
* - undefined if the key does not exist
|
|
359
365
|
* - 0 if the key has no timeout
|
|
360
366
|
*/
|
|
361
|
-
getTimeout(key) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return ret;
|
|
369
|
-
});
|
|
367
|
+
async getTimeout(key) {
|
|
368
|
+
if (!key)
|
|
369
|
+
throw new Error(`@key (CacheKey) is required.`);
|
|
370
|
+
const namespacedKey = this.asNamespacedKey(key);
|
|
371
|
+
const ret = await this.backend.ttl(namespacedKey);
|
|
372
|
+
(0, engine_1._log)(NS, `.getTimeout ${namespacedKey} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
373
|
+
return ret;
|
|
370
374
|
}
|
|
371
375
|
/**
|
|
372
376
|
* Remove the timeout from a key
|
|
373
377
|
*
|
|
374
378
|
* @param key
|
|
375
379
|
*/
|
|
376
|
-
removeTimeout(key) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
return ret;
|
|
384
|
-
});
|
|
380
|
+
async removeTimeout(key) {
|
|
381
|
+
if (!key)
|
|
382
|
+
throw new Error(`@key (CacheKey) is required.`);
|
|
383
|
+
const namespacedKey = this.asNamespacedKey(key);
|
|
384
|
+
const ret = await this.backend.expire(namespacedKey, 0);
|
|
385
|
+
(0, engine_1._log)(NS, `.removeTimeout ${namespacedKey} / ret =`, typeof ret === 'string' ? ret : engine_1.$U.json(ret));
|
|
386
|
+
return ret;
|
|
385
387
|
}
|
|
386
388
|
/**
|
|
387
389
|
* Get namespace prefixed cache key
|
|
@@ -397,31 +399,17 @@ class CacheService {
|
|
|
397
399
|
}
|
|
398
400
|
}
|
|
399
401
|
exports.CacheService = CacheService;
|
|
400
|
-
/**
|
|
401
|
-
* Environment variable name for cache server endpoint
|
|
402
|
-
* @static
|
|
403
|
-
*/
|
|
404
|
-
CacheService.ENV_CACHE_ENDPOINT = 'CACHE_ENDPOINT';
|
|
405
|
-
/**
|
|
406
|
-
* Environment variable name for default cache timeout
|
|
407
|
-
* @static
|
|
408
|
-
*/
|
|
409
|
-
CacheService.ENV_CACHE_DEFAULT_TIMEOUT = 'CACHE_DEFAULT_TIMEOUT';
|
|
410
|
-
/**
|
|
411
|
-
* Default cache timeout
|
|
412
|
-
* @static
|
|
413
|
-
*/
|
|
414
|
-
CacheService.DEF_CACHE_DEFAULT_TIMEOUT = 24 * 60 * 60; // 1-day
|
|
415
|
-
/**
|
|
416
|
-
* Namespace delimiter
|
|
417
|
-
* @private
|
|
418
|
-
* @static
|
|
419
|
-
*/
|
|
420
|
-
CacheService.NAMESPACE_DELIMITER = '::';
|
|
421
402
|
/**
|
|
422
403
|
* class `DummyCacheService`: use 'node-cache' library
|
|
423
404
|
*/
|
|
424
405
|
class DummyCacheService extends CacheService {
|
|
406
|
+
/**
|
|
407
|
+
* Singleton node-cache backend
|
|
408
|
+
*
|
|
409
|
+
* @private
|
|
410
|
+
* @static
|
|
411
|
+
*/
|
|
412
|
+
static backend;
|
|
425
413
|
/**
|
|
426
414
|
* Factory method
|
|
427
415
|
*
|
|
@@ -429,9 +417,9 @@ class DummyCacheService extends CacheService {
|
|
|
429
417
|
* @static
|
|
430
418
|
*/
|
|
431
419
|
static create(options) {
|
|
432
|
-
const ns =
|
|
433
|
-
const defTimeout = engine_1.$U.N(options
|
|
434
|
-
options =
|
|
420
|
+
const ns = options?.ns || '';
|
|
421
|
+
const defTimeout = engine_1.$U.N(options?.defTimeout, engine_1.$U.N(engine_1.$U.env(CacheService.ENV_CACHE_DEFAULT_TIMEOUT), CacheService.DEF_CACHE_DEFAULT_TIMEOUT));
|
|
422
|
+
options = { ...options, ns, defTimeout };
|
|
435
423
|
(0, engine_1._log)(NS, `constructing dummy cache ...`);
|
|
436
424
|
// NOTE: Use singleton backend instance
|
|
437
425
|
// because node-cache is volatile and client instance does not share keys with other instance
|
|
@@ -451,12 +439,9 @@ exports.DummyCacheService = DummyCacheService;
|
|
|
451
439
|
* function `sleep`
|
|
452
440
|
* @param ms duration in milliseconds
|
|
453
441
|
*/
|
|
454
|
-
function sleep(ms) {
|
|
455
|
-
return
|
|
456
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
457
|
-
});
|
|
442
|
+
async function sleep(ms) {
|
|
443
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
458
444
|
}
|
|
459
|
-
exports.sleep = sleep;
|
|
460
445
|
/**
|
|
461
446
|
* Get TTL from timeout
|
|
462
447
|
* @param timeout timeout in seconds or Timeout object
|
|
@@ -480,7 +465,6 @@ function toTTL(timeout) {
|
|
|
480
465
|
}
|
|
481
466
|
throw new Error(`@timeout (number | Timeout) is invalid.`);
|
|
482
467
|
}
|
|
483
|
-
exports.toTTL = toTTL;
|
|
484
468
|
/**
|
|
485
469
|
* Get timestamp of expiration from TTL
|
|
486
470
|
* @param ttl remaining time to live in seconds
|
|
@@ -489,7 +473,6 @@ exports.toTTL = toTTL;
|
|
|
489
473
|
function fromTTL(ttl) {
|
|
490
474
|
return ttl > 0 ? Date.now() + ttl * 1000 : 0;
|
|
491
475
|
}
|
|
492
|
-
exports.fromTTL = fromTTL;
|
|
493
476
|
/** ********************************************************************************************************************
|
|
494
477
|
* Internal Classes
|
|
495
478
|
** ********************************************************************************************************************/
|
|
@@ -498,117 +481,98 @@ exports.fromTTL = fromTTL;
|
|
|
498
481
|
* @internal
|
|
499
482
|
*/
|
|
500
483
|
class NodeCacheBackend {
|
|
484
|
+
/**
|
|
485
|
+
* node-cache client
|
|
486
|
+
* @private
|
|
487
|
+
*/
|
|
488
|
+
cache;
|
|
489
|
+
/**
|
|
490
|
+
* backend type
|
|
491
|
+
*/
|
|
492
|
+
name = 'node-cache';
|
|
501
493
|
/**
|
|
502
494
|
* Public constructor
|
|
503
495
|
*/
|
|
504
496
|
constructor(defTTL = 0) {
|
|
505
|
-
/**
|
|
506
|
-
* backend type
|
|
507
|
-
*/
|
|
508
|
-
this.name = 'node-cache';
|
|
509
497
|
this.cache = new node_cache_1.default({ stdTTL: defTTL });
|
|
510
498
|
}
|
|
511
499
|
/**
|
|
512
500
|
* CacheBackend.set implementation
|
|
513
501
|
*/
|
|
514
|
-
set(key, val, ttl) {
|
|
515
|
-
return
|
|
516
|
-
return this.cache.set(key, val, ttl);
|
|
517
|
-
});
|
|
502
|
+
async set(key, val, ttl) {
|
|
503
|
+
return this.cache.set(key, val, ttl);
|
|
518
504
|
}
|
|
519
505
|
/**
|
|
520
506
|
* CacheBackend.get implementation
|
|
521
507
|
*/
|
|
522
|
-
get(key) {
|
|
523
|
-
return
|
|
524
|
-
return this.cache.get(key);
|
|
525
|
-
});
|
|
508
|
+
async get(key) {
|
|
509
|
+
return this.cache.get(key);
|
|
526
510
|
}
|
|
527
511
|
/**
|
|
528
512
|
* CacheBackend.mset implementation
|
|
529
513
|
*/
|
|
530
|
-
mset(entries) {
|
|
531
|
-
return
|
|
532
|
-
return this.cache.mset(entries);
|
|
533
|
-
});
|
|
514
|
+
async mset(entries) {
|
|
515
|
+
return this.cache.mset(entries);
|
|
534
516
|
}
|
|
535
517
|
/**
|
|
536
518
|
* CacheBackend.mget implementation
|
|
537
519
|
*/
|
|
538
|
-
mget(keys) {
|
|
539
|
-
return
|
|
540
|
-
return this.cache.mget(keys);
|
|
541
|
-
});
|
|
520
|
+
async mget(keys) {
|
|
521
|
+
return this.cache.mget(keys);
|
|
542
522
|
}
|
|
543
523
|
/**
|
|
544
524
|
* CacheBackend.pop implementation
|
|
545
525
|
*/
|
|
546
|
-
pop(key) {
|
|
547
|
-
return
|
|
548
|
-
return this.cache.take(key);
|
|
549
|
-
});
|
|
526
|
+
async pop(key) {
|
|
527
|
+
return this.cache.take(key);
|
|
550
528
|
}
|
|
551
529
|
/**
|
|
552
530
|
* CacheBackend.incr implementation
|
|
553
531
|
*/
|
|
554
|
-
incr(key, increment) {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
return newVal;
|
|
562
|
-
});
|
|
532
|
+
async incr(key, increment) {
|
|
533
|
+
const org = this.cache.get(key);
|
|
534
|
+
if (typeof org !== 'number')
|
|
535
|
+
throw new Error(`@key [${key}] does not hold a number value.`);
|
|
536
|
+
const newVal = org + increment;
|
|
537
|
+
this.cache.set(key, newVal);
|
|
538
|
+
return newVal;
|
|
563
539
|
}
|
|
564
540
|
/**
|
|
565
541
|
* CacheBackend.keys implementation
|
|
566
542
|
*/
|
|
567
|
-
keys() {
|
|
568
|
-
return
|
|
569
|
-
return this.cache.keys();
|
|
570
|
-
});
|
|
543
|
+
async keys() {
|
|
544
|
+
return this.cache.keys();
|
|
571
545
|
}
|
|
572
546
|
/**
|
|
573
547
|
* CacheBackend.has implementation
|
|
574
548
|
*/
|
|
575
|
-
has(key) {
|
|
576
|
-
return
|
|
577
|
-
return this.cache.has(key);
|
|
578
|
-
});
|
|
549
|
+
async has(key) {
|
|
550
|
+
return this.cache.has(key);
|
|
579
551
|
}
|
|
580
552
|
/**
|
|
581
553
|
* CacheBackend.del implementation
|
|
582
554
|
*/
|
|
583
|
-
del(key) {
|
|
584
|
-
return
|
|
585
|
-
return this.cache.del(key) === 1;
|
|
586
|
-
});
|
|
555
|
+
async del(key) {
|
|
556
|
+
return this.cache.del(key) === 1;
|
|
587
557
|
}
|
|
588
558
|
/**
|
|
589
559
|
* CacheBackend.expire implementation
|
|
590
560
|
*/
|
|
591
|
-
expire(key, ttl) {
|
|
592
|
-
return
|
|
593
|
-
return this.cache.ttl(key, ttl);
|
|
594
|
-
});
|
|
561
|
+
async expire(key, ttl) {
|
|
562
|
+
return this.cache.ttl(key, ttl);
|
|
595
563
|
}
|
|
596
564
|
/**
|
|
597
565
|
* CacheBackend.ttl implementation
|
|
598
566
|
*/
|
|
599
|
-
ttl(key) {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
return ts && ts - Date.now();
|
|
603
|
-
});
|
|
567
|
+
async ttl(key) {
|
|
568
|
+
const ts = this.cache.getTtl(key); // Timestamp in milliseconds
|
|
569
|
+
return ts && ts - Date.now();
|
|
604
570
|
}
|
|
605
571
|
/**
|
|
606
572
|
* CacheBackend.close implementation
|
|
607
573
|
*/
|
|
608
|
-
close() {
|
|
609
|
-
|
|
610
|
-
this.cache.close();
|
|
611
|
-
});
|
|
574
|
+
async close() {
|
|
575
|
+
this.cache.close();
|
|
612
576
|
}
|
|
613
577
|
}
|
|
614
578
|
/**
|
|
@@ -616,14 +580,24 @@ class NodeCacheBackend {
|
|
|
616
580
|
* @internal
|
|
617
581
|
*/
|
|
618
582
|
class MemcachedBackend {
|
|
583
|
+
/**
|
|
584
|
+
* Memcached promisified APIs
|
|
585
|
+
* @private
|
|
586
|
+
*/
|
|
587
|
+
api;
|
|
588
|
+
/**
|
|
589
|
+
* Default TTL as number in seconds
|
|
590
|
+
* @private
|
|
591
|
+
*/
|
|
592
|
+
defTTL;
|
|
593
|
+
/**
|
|
594
|
+
* backend type
|
|
595
|
+
*/
|
|
596
|
+
name = 'memcached';
|
|
619
597
|
/**
|
|
620
598
|
* Public constructor
|
|
621
599
|
*/
|
|
622
600
|
constructor(endpoint, defTTL = 0) {
|
|
623
|
-
/**
|
|
624
|
-
* backend type
|
|
625
|
-
*/
|
|
626
|
-
this.name = 'memcached';
|
|
627
601
|
const memcached = new memcached_1.default(endpoint || 'localhost:11211');
|
|
628
602
|
// Build promisified API map
|
|
629
603
|
this.api = {
|
|
@@ -654,159 +628,137 @@ class MemcachedBackend {
|
|
|
654
628
|
/**
|
|
655
629
|
* CacheBackend.set implementation
|
|
656
630
|
*/
|
|
657
|
-
set(key, val, ttl = this.defTTL) {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
return yield this.api.set(key, entry, ttl);
|
|
662
|
-
});
|
|
631
|
+
async set(key, val, ttl = this.defTTL) {
|
|
632
|
+
const entry = { val, exp: fromTTL(ttl) };
|
|
633
|
+
(0, engine_1._log)(NS, `[${this.name}-backend] storing to key [${key}] =`, engine_1.$U.json(entry));
|
|
634
|
+
return await this.api.set(key, entry, ttl);
|
|
663
635
|
}
|
|
664
636
|
/**
|
|
665
637
|
* CacheBackend.get implementation
|
|
666
638
|
*/
|
|
667
|
-
get(key) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
return entry && entry.val;
|
|
672
|
-
});
|
|
639
|
+
async get(key) {
|
|
640
|
+
const entry = await this.api.get(key);
|
|
641
|
+
(0, engine_1._log)(NS, `[${this.name}-backend] entry fetched =`, engine_1.$U.json(entry));
|
|
642
|
+
return entry && entry.val;
|
|
673
643
|
}
|
|
674
644
|
/**
|
|
675
645
|
* CacheBackend.mset implementation
|
|
676
646
|
*/
|
|
677
|
-
mset(entries) {
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
return this.api.set(key, entry, ttl);
|
|
684
|
-
});
|
|
685
|
-
const results = yield Promise.all(promises);
|
|
686
|
-
return results.every(result => result === true);
|
|
647
|
+
async mset(entries) {
|
|
648
|
+
(0, engine_1._log)(NS, `[${this.name}-backend] storing multiple keys ...`);
|
|
649
|
+
const promises = entries.map(({ key, val, ttl = this.defTTL }, idx) => {
|
|
650
|
+
const entry = { val, exp: fromTTL(ttl) };
|
|
651
|
+
(0, engine_1._log)(NS, ` ${idx}) key [${key}] =`, engine_1.$U.json(entry));
|
|
652
|
+
return this.api.set(key, entry, ttl);
|
|
687
653
|
});
|
|
654
|
+
const results = await Promise.all(promises);
|
|
655
|
+
return results.every(result => result === true);
|
|
688
656
|
}
|
|
689
657
|
/**
|
|
690
658
|
* CacheBackend.mget implementation
|
|
691
659
|
*/
|
|
692
|
-
mget(keys) {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
map[key] = entry.val;
|
|
699
|
-
});
|
|
700
|
-
return map;
|
|
660
|
+
async mget(keys) {
|
|
661
|
+
const map = await this.api.getMulti(keys);
|
|
662
|
+
(0, engine_1._log)(NS, `[${this.name}-backend] entry map fetched =`, engine_1.$U.json(map));
|
|
663
|
+
Object.keys(map).forEach(key => {
|
|
664
|
+
const entry = map[key];
|
|
665
|
+
map[key] = entry.val;
|
|
701
666
|
});
|
|
667
|
+
return map;
|
|
702
668
|
}
|
|
703
669
|
/**
|
|
704
670
|
* CacheBackend.incr implementation
|
|
705
671
|
*/
|
|
706
|
-
incr(key, increment) {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
if
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
return increment;
|
|
720
|
-
}
|
|
721
|
-
else {
|
|
722
|
-
const { [key]: oldEntry, cas } = result;
|
|
723
|
-
if (typeof oldEntry.val !== 'number')
|
|
724
|
-
throw new Error(`.key [${key}] has non-numeric value.`);
|
|
725
|
-
// Preserve remaining lifetime w/ best effort strategy, not accurate
|
|
726
|
-
const now = Date.now();
|
|
727
|
-
const ttl = oldEntry.exp && Math.round((oldEntry.exp - now) / 1000);
|
|
728
|
-
const entry = {
|
|
729
|
-
val: oldEntry.val + increment,
|
|
730
|
-
exp: ttl && now + ttl * 1000,
|
|
731
|
-
};
|
|
732
|
-
if (yield this.api.cas(key, entry, cas, ttl))
|
|
733
|
-
return entry.val;
|
|
734
|
-
}
|
|
672
|
+
async incr(key, increment) {
|
|
673
|
+
// NOTE:
|
|
674
|
+
// Memcached는 음수에 대한 incr/decr를 지원하지 않으며 0 미만으로 decr 되지 않는다.
|
|
675
|
+
// 이런 이유로 sets & cas 조합을 이용해 직접 구현함
|
|
676
|
+
(0, engine_1._log)(NS, `[${this.name}-backend] incrementing (${increment}) to key [${key}] ...`);
|
|
677
|
+
// Use get/check-and-save + retry strategy for consistency
|
|
678
|
+
for (let retry = 0; retry < 5; await sleep(10), retry++) {
|
|
679
|
+
const result = await this.api.gets(key); // Get entry w/ CAS id
|
|
680
|
+
if (result === undefined) {
|
|
681
|
+
// Initialize to increment value if the key does not exist
|
|
682
|
+
if (!(await this.set(key, increment, 0)))
|
|
683
|
+
break;
|
|
684
|
+
return increment;
|
|
735
685
|
}
|
|
736
|
-
|
|
737
|
-
|
|
686
|
+
else {
|
|
687
|
+
const { [key]: oldEntry, cas } = result;
|
|
688
|
+
if (typeof oldEntry.val !== 'number')
|
|
689
|
+
throw new Error(`.key [${key}] has non-numeric value.`);
|
|
690
|
+
// Preserve remaining lifetime w/ best effort strategy, not accurate
|
|
691
|
+
const now = Date.now();
|
|
692
|
+
const ttl = oldEntry.exp && Math.round((oldEntry.exp - now) / 1000);
|
|
693
|
+
const entry = {
|
|
694
|
+
val: oldEntry.val + increment,
|
|
695
|
+
exp: ttl && now + ttl * 1000,
|
|
696
|
+
};
|
|
697
|
+
if (await this.api.cas(key, entry, cas, ttl))
|
|
698
|
+
return entry.val;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
throw new Error(`[memcached] failed to increment key [${key}].`);
|
|
738
702
|
}
|
|
739
703
|
/**
|
|
740
704
|
* CacheBackend.keys implementation
|
|
741
705
|
*/
|
|
742
|
-
keys() {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
return cachedump.map(({ key }) => key);
|
|
755
|
-
});
|
|
706
|
+
async keys() {
|
|
707
|
+
// NOTE:
|
|
708
|
+
// memcached는 원래 keys 기능을 지원하지 않으며
|
|
709
|
+
// 아래와 같이 cachedump를 사용하여 가능하지만 set한 key가 dump 될 때 까지 상당한 시간이 소요되는 것으로 보인다.
|
|
710
|
+
// 따라서 이 operation의 결과를 신뢰하지 않도록 한다.
|
|
711
|
+
const item = (await this.api.items())[0];
|
|
712
|
+
if (!item || Object.keys(item).length === 0)
|
|
713
|
+
return [];
|
|
714
|
+
const [server, slabid] = [item.server, Number(Object.keys(item)[0])];
|
|
715
|
+
const number = item[slabid].number;
|
|
716
|
+
const cachedump = await this.api.cachedump(server, slabid, number);
|
|
717
|
+
return cachedump.map(({ key }) => key);
|
|
756
718
|
}
|
|
757
719
|
/**
|
|
758
720
|
* CacheBackend.has implementation
|
|
759
721
|
*/
|
|
760
|
-
has(key) {
|
|
761
|
-
return
|
|
762
|
-
return (yield this.api.get(key)) !== undefined;
|
|
763
|
-
});
|
|
722
|
+
async has(key) {
|
|
723
|
+
return (await this.api.get(key)) !== undefined;
|
|
764
724
|
}
|
|
765
725
|
/**
|
|
766
726
|
* CacheBackend.del implementation
|
|
767
727
|
*/
|
|
768
|
-
del(key) {
|
|
769
|
-
return
|
|
770
|
-
return yield this.api.del(key);
|
|
771
|
-
});
|
|
728
|
+
async del(key) {
|
|
729
|
+
return await this.api.del(key);
|
|
772
730
|
}
|
|
773
731
|
/**
|
|
774
732
|
* CacheBackend.expire implementation
|
|
775
733
|
*/
|
|
776
|
-
expire(key, ttl) {
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
return saved;
|
|
792
|
-
});
|
|
734
|
+
async expire(key, ttl) {
|
|
735
|
+
let saved = false;
|
|
736
|
+
for (let retry = 0; !saved && retry < 5; await sleep(10), retry++) {
|
|
737
|
+
const result = await this.api.gets(key); // Get entry w/ CAS id
|
|
738
|
+
if (result === undefined)
|
|
739
|
+
break; // If key does not exist or already expired
|
|
740
|
+
// Refresh timeout
|
|
741
|
+
const { [key]: oldEntry, cas } = result;
|
|
742
|
+
const newEntry = {
|
|
743
|
+
val: oldEntry.val,
|
|
744
|
+
exp: ttl && Date.now() + ttl * 1000,
|
|
745
|
+
};
|
|
746
|
+
saved = await this.api.cas(key, newEntry, cas, ttl);
|
|
747
|
+
}
|
|
748
|
+
return saved;
|
|
793
749
|
}
|
|
794
750
|
/**
|
|
795
751
|
* CacheBackend.ttl implementation
|
|
796
752
|
*/
|
|
797
|
-
ttl(key) {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
return (entry === null || entry === void 0 ? void 0 : entry.exp) && entry.exp - Date.now();
|
|
801
|
-
});
|
|
753
|
+
async ttl(key) {
|
|
754
|
+
const entry = await this.api.get(key); // undefined if key does not exist
|
|
755
|
+
return entry?.exp && entry.exp - Date.now();
|
|
802
756
|
}
|
|
803
757
|
/**
|
|
804
758
|
* CacheBackend.close implementation
|
|
805
759
|
*/
|
|
806
|
-
close() {
|
|
807
|
-
|
|
808
|
-
this.api.end();
|
|
809
|
-
});
|
|
760
|
+
async close() {
|
|
761
|
+
this.api.end();
|
|
810
762
|
}
|
|
811
763
|
}
|
|
812
764
|
/**
|
|
@@ -814,156 +766,140 @@ class MemcachedBackend {
|
|
|
814
766
|
* @internal
|
|
815
767
|
*/
|
|
816
768
|
class RedisBackend {
|
|
769
|
+
/**
|
|
770
|
+
* ioredis client
|
|
771
|
+
* @private
|
|
772
|
+
*/
|
|
773
|
+
redis;
|
|
774
|
+
/**
|
|
775
|
+
* Default TTL as number in seconds
|
|
776
|
+
* @private
|
|
777
|
+
*/
|
|
778
|
+
defTTL;
|
|
779
|
+
/**
|
|
780
|
+
* backend type
|
|
781
|
+
*/
|
|
782
|
+
name = 'redis';
|
|
817
783
|
/**
|
|
818
784
|
* Public constructor
|
|
819
785
|
*/
|
|
820
786
|
constructor(endpoint, defTTL = 0) {
|
|
821
|
-
/**
|
|
822
|
-
* backend type
|
|
823
|
-
*/
|
|
824
|
-
this.name = 'redis';
|
|
825
787
|
this.redis = new ioredis_1.default(endpoint || 'localhost:6379');
|
|
826
788
|
this.defTTL = defTTL;
|
|
827
789
|
}
|
|
828
790
|
/**
|
|
829
791
|
* CacheBackend.set implementation
|
|
830
792
|
*/
|
|
831
|
-
set(key, val, ttl = this.defTTL) {
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
return true; // 'set' command always return OK
|
|
836
|
-
});
|
|
793
|
+
async set(key, val, ttl = this.defTTL) {
|
|
794
|
+
const data = JSON.stringify(val); // Serialize
|
|
795
|
+
ttl > 0 ? await this.redis.set(key, data, 'EX', ttl) : await this.redis.set(key, data);
|
|
796
|
+
return true; // 'set' command always return OK
|
|
837
797
|
}
|
|
838
798
|
/**
|
|
839
799
|
* CacheBackend.get implementation
|
|
840
800
|
*/
|
|
841
|
-
get(key) {
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
return JSON.parse(data); // Deserialize
|
|
846
|
-
});
|
|
801
|
+
async get(key) {
|
|
802
|
+
const data = await this.redis.get(key);
|
|
803
|
+
if (data !== null)
|
|
804
|
+
return JSON.parse(data); // Deserialize
|
|
847
805
|
}
|
|
848
806
|
/**
|
|
849
807
|
* CacheBackend.mset implementation
|
|
850
808
|
*/
|
|
851
|
-
mset(entries) {
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
return true;
|
|
862
|
-
});
|
|
809
|
+
async mset(entries) {
|
|
810
|
+
// Create transaction pipeline
|
|
811
|
+
// -> MSET command를 사용할 수도 있으나 ttl 지정이 불가능하여 pipeline으로 구현함
|
|
812
|
+
const pipeline = entries.reduce((pipeline, { key, val, ttl = this.defTTL }) => {
|
|
813
|
+
const data = JSON.stringify(val); // Serialize
|
|
814
|
+
return ttl > 0 ? pipeline.set(key, data, 'EX', ttl) : pipeline.set(key, data);
|
|
815
|
+
}, this.redis.multi());
|
|
816
|
+
// Execute transaction
|
|
817
|
+
await pipeline.exec(); // Always OK
|
|
818
|
+
return true;
|
|
863
819
|
}
|
|
864
820
|
/**
|
|
865
821
|
* CacheBackend.mget implementation
|
|
866
822
|
*/
|
|
867
|
-
mget(keys) {
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
}, {});
|
|
878
|
-
});
|
|
823
|
+
async mget(keys) {
|
|
824
|
+
const list = await this.redis.mget(keys);
|
|
825
|
+
// Deserialize and map array into object
|
|
826
|
+
return list.reduce((map, data, idx) => {
|
|
827
|
+
if (data !== null) {
|
|
828
|
+
const key = keys[idx];
|
|
829
|
+
map[key] = JSON.parse(data); // Deserialize
|
|
830
|
+
}
|
|
831
|
+
return map;
|
|
832
|
+
}, {});
|
|
879
833
|
}
|
|
880
834
|
/**
|
|
881
835
|
* CacheBackend.getset implementation
|
|
882
836
|
*/
|
|
883
|
-
getset(key, val) {
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
return JSON.parse(oldData); // Deserialize
|
|
889
|
-
});
|
|
837
|
+
async getset(key, val) {
|
|
838
|
+
const newData = JSON.stringify(val); // Serialize
|
|
839
|
+
const oldData = await this.redis.getset(key, newData);
|
|
840
|
+
if (oldData !== null)
|
|
841
|
+
return JSON.parse(oldData); // Deserialize
|
|
890
842
|
}
|
|
891
843
|
/**
|
|
892
844
|
* CacheBackend.pop implementation
|
|
893
845
|
*/
|
|
894
|
-
pop(key) {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
return JSON.parse(data);
|
|
903
|
-
});
|
|
846
|
+
async pop(key) {
|
|
847
|
+
const [[err, data]] = await this.redis
|
|
848
|
+
.multi()
|
|
849
|
+
.get(key) // read
|
|
850
|
+
.del(key) // and delete
|
|
851
|
+
.exec();
|
|
852
|
+
if (!err && data !== null)
|
|
853
|
+
return JSON.parse(data);
|
|
904
854
|
}
|
|
905
855
|
/**
|
|
906
856
|
* CacheBackend.incr implementation
|
|
907
857
|
*/
|
|
908
|
-
incr(key, increment) {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
return Number(ret);
|
|
913
|
-
});
|
|
858
|
+
async incr(key, increment) {
|
|
859
|
+
// Support both integer and floating point
|
|
860
|
+
const ret = await this.redis.incrbyfloat(key, increment);
|
|
861
|
+
return Number(ret);
|
|
914
862
|
}
|
|
915
863
|
/**
|
|
916
864
|
* CacheBackend.keys implementation
|
|
917
865
|
*/
|
|
918
|
-
keys() {
|
|
919
|
-
return
|
|
920
|
-
return yield this.redis.keys('*');
|
|
921
|
-
});
|
|
866
|
+
async keys() {
|
|
867
|
+
return await this.redis.keys('*');
|
|
922
868
|
}
|
|
923
869
|
/**
|
|
924
870
|
* CacheBackend.has implementation
|
|
925
871
|
*/
|
|
926
|
-
has(key) {
|
|
927
|
-
return
|
|
928
|
-
return (yield this.redis.exists(key)) > 0; // 1: exists / 0: does not exist
|
|
929
|
-
});
|
|
872
|
+
async has(key) {
|
|
873
|
+
return (await this.redis.exists(key)) > 0; // 1: exists / 0: does not exist
|
|
930
874
|
}
|
|
931
875
|
/**
|
|
932
876
|
* CacheBackend.del implementation
|
|
933
877
|
*/
|
|
934
|
-
del(key) {
|
|
935
|
-
return
|
|
936
|
-
return (yield this.redis.del(key)) === 1; // number of keys removed
|
|
937
|
-
});
|
|
878
|
+
async del(key) {
|
|
879
|
+
return (await this.redis.del(key)) === 1; // number of keys removed
|
|
938
880
|
}
|
|
939
881
|
/**
|
|
940
882
|
* CacheBackend.expire implementation
|
|
941
883
|
*/
|
|
942
|
-
expire(key, ttl) {
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
return ret > 0; // 1: success / 0: key does not exist
|
|
946
|
-
});
|
|
884
|
+
async expire(key, ttl) {
|
|
885
|
+
const ret = ttl > 0 ? await this.redis.expire(key, ttl) : await this.redis.persist(key);
|
|
886
|
+
return ret > 0; // 1: success / 0: key does not exist
|
|
947
887
|
}
|
|
948
888
|
/**
|
|
949
889
|
* CacheBackend.ttl implementation
|
|
950
890
|
*/
|
|
951
|
-
ttl(key) {
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
return 0;
|
|
958
|
-
});
|
|
891
|
+
async ttl(key) {
|
|
892
|
+
const ms = await this.redis.pttl(key); // -2: key does not exist / -1: no timeout
|
|
893
|
+
if (ms >= 0)
|
|
894
|
+
return ms;
|
|
895
|
+
if (ms === -1)
|
|
896
|
+
return 0;
|
|
959
897
|
}
|
|
960
898
|
/**
|
|
961
899
|
* CacheBackend.close implementation
|
|
962
900
|
*/
|
|
963
|
-
close() {
|
|
964
|
-
|
|
965
|
-
yield this.redis.quit();
|
|
966
|
-
});
|
|
901
|
+
async close() {
|
|
902
|
+
await this.redis.quit();
|
|
967
903
|
}
|
|
968
904
|
}
|
|
969
905
|
//# sourceMappingURL=cache-service.js.map
|