cds-caching 1.0.0 → 1.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/README.md +182 -25
- package/lib/CachingService.d.ts +16 -10
- package/lib/CachingService.js +69 -13
- package/lib/operations/AsyncOperations.js +179 -16
- package/lib/operations/BasicOperations.js +166 -20
- package/lib/operations/CapOperations.js +251 -45
- package/lib/support/CacheStoreManager.js +44 -11
- package/lib/support/RuntimeConfigurationManager.js +4 -2
- package/lib/support/optionalRequire.js +45 -0
- package/lib/util.js +1 -1
- package/package.json +40 -12
|
@@ -7,6 +7,36 @@ class AsyncOperations {
|
|
|
7
7
|
this.keyManager = keyManager;
|
|
8
8
|
this.statistics = statistics;
|
|
9
9
|
this.runtimeConfigManager = runtimeConfigManager;
|
|
10
|
+
this.log = console; // Default logger
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Safely execute cache operations with error handling
|
|
15
|
+
* @param {Function} operation - The cache operation to execute
|
|
16
|
+
* @param {string} operationName - Name of the operation for logging
|
|
17
|
+
* @param {object} context - Context information for logging
|
|
18
|
+
* @returns {Promise<object>} - The result with error information
|
|
19
|
+
*/
|
|
20
|
+
async safeCacheOperation(operation, operationName, context = {}) {
|
|
21
|
+
try {
|
|
22
|
+
const result = await operation();
|
|
23
|
+
return { success: true, result, error: null };
|
|
24
|
+
} catch (error) {
|
|
25
|
+
this.log.warn(`Cache ${operationName} failed:`, {
|
|
26
|
+
error: error.message,
|
|
27
|
+
stack: error.stack,
|
|
28
|
+
context: context
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
result: null,
|
|
33
|
+
error: {
|
|
34
|
+
message: error.message,
|
|
35
|
+
operation: operationName,
|
|
36
|
+
context: context
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
10
40
|
}
|
|
11
41
|
|
|
12
42
|
/**
|
|
@@ -85,25 +115,91 @@ class AsyncOperations {
|
|
|
85
115
|
cacheOptions: JSON.stringify(options)
|
|
86
116
|
};
|
|
87
117
|
|
|
88
|
-
if
|
|
118
|
+
// Safely check if key exists in cache
|
|
119
|
+
const hasKeyResult = await this.safeCacheOperation(
|
|
120
|
+
() => this.cache.has(cacheKey),
|
|
121
|
+
'has',
|
|
122
|
+
{ key: cacheKey, functionName: asyncFunction.name || 'anonymous' }
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const hasKey = hasKeyResult.success && hasKeyResult.result;
|
|
126
|
+
const cacheErrors = [];
|
|
127
|
+
|
|
128
|
+
if (hasKey) {
|
|
89
129
|
const latency = this.getElapsedMs(startTime);
|
|
90
|
-
|
|
130
|
+
|
|
131
|
+
// Safely record hit statistics
|
|
132
|
+
const hitStatsResult = await this.safeCacheOperation(
|
|
133
|
+
() => this.statistics.recordHit(latency, cacheKey, metadata),
|
|
134
|
+
'recordHit',
|
|
135
|
+
{ key: cacheKey, latency }
|
|
136
|
+
);
|
|
137
|
+
if (!hitStatsResult.success) {
|
|
138
|
+
cacheErrors.push(hitStatsResult.error);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Safely get value from cache
|
|
142
|
+
const getResult = await this.safeCacheOperation(
|
|
143
|
+
() => this.cache.send("GET", { key: cacheKey }),
|
|
144
|
+
'get',
|
|
145
|
+
{ key: cacheKey }
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
if (getResult.success && getResult.result?.value !== undefined) {
|
|
149
|
+
return {
|
|
150
|
+
result: getResult.result.value,
|
|
151
|
+
cacheKey,
|
|
152
|
+
metadata: { hit: true, latency },
|
|
153
|
+
cacheErrors: cacheErrors
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
91
157
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
} else {
|
|
158
|
+
// Cache miss or cache error - delegate to underlying function
|
|
159
|
+
try {
|
|
95
160
|
const response = await asyncFunction(...args);
|
|
96
161
|
const latency = this.getElapsedMs(startTime);
|
|
97
|
-
|
|
162
|
+
|
|
163
|
+
// Safely record miss statistics
|
|
164
|
+
const missStatsResult = await this.safeCacheOperation(
|
|
165
|
+
() => this.statistics.recordMiss(latency, cacheKey, metadata),
|
|
166
|
+
'recordMiss',
|
|
167
|
+
{ key: cacheKey, latency }
|
|
168
|
+
);
|
|
169
|
+
if (!missStatsResult.success) {
|
|
170
|
+
cacheErrors.push(missStatsResult.error);
|
|
171
|
+
}
|
|
98
172
|
|
|
173
|
+
// Safely store in cache
|
|
99
174
|
const wrappedValue = {
|
|
100
175
|
value: response,
|
|
101
176
|
tags: options.tags || [],
|
|
102
177
|
timestamp: Date.now()
|
|
103
178
|
};
|
|
104
|
-
|
|
179
|
+
|
|
180
|
+
const setResult = await this.safeCacheOperation(
|
|
181
|
+
() => this.cache.send("SET", { key: cacheKey, value: wrappedValue, ttl: options.ttl || 0 }),
|
|
182
|
+
'set',
|
|
183
|
+
{ key: cacheKey, ttl: options.ttl }
|
|
184
|
+
);
|
|
185
|
+
if (!setResult.success) {
|
|
186
|
+
cacheErrors.push(setResult.error);
|
|
187
|
+
}
|
|
105
188
|
|
|
106
|
-
return {
|
|
189
|
+
return {
|
|
190
|
+
result: response,
|
|
191
|
+
cacheKey,
|
|
192
|
+
metadata: { hit: false, latency },
|
|
193
|
+
cacheErrors: cacheErrors
|
|
194
|
+
};
|
|
195
|
+
} catch (functionError) {
|
|
196
|
+
// If the underlying function fails, throw the error
|
|
197
|
+
this.log.error('Function execution failed:', {
|
|
198
|
+
error: functionError.message,
|
|
199
|
+
functionName: asyncFunction.name || 'anonymous',
|
|
200
|
+
key: cacheKey
|
|
201
|
+
});
|
|
202
|
+
throw functionError;
|
|
107
203
|
}
|
|
108
204
|
}
|
|
109
205
|
}
|
|
@@ -137,24 +233,91 @@ class AsyncOperations {
|
|
|
137
233
|
cacheOptions: JSON.stringify(options)
|
|
138
234
|
};
|
|
139
235
|
|
|
140
|
-
if
|
|
236
|
+
// Safely check if key exists in cache
|
|
237
|
+
const hasKeyResult = await this.safeCacheOperation(
|
|
238
|
+
() => this.cache.has(cacheKey),
|
|
239
|
+
'has',
|
|
240
|
+
{ key: cacheKey, functionName: asyncFunction.name || 'anonymous' }
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const hasKey = hasKeyResult.success && hasKeyResult.result;
|
|
244
|
+
const cacheErrors = [];
|
|
245
|
+
|
|
246
|
+
if (hasKey) {
|
|
141
247
|
const latency = this.getElapsedMs(startTime);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
248
|
+
|
|
249
|
+
// Safely record hit statistics
|
|
250
|
+
const hitStatsResult = await this.safeCacheOperation(
|
|
251
|
+
() => this.statistics.recordHit(latency, cacheKey, metadata),
|
|
252
|
+
'recordHit',
|
|
253
|
+
{ key: cacheKey, latency }
|
|
254
|
+
);
|
|
255
|
+
if (!hitStatsResult.success) {
|
|
256
|
+
cacheErrors.push(hitStatsResult.error);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Safely get value from cache
|
|
260
|
+
const getResult = await this.safeCacheOperation(
|
|
261
|
+
() => this.cache.send("GET", { key: cacheKey }),
|
|
262
|
+
'get',
|
|
263
|
+
{ key: cacheKey }
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
if (getResult.success && getResult.result?.value !== undefined) {
|
|
267
|
+
return {
|
|
268
|
+
result: getResult.result.value,
|
|
269
|
+
cacheKey,
|
|
270
|
+
metadata: { hit: true, latency },
|
|
271
|
+
cacheErrors: cacheErrors
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Cache miss or cache error - delegate to underlying function
|
|
277
|
+
try {
|
|
146
278
|
const response = await asyncFunction(...args);
|
|
147
279
|
const latency = this.getElapsedMs(startTime);
|
|
148
|
-
|
|
280
|
+
|
|
281
|
+
// Safely record miss statistics
|
|
282
|
+
const missStatsResult = await this.safeCacheOperation(
|
|
283
|
+
() => this.statistics.recordMiss(latency, cacheKey, metadata),
|
|
284
|
+
'recordMiss',
|
|
285
|
+
{ key: cacheKey, latency }
|
|
286
|
+
);
|
|
287
|
+
if (!missStatsResult.success) {
|
|
288
|
+
cacheErrors.push(missStatsResult.error);
|
|
289
|
+
}
|
|
149
290
|
|
|
291
|
+
// Safely store in cache
|
|
150
292
|
const wrappedValue = {
|
|
151
293
|
value: response,
|
|
152
294
|
tags: options.tags || [],
|
|
153
295
|
timestamp: Date.now()
|
|
154
296
|
};
|
|
155
|
-
|
|
297
|
+
|
|
298
|
+
const setResult = await this.safeCacheOperation(
|
|
299
|
+
() => this.cache.send("SET", { key: cacheKey, value: wrappedValue, ttl: options.ttl || 0 }),
|
|
300
|
+
'set',
|
|
301
|
+
{ key: cacheKey, ttl: options.ttl }
|
|
302
|
+
);
|
|
303
|
+
if (!setResult.success) {
|
|
304
|
+
cacheErrors.push(setResult.error);
|
|
305
|
+
}
|
|
156
306
|
|
|
157
|
-
return {
|
|
307
|
+
return {
|
|
308
|
+
result: response,
|
|
309
|
+
cacheKey,
|
|
310
|
+
metadata: { hit: false, latency },
|
|
311
|
+
cacheErrors: cacheErrors
|
|
312
|
+
};
|
|
313
|
+
} catch (functionError) {
|
|
314
|
+
// If the underlying function fails, throw the error
|
|
315
|
+
this.log.error('Function execution failed:', {
|
|
316
|
+
error: functionError.message,
|
|
317
|
+
functionName: asyncFunction.name || 'anonymous',
|
|
318
|
+
key: cacheKey
|
|
319
|
+
});
|
|
320
|
+
throw functionError;
|
|
158
321
|
}
|
|
159
322
|
}
|
|
160
323
|
|
|
@@ -9,6 +9,16 @@ class BasicOperations {
|
|
|
9
9
|
this.statistics = statistics;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
_keyv() {
|
|
13
|
+
// The underlying Keyv instance is stored on the CachingService as `this.cache`
|
|
14
|
+
// (see `CachingService.init()` where `this.cache = new Keyv(...)`).
|
|
15
|
+
const keyv = this.cache?.cache;
|
|
16
|
+
if (!keyv) {
|
|
17
|
+
throw new Error('cds-caching: Keyv store not initialized on caching service');
|
|
18
|
+
}
|
|
19
|
+
return keyv;
|
|
20
|
+
}
|
|
21
|
+
|
|
12
22
|
/**
|
|
13
23
|
* Set a value in the cache
|
|
14
24
|
* @param {string|object} key - the key to set
|
|
@@ -16,14 +26,17 @@ class BasicOperations {
|
|
|
16
26
|
* @param {object} options - cache options
|
|
17
27
|
* @returns {Promise<void>}
|
|
18
28
|
*/
|
|
19
|
-
async set(key, value, options = {}) {
|
|
29
|
+
async set(key, value, options = {}, tx = null) {
|
|
20
30
|
const wrappedValue = {
|
|
21
31
|
value,
|
|
22
32
|
tags: this.tagResolver.resolveTags(options.tags, value, options.params) || [],
|
|
23
33
|
timestamp: Date.now()
|
|
24
34
|
};
|
|
25
35
|
const createdKey = this.keyManager.createKey(key, {}, options.key);
|
|
26
|
-
|
|
36
|
+
// If no key was created, the input should not be cached (e.g. non-SELECT CQN)
|
|
37
|
+
if (!createdKey) return;
|
|
38
|
+
const srv = tx || this.cache;
|
|
39
|
+
await srv.send('SET', {
|
|
27
40
|
key: createdKey,
|
|
28
41
|
value: wrappedValue,
|
|
29
42
|
ttl: options.ttl || 0
|
|
@@ -44,9 +57,16 @@ class BasicOperations {
|
|
|
44
57
|
* @param {string|object} key - the key to get
|
|
45
58
|
* @returns {Promise<any>} - the cached value
|
|
46
59
|
*/
|
|
47
|
-
async get(key) {
|
|
60
|
+
async get(key, tx = null) {
|
|
48
61
|
const createdKey = this.keyManager.createKey(key);
|
|
49
|
-
|
|
62
|
+
|
|
63
|
+
// If not key was created, return undefined
|
|
64
|
+
if(!createdKey) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const srv = tx || this.cache;
|
|
69
|
+
const wrappedValue = await srv.send('GET', {
|
|
50
70
|
key: createdKey
|
|
51
71
|
});
|
|
52
72
|
|
|
@@ -68,9 +88,21 @@ class BasicOperations {
|
|
|
68
88
|
* @param {string|object} key - the key to check
|
|
69
89
|
* @returns {Promise<boolean>} - whether the key exists
|
|
70
90
|
*/
|
|
71
|
-
async has(key) {
|
|
91
|
+
async has(key, tx = null) {
|
|
72
92
|
const createdKey = this.keyManager.createKey(key);
|
|
73
|
-
|
|
93
|
+
if (!createdKey) return false;
|
|
94
|
+
try {
|
|
95
|
+
// Intentionally bypass CAP service/transaction handling here.
|
|
96
|
+
// This avoids coupling to the caller's request tx (which might already be rolled back)
|
|
97
|
+
// and makes `has()` safe to call even in concurrent BEFORE handlers.
|
|
98
|
+
return await this._keyv().has(createdKey);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (this.cache.options.throwOnErrors) {
|
|
101
|
+
throw error;
|
|
102
|
+
} else {
|
|
103
|
+
return false; // We don't want to throw errors here
|
|
104
|
+
}
|
|
105
|
+
}
|
|
74
106
|
}
|
|
75
107
|
|
|
76
108
|
/**
|
|
@@ -78,9 +110,11 @@ class BasicOperations {
|
|
|
78
110
|
* @param {string|object} key - the key to delete
|
|
79
111
|
* @returns {Promise<boolean>} - whether the key was deleted
|
|
80
112
|
*/
|
|
81
|
-
async delete(key) {
|
|
113
|
+
async delete(key, tx = null) {
|
|
82
114
|
const createdKey = this.keyManager.createKey(key);
|
|
83
|
-
|
|
115
|
+
if (!createdKey) return false;
|
|
116
|
+
const srv = tx || this.cache;
|
|
117
|
+
const result = await srv.send('DELETE', {
|
|
84
118
|
key: createdKey
|
|
85
119
|
});
|
|
86
120
|
|
|
@@ -100,8 +134,11 @@ class BasicOperations {
|
|
|
100
134
|
* Clear all cache entries
|
|
101
135
|
* @returns {Promise<void>}
|
|
102
136
|
*/
|
|
103
|
-
async clear() {
|
|
104
|
-
|
|
137
|
+
async clear(tx = null) {
|
|
138
|
+
const srv = tx || this.cache;
|
|
139
|
+
await srv.send('CLEAR', {
|
|
140
|
+
deleteAll: true
|
|
141
|
+
});
|
|
105
142
|
|
|
106
143
|
// Record the native clear operation
|
|
107
144
|
const metadata = {
|
|
@@ -119,10 +156,11 @@ class BasicOperations {
|
|
|
119
156
|
* @param {string} tag - the tag to match
|
|
120
157
|
* @returns {Promise<void>}
|
|
121
158
|
*/
|
|
122
|
-
async deleteByTag(tag) {
|
|
159
|
+
async deleteByTag(tag, tx = null) {
|
|
160
|
+
// Iterate directly on the underlying store to avoid recursion via CachingService.iterator()
|
|
123
161
|
for await (const [key, wrappedValue] of this.iterator()) {
|
|
124
162
|
if (wrappedValue?.tags?.includes(tag)) {
|
|
125
|
-
await this.delete(key);
|
|
163
|
+
await this.delete(key, tx);
|
|
126
164
|
}
|
|
127
165
|
}
|
|
128
166
|
|
|
@@ -142,9 +180,11 @@ class BasicOperations {
|
|
|
142
180
|
* @param {string|object} key - the key to get metadata for
|
|
143
181
|
* @returns {Promise<object|null>} - the metadata or null if not found
|
|
144
182
|
*/
|
|
145
|
-
async metadata(key) {
|
|
183
|
+
async metadata(key, tx = null) {
|
|
146
184
|
const createdKey = this.keyManager.createKey(key);
|
|
147
|
-
|
|
185
|
+
if (!createdKey) return null;
|
|
186
|
+
const srv = tx || this.cache;
|
|
187
|
+
const wrappedValue = await srv.send('GET', {
|
|
148
188
|
key: createdKey
|
|
149
189
|
});
|
|
150
190
|
if (!wrappedValue) return null;
|
|
@@ -158,9 +198,11 @@ class BasicOperations {
|
|
|
158
198
|
* @param {string|object} key - the key to get tags for
|
|
159
199
|
* @returns {Promise<string[]>} - the tags
|
|
160
200
|
*/
|
|
161
|
-
async tags(key) {
|
|
201
|
+
async tags(key, tx = null) {
|
|
162
202
|
const createdKey = this.keyManager.createKey(key);
|
|
163
|
-
|
|
203
|
+
if (!createdKey) return [];
|
|
204
|
+
const srv = tx || this.cache;
|
|
205
|
+
const wrappedValue = await srv.send('GET', {
|
|
164
206
|
key: createdKey
|
|
165
207
|
});
|
|
166
208
|
return wrappedValue?.tags || [];
|
|
@@ -171,9 +213,11 @@ class BasicOperations {
|
|
|
171
213
|
* @param {string|object} key - the key to get
|
|
172
214
|
* @returns {Promise<any>} - the raw cached value
|
|
173
215
|
*/
|
|
174
|
-
async getRaw(key) {
|
|
216
|
+
async getRaw(key, tx = null) {
|
|
175
217
|
const createdKey = this.keyManager.createKey(key);
|
|
176
|
-
|
|
218
|
+
if (!createdKey) return undefined;
|
|
219
|
+
const srv = tx || this.cache;
|
|
220
|
+
const wrappedValue = await srv.send('GET', {
|
|
177
221
|
key: createdKey
|
|
178
222
|
});
|
|
179
223
|
return wrappedValue?.value;
|
|
@@ -183,8 +227,9 @@ class BasicOperations {
|
|
|
183
227
|
* Iterator for all cache entries
|
|
184
228
|
* @returns {AsyncIterator} - iterator for cache entries
|
|
185
229
|
*/
|
|
186
|
-
async *iterator() {
|
|
187
|
-
|
|
230
|
+
async *iterator(tx = null) {
|
|
231
|
+
// Iterate directly on Keyv to avoid recursion via CachingService.iterator()
|
|
232
|
+
for await (const [key, value] of this._keyv().iterator()) {
|
|
188
233
|
if (typeof value === "string") {
|
|
189
234
|
try {
|
|
190
235
|
yield [key, JSON.parse(value)];
|
|
@@ -196,6 +241,107 @@ class BasicOperations {
|
|
|
196
241
|
}
|
|
197
242
|
}
|
|
198
243
|
}
|
|
244
|
+
|
|
245
|
+
async setInTx(key, value, options = {}) {
|
|
246
|
+
const tx = await this.cache.tx();
|
|
247
|
+
try {
|
|
248
|
+
await this.set(key, value, options, tx);
|
|
249
|
+
await tx.commit();
|
|
250
|
+
return;
|
|
251
|
+
} catch (error) {
|
|
252
|
+
await tx.rollback();
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async getInTx(key) {
|
|
258
|
+
const tx = await this.cache.tx();
|
|
259
|
+
try {
|
|
260
|
+
const value = await this.get(key, tx);
|
|
261
|
+
await tx.commit();
|
|
262
|
+
return value;
|
|
263
|
+
} catch (error) {
|
|
264
|
+
await tx.rollback();
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async hasInTx(key) {
|
|
270
|
+
// `has()` bypasses CAP tx handling on purpose, so opening a new transaction is unnecessary.
|
|
271
|
+
return this.has(key);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async deleteInTx(key) {
|
|
275
|
+
const tx = await this.cache.tx();
|
|
276
|
+
try {
|
|
277
|
+
const value = await this.delete(key, tx);
|
|
278
|
+
await tx.commit();
|
|
279
|
+
return value;
|
|
280
|
+
} catch (error) {
|
|
281
|
+
await tx.rollback();
|
|
282
|
+
throw error;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async clearInTx() {
|
|
287
|
+
const tx = await this.cache.tx();
|
|
288
|
+
try {
|
|
289
|
+
await this.clear(tx);
|
|
290
|
+
await tx.commit();
|
|
291
|
+
return;
|
|
292
|
+
} catch (error) {
|
|
293
|
+
await tx.rollback();
|
|
294
|
+
throw error;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async deleteByTagInTx(tag) {
|
|
299
|
+
const tx = await this.cache.tx();
|
|
300
|
+
try {
|
|
301
|
+
await this.deleteByTag(tag, tx);
|
|
302
|
+
await tx.commit();
|
|
303
|
+
return;
|
|
304
|
+
} catch (error) {
|
|
305
|
+
await tx.rollback();
|
|
306
|
+
throw error;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async metadataInTx(key) {
|
|
311
|
+
const tx = await this.cache.tx();
|
|
312
|
+
try {
|
|
313
|
+
const value = await this.metadata(key, tx);
|
|
314
|
+
await tx.commit();
|
|
315
|
+
return value;
|
|
316
|
+
} catch (error) {
|
|
317
|
+
await tx.rollback();
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async tagsInTx(key) {
|
|
323
|
+
const tx = await this.cache.tx();
|
|
324
|
+
try {
|
|
325
|
+
const value = await this.tags(key, tx);
|
|
326
|
+
await tx.commit();
|
|
327
|
+
return value;
|
|
328
|
+
} catch (error) {
|
|
329
|
+
await tx.rollback();
|
|
330
|
+
throw error;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async getRawInTx(key) {
|
|
335
|
+
const tx = await this.cache.tx();
|
|
336
|
+
try {
|
|
337
|
+
const value = await this.getRaw(key, tx);
|
|
338
|
+
await tx.commit();
|
|
339
|
+
return value;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
await tx.rollback();
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
199
345
|
}
|
|
200
346
|
|
|
201
347
|
module.exports = BasicOperations;
|