monsqlize 3.3.0 → 3.3.2
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/CHANGELOG.md +5 -3
- package/MIGRATION.md +66 -5
- package/README.md +37 -9
- package/changelogs/v3.3.1.md +28 -0
- package/changelogs/v3.3.2.md +37 -0
- package/dist/cjs/cli/data-task.cjs +7 -5
- package/dist/cjs/index.cjs +5032 -3325
- package/dist/cjs/transaction/Transaction.cjs +140 -14
- package/dist/cjs/transaction/TransactionManager.cjs +145 -25
- package/dist/esm/index.mjs +4836 -3130
- package/dist/types/base.d.mts +1 -1
- package/dist/types/base.d.ts +1 -1
- package/dist/types/collection.d.mts +10 -10
- package/dist/types/collection.d.ts +10 -10
- package/dist/types/data-tasks.d.mts +1 -0
- package/dist/types/data-tasks.d.ts +1 -0
- package/dist/types/index.d.mts +3 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/model.d.mts +16 -12
- package/dist/types/model.d.ts +16 -12
- package/dist/types/monsqlize.d.mts +8 -2
- package/dist/types/monsqlize.d.ts +8 -2
- package/dist/types/pool.d.mts +3 -0
- package/dist/types/pool.d.ts +3 -0
- package/dist/types/runtime.d.mts +11 -23
- package/dist/types/runtime.d.ts +11 -23
- package/dist/types/slow-query-log.d.mts +9 -1
- package/dist/types/slow-query-log.d.ts +9 -1
- package/dist/types/sync.d.mts +4 -2
- package/dist/types/sync.d.ts +4 -2
- package/dist/types/transaction.d.mts +6 -4
- package/dist/types/transaction.d.ts +6 -4
- package/package.json +7 -5
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
// src/core/cache-coordination.ts
|
|
4
|
+
var coordinators = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
var origins = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
function coordinator(cache) {
|
|
7
|
+
const origin = origins.get(cache) ?? cache;
|
|
8
|
+
let state = coordinators.get(origin);
|
|
9
|
+
if (!state) {
|
|
10
|
+
state = { tickets: /* @__PURE__ */ new Set(), tails: /* @__PURE__ */ new Map() };
|
|
11
|
+
coordinators.set(origin, state);
|
|
12
|
+
}
|
|
13
|
+
return state;
|
|
14
|
+
}
|
|
15
|
+
function matches(intent, key) {
|
|
16
|
+
if (intent.type === "key") return intent.value === key;
|
|
17
|
+
const pattern = intent.value.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*");
|
|
18
|
+
return new RegExp(`^${pattern}$`).test(key);
|
|
19
|
+
}
|
|
20
|
+
function serialize(state, key, task) {
|
|
21
|
+
const result = (state.tails.get(key) ?? Promise.resolve()).catch(() => void 0).then(task);
|
|
22
|
+
state.tails.set(key, result);
|
|
23
|
+
void result.finally(() => {
|
|
24
|
+
if (state.tails.get(key) === result) state.tails.delete(key);
|
|
25
|
+
}).catch(() => void 0);
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
async function coordinateInvalidation(cache, intents, task) {
|
|
29
|
+
const state = coordinator(cache);
|
|
30
|
+
for (const ticket of state.tickets) {
|
|
31
|
+
if (intents.some((intent) => matches(intent, ticket.key))) ticket.valid = false;
|
|
32
|
+
}
|
|
33
|
+
const keys = [...state.tails.keys()].filter((key) => intents.some((intent) => matches(intent, key)));
|
|
34
|
+
const previous = keys.map((key) => state.tails.get(key));
|
|
35
|
+
let finish;
|
|
36
|
+
const done = new Promise((resolve) => {
|
|
37
|
+
finish = resolve;
|
|
38
|
+
});
|
|
39
|
+
const pending = keys.map((key) => serialize(state, key, () => done));
|
|
40
|
+
try {
|
|
41
|
+
await Promise.allSettled(previous);
|
|
42
|
+
return await task();
|
|
43
|
+
} finally {
|
|
44
|
+
finish();
|
|
45
|
+
await Promise.allSettled(pending);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
3
49
|
// src/capabilities/transaction/index.ts
|
|
4
50
|
var import_node_crypto = require("node:crypto");
|
|
5
51
|
|
|
@@ -148,8 +194,13 @@ var Transaction = class {
|
|
|
148
194
|
constructor(session, options = {}) {
|
|
149
195
|
this.session = session;
|
|
150
196
|
this.options = options;
|
|
197
|
+
this.commitPromise = null;
|
|
198
|
+
this.abortPromise = null;
|
|
199
|
+
this.endPromise = null;
|
|
200
|
+
this.commitOutcomeUnknown = false;
|
|
151
201
|
this.id = `tx_${(0, import_node_crypto.randomBytes)(8).toString("hex")}`;
|
|
152
202
|
this.state = "pending";
|
|
203
|
+
this.acceptingWrites = false;
|
|
153
204
|
this.startedAt = null;
|
|
154
205
|
this.timeoutTimer = null;
|
|
155
206
|
this.pendingInvalidations = /* @__PURE__ */ new Set();
|
|
@@ -163,11 +214,13 @@ var Transaction = class {
|
|
|
163
214
|
* @since v1.4.0
|
|
164
215
|
*/
|
|
165
216
|
async start() {
|
|
217
|
+
if (this.endPromise || this.abortPromise) throw createError(ErrorCodes.INVALID_OPERATION, "Transaction is already ending.");
|
|
166
218
|
if (this.state !== "pending") {
|
|
167
219
|
throw createError(ErrorCodes.INVALID_OPERATION, `Cannot start transaction in state: ${this.state}`);
|
|
168
220
|
}
|
|
169
221
|
this.session.startTransaction(this.options.transactionOptions);
|
|
170
222
|
this.state = "active";
|
|
223
|
+
this.acceptingWrites = true;
|
|
171
224
|
this.startedAt = Date.now();
|
|
172
225
|
const timeout = this.options.timeout ?? 3e4;
|
|
173
226
|
if (timeout > 0) {
|
|
@@ -187,11 +240,42 @@ var Transaction = class {
|
|
|
187
240
|
* @since v1.4.0
|
|
188
241
|
*/
|
|
189
242
|
async commit() {
|
|
243
|
+
if (this.commitPromise) return this.commitPromise;
|
|
244
|
+
if (this.endPromise || this.abortPromise) {
|
|
245
|
+
throw createError(ErrorCodes.INVALID_OPERATION, "Transaction is already ending.");
|
|
246
|
+
}
|
|
190
247
|
if (this.state !== "active") {
|
|
191
248
|
throw createError(ErrorCodes.INVALID_OPERATION, `Cannot commit transaction in state: ${this.state}`);
|
|
192
249
|
}
|
|
250
|
+
this.acceptingWrites = false;
|
|
251
|
+
const timeout = this.options.timeout ?? 3e4;
|
|
252
|
+
if (!this.commitOutcomeUnknown && timeout > 0 && this.getDuration() >= timeout) {
|
|
253
|
+
await this.abort();
|
|
254
|
+
throw createError(ErrorCodes.OPERATION_TIMEOUT, "Transaction work exceeded its timeout before commit.");
|
|
255
|
+
}
|
|
256
|
+
this.clearTimeout();
|
|
257
|
+
const committing = this.commitOnce();
|
|
258
|
+
this.commitPromise = committing;
|
|
259
|
+
try {
|
|
260
|
+
await committing;
|
|
261
|
+
} finally {
|
|
262
|
+
if (this.commitPromise === committing) this.commitPromise = null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async commitOnce() {
|
|
193
266
|
if (typeof this.session.commitTransaction === "function") {
|
|
194
|
-
|
|
267
|
+
try {
|
|
268
|
+
await commitTransactionWithRetry(this.session, this.options.logger);
|
|
269
|
+
this.commitOutcomeUnknown = false;
|
|
270
|
+
} catch (error) {
|
|
271
|
+
this.commitOutcomeUnknown = isUnknownTransactionCommitResult(error);
|
|
272
|
+
if (this.commitOutcomeUnknown) {
|
|
273
|
+
await this.flushPendingInvalidations().catch((cacheError) => {
|
|
274
|
+
this.options.logger?.warn?.("[Transaction] uncertain commit cache invalidation failed.", cacheError);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
195
279
|
}
|
|
196
280
|
this.state = "committed";
|
|
197
281
|
try {
|
|
@@ -215,6 +299,14 @@ var Transaction = class {
|
|
|
215
299
|
* @since v1.4.0
|
|
216
300
|
*/
|
|
217
301
|
async abort() {
|
|
302
|
+
this.acceptingWrites = false;
|
|
303
|
+
if (this.commitPromise) await this.commitPromise.catch(() => void 0);
|
|
304
|
+
if (this.commitOutcomeUnknown) return;
|
|
305
|
+
if (this.abortPromise) return this.abortPromise;
|
|
306
|
+
this.abortPromise = this.abortOnce();
|
|
307
|
+
return this.abortPromise;
|
|
308
|
+
}
|
|
309
|
+
async abortOnce() {
|
|
218
310
|
if (this.state !== "pending" && this.state !== "active") {
|
|
219
311
|
return;
|
|
220
312
|
}
|
|
@@ -239,10 +331,24 @@ var Transaction = class {
|
|
|
239
331
|
* @since v1.4.0
|
|
240
332
|
*/
|
|
241
333
|
async end() {
|
|
334
|
+
if (this.endPromise) return this.endPromise;
|
|
335
|
+
this.endPromise = this.endOnce();
|
|
336
|
+
return this.endPromise;
|
|
337
|
+
}
|
|
338
|
+
async endOnce() {
|
|
339
|
+
this.acceptingWrites = false;
|
|
340
|
+
if (this.commitPromise) await this.commitPromise.catch(() => void 0);
|
|
341
|
+
if (this.abortPromise) await this.abortPromise;
|
|
342
|
+
else if (this.state === "active" && !this.commitOutcomeUnknown) await this.abort();
|
|
242
343
|
this.clearTimeout();
|
|
243
344
|
this.options.lockManager?.releaseLocks(this.id);
|
|
244
345
|
await this.session.endSession();
|
|
245
346
|
}
|
|
347
|
+
assertAcceptingWrites() {
|
|
348
|
+
if (this.state !== "active" || !this.acceptingWrites) {
|
|
349
|
+
throw createError(ErrorCodes.INVALID_OPERATION, `Managed transaction ${this.id} is no longer accepting writes.`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
246
352
|
/**
|
|
247
353
|
* Record a cache invalidation intent.
|
|
248
354
|
* @since v1.4.0
|
|
@@ -270,15 +376,17 @@ var Transaction = class {
|
|
|
270
376
|
return;
|
|
271
377
|
}
|
|
272
378
|
const cacheWithDelete = this.options.cache;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
379
|
+
await coordinateInvalidation(this.options.cache, [...this.pendingInvalidationIntents.values()], async () => {
|
|
380
|
+
for (const intent of this.pendingInvalidationIntents.values()) {
|
|
381
|
+
if (intent.type === "pattern") {
|
|
382
|
+
await cacheWithDelete.delPattern?.(intent.value);
|
|
383
|
+
} else if (typeof cacheWithDelete.del === "function") {
|
|
384
|
+
await cacheWithDelete.del(intent.value);
|
|
385
|
+
} else if (typeof cacheWithDelete.delete === "function") {
|
|
386
|
+
await cacheWithDelete.delete(intent.value);
|
|
387
|
+
}
|
|
280
388
|
}
|
|
281
|
-
}
|
|
389
|
+
});
|
|
282
390
|
}
|
|
283
391
|
async clearPendingInvalidationBarriers() {
|
|
284
392
|
if (!this.options.cache || this.pendingInvalidationIntents.size === 0) {
|
|
@@ -353,12 +461,30 @@ function stringifySessionId(id) {
|
|
|
353
461
|
}
|
|
354
462
|
return String(id);
|
|
355
463
|
}
|
|
356
|
-
function
|
|
357
|
-
|
|
358
|
-
|
|
464
|
+
function transactionErrorChain(error) {
|
|
465
|
+
const chain = [];
|
|
466
|
+
const seen = /* @__PURE__ */ new Set();
|
|
467
|
+
let current = error;
|
|
468
|
+
while (current && typeof current === "object" && chain.length < 8 && !seen.has(current)) {
|
|
469
|
+
seen.add(current);
|
|
470
|
+
const candidate = current;
|
|
471
|
+
chain.push(candidate);
|
|
472
|
+
current = candidate.cause;
|
|
473
|
+
}
|
|
474
|
+
return chain;
|
|
475
|
+
}
|
|
476
|
+
function hasTransactionErrorLabel(candidate, label) {
|
|
477
|
+
if (Array.isArray(candidate.errorLabels) && candidate.errorLabels.includes(label)) return true;
|
|
478
|
+
if (typeof candidate.hasErrorLabel === "function") {
|
|
479
|
+
try {
|
|
480
|
+
return Boolean(candidate.hasErrorLabel.call(candidate, label));
|
|
481
|
+
} catch {
|
|
482
|
+
}
|
|
359
483
|
}
|
|
360
|
-
|
|
361
|
-
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
function isUnknownTransactionCommitResult(error) {
|
|
487
|
+
return transactionErrorChain(error).some((candidate) => hasTransactionErrorLabel(candidate, "UnknownTransactionCommitResult"));
|
|
362
488
|
}
|
|
363
489
|
async function commitTransactionWithRetry(session, logger) {
|
|
364
490
|
const maxAttempts = 3;
|
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
// src/core/cache-coordination.ts
|
|
4
|
+
var coordinators = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
var origins = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
function coordinator(cache) {
|
|
7
|
+
const origin = origins.get(cache) ?? cache;
|
|
8
|
+
let state = coordinators.get(origin);
|
|
9
|
+
if (!state) {
|
|
10
|
+
state = { tickets: /* @__PURE__ */ new Set(), tails: /* @__PURE__ */ new Map() };
|
|
11
|
+
coordinators.set(origin, state);
|
|
12
|
+
}
|
|
13
|
+
return state;
|
|
14
|
+
}
|
|
15
|
+
function matches(intent, key) {
|
|
16
|
+
if (intent.type === "key") return intent.value === key;
|
|
17
|
+
const pattern = intent.value.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*");
|
|
18
|
+
return new RegExp(`^${pattern}$`).test(key);
|
|
19
|
+
}
|
|
20
|
+
function serialize(state, key, task) {
|
|
21
|
+
const result = (state.tails.get(key) ?? Promise.resolve()).catch(() => void 0).then(task);
|
|
22
|
+
state.tails.set(key, result);
|
|
23
|
+
void result.finally(() => {
|
|
24
|
+
if (state.tails.get(key) === result) state.tails.delete(key);
|
|
25
|
+
}).catch(() => void 0);
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
async function coordinateInvalidation(cache, intents, task) {
|
|
29
|
+
const state = coordinator(cache);
|
|
30
|
+
for (const ticket of state.tickets) {
|
|
31
|
+
if (intents.some((intent) => matches(intent, ticket.key))) ticket.valid = false;
|
|
32
|
+
}
|
|
33
|
+
const keys = [...state.tails.keys()].filter((key) => intents.some((intent) => matches(intent, key)));
|
|
34
|
+
const previous = keys.map((key) => state.tails.get(key));
|
|
35
|
+
let finish;
|
|
36
|
+
const done = new Promise((resolve) => {
|
|
37
|
+
finish = resolve;
|
|
38
|
+
});
|
|
39
|
+
const pending = keys.map((key) => serialize(state, key, () => done));
|
|
40
|
+
try {
|
|
41
|
+
await Promise.allSettled(previous);
|
|
42
|
+
return await task();
|
|
43
|
+
} finally {
|
|
44
|
+
finish();
|
|
45
|
+
await Promise.allSettled(pending);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
3
49
|
// src/capabilities/transaction/index.ts
|
|
4
50
|
var import_node_crypto = require("node:crypto");
|
|
5
51
|
|
|
@@ -148,8 +194,13 @@ var Transaction = class {
|
|
|
148
194
|
constructor(session, options = {}) {
|
|
149
195
|
this.session = session;
|
|
150
196
|
this.options = options;
|
|
197
|
+
this.commitPromise = null;
|
|
198
|
+
this.abortPromise = null;
|
|
199
|
+
this.endPromise = null;
|
|
200
|
+
this.commitOutcomeUnknown = false;
|
|
151
201
|
this.id = `tx_${(0, import_node_crypto.randomBytes)(8).toString("hex")}`;
|
|
152
202
|
this.state = "pending";
|
|
203
|
+
this.acceptingWrites = false;
|
|
153
204
|
this.startedAt = null;
|
|
154
205
|
this.timeoutTimer = null;
|
|
155
206
|
this.pendingInvalidations = /* @__PURE__ */ new Set();
|
|
@@ -163,11 +214,13 @@ var Transaction = class {
|
|
|
163
214
|
* @since v1.4.0
|
|
164
215
|
*/
|
|
165
216
|
async start() {
|
|
217
|
+
if (this.endPromise || this.abortPromise) throw createError(ErrorCodes.INVALID_OPERATION, "Transaction is already ending.");
|
|
166
218
|
if (this.state !== "pending") {
|
|
167
219
|
throw createError(ErrorCodes.INVALID_OPERATION, `Cannot start transaction in state: ${this.state}`);
|
|
168
220
|
}
|
|
169
221
|
this.session.startTransaction(this.options.transactionOptions);
|
|
170
222
|
this.state = "active";
|
|
223
|
+
this.acceptingWrites = true;
|
|
171
224
|
this.startedAt = Date.now();
|
|
172
225
|
const timeout = this.options.timeout ?? 3e4;
|
|
173
226
|
if (timeout > 0) {
|
|
@@ -187,11 +240,42 @@ var Transaction = class {
|
|
|
187
240
|
* @since v1.4.0
|
|
188
241
|
*/
|
|
189
242
|
async commit() {
|
|
243
|
+
if (this.commitPromise) return this.commitPromise;
|
|
244
|
+
if (this.endPromise || this.abortPromise) {
|
|
245
|
+
throw createError(ErrorCodes.INVALID_OPERATION, "Transaction is already ending.");
|
|
246
|
+
}
|
|
190
247
|
if (this.state !== "active") {
|
|
191
248
|
throw createError(ErrorCodes.INVALID_OPERATION, `Cannot commit transaction in state: ${this.state}`);
|
|
192
249
|
}
|
|
250
|
+
this.acceptingWrites = false;
|
|
251
|
+
const timeout = this.options.timeout ?? 3e4;
|
|
252
|
+
if (!this.commitOutcomeUnknown && timeout > 0 && this.getDuration() >= timeout) {
|
|
253
|
+
await this.abort();
|
|
254
|
+
throw createError(ErrorCodes.OPERATION_TIMEOUT, "Transaction work exceeded its timeout before commit.");
|
|
255
|
+
}
|
|
256
|
+
this.clearTimeout();
|
|
257
|
+
const committing = this.commitOnce();
|
|
258
|
+
this.commitPromise = committing;
|
|
259
|
+
try {
|
|
260
|
+
await committing;
|
|
261
|
+
} finally {
|
|
262
|
+
if (this.commitPromise === committing) this.commitPromise = null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async commitOnce() {
|
|
193
266
|
if (typeof this.session.commitTransaction === "function") {
|
|
194
|
-
|
|
267
|
+
try {
|
|
268
|
+
await commitTransactionWithRetry(this.session, this.options.logger);
|
|
269
|
+
this.commitOutcomeUnknown = false;
|
|
270
|
+
} catch (error) {
|
|
271
|
+
this.commitOutcomeUnknown = isUnknownTransactionCommitResult(error);
|
|
272
|
+
if (this.commitOutcomeUnknown) {
|
|
273
|
+
await this.flushPendingInvalidations().catch((cacheError) => {
|
|
274
|
+
this.options.logger?.warn?.("[Transaction] uncertain commit cache invalidation failed.", cacheError);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
195
279
|
}
|
|
196
280
|
this.state = "committed";
|
|
197
281
|
try {
|
|
@@ -215,6 +299,14 @@ var Transaction = class {
|
|
|
215
299
|
* @since v1.4.0
|
|
216
300
|
*/
|
|
217
301
|
async abort() {
|
|
302
|
+
this.acceptingWrites = false;
|
|
303
|
+
if (this.commitPromise) await this.commitPromise.catch(() => void 0);
|
|
304
|
+
if (this.commitOutcomeUnknown) return;
|
|
305
|
+
if (this.abortPromise) return this.abortPromise;
|
|
306
|
+
this.abortPromise = this.abortOnce();
|
|
307
|
+
return this.abortPromise;
|
|
308
|
+
}
|
|
309
|
+
async abortOnce() {
|
|
218
310
|
if (this.state !== "pending" && this.state !== "active") {
|
|
219
311
|
return;
|
|
220
312
|
}
|
|
@@ -239,10 +331,24 @@ var Transaction = class {
|
|
|
239
331
|
* @since v1.4.0
|
|
240
332
|
*/
|
|
241
333
|
async end() {
|
|
334
|
+
if (this.endPromise) return this.endPromise;
|
|
335
|
+
this.endPromise = this.endOnce();
|
|
336
|
+
return this.endPromise;
|
|
337
|
+
}
|
|
338
|
+
async endOnce() {
|
|
339
|
+
this.acceptingWrites = false;
|
|
340
|
+
if (this.commitPromise) await this.commitPromise.catch(() => void 0);
|
|
341
|
+
if (this.abortPromise) await this.abortPromise;
|
|
342
|
+
else if (this.state === "active" && !this.commitOutcomeUnknown) await this.abort();
|
|
242
343
|
this.clearTimeout();
|
|
243
344
|
this.options.lockManager?.releaseLocks(this.id);
|
|
244
345
|
await this.session.endSession();
|
|
245
346
|
}
|
|
347
|
+
assertAcceptingWrites() {
|
|
348
|
+
if (this.state !== "active" || !this.acceptingWrites) {
|
|
349
|
+
throw createError(ErrorCodes.INVALID_OPERATION, `Managed transaction ${this.id} is no longer accepting writes.`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
246
352
|
/**
|
|
247
353
|
* Record a cache invalidation intent.
|
|
248
354
|
* @since v1.4.0
|
|
@@ -270,15 +376,17 @@ var Transaction = class {
|
|
|
270
376
|
return;
|
|
271
377
|
}
|
|
272
378
|
const cacheWithDelete = this.options.cache;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
379
|
+
await coordinateInvalidation(this.options.cache, [...this.pendingInvalidationIntents.values()], async () => {
|
|
380
|
+
for (const intent of this.pendingInvalidationIntents.values()) {
|
|
381
|
+
if (intent.type === "pattern") {
|
|
382
|
+
await cacheWithDelete.delPattern?.(intent.value);
|
|
383
|
+
} else if (typeof cacheWithDelete.del === "function") {
|
|
384
|
+
await cacheWithDelete.del(intent.value);
|
|
385
|
+
} else if (typeof cacheWithDelete.delete === "function") {
|
|
386
|
+
await cacheWithDelete.delete(intent.value);
|
|
387
|
+
}
|
|
280
388
|
}
|
|
281
|
-
}
|
|
389
|
+
});
|
|
282
390
|
}
|
|
283
391
|
async clearPendingInvalidationBarriers() {
|
|
284
392
|
if (!this.options.cache || this.pendingInvalidationIntents.size === 0) {
|
|
@@ -416,12 +524,12 @@ var TransactionManager = class {
|
|
|
416
524
|
return result;
|
|
417
525
|
} catch (error) {
|
|
418
526
|
lastError = error;
|
|
419
|
-
await transaction.abort();
|
|
420
|
-
this.recordStats(transaction, Date.now() - startedAt, false);
|
|
421
527
|
if (transaction.state === "committed") {
|
|
422
528
|
throw error;
|
|
423
529
|
}
|
|
424
|
-
|
|
530
|
+
await transaction.abort();
|
|
531
|
+
this.recordStats(transaction, Date.now() - startedAt, false);
|
|
532
|
+
if (!enableRetry || attempt === maxRetries || isUnknownTransactionCommitResult(error) || !isTransientTransactionError(error)) {
|
|
425
533
|
throw error;
|
|
426
534
|
}
|
|
427
535
|
await sleep(retryDelay * Math.pow(retryBackoff, attempt));
|
|
@@ -522,22 +630,34 @@ function stringifySessionId(id) {
|
|
|
522
630
|
}
|
|
523
631
|
return String(id);
|
|
524
632
|
}
|
|
525
|
-
function
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
633
|
+
function transactionErrorChain(error) {
|
|
634
|
+
const chain = [];
|
|
635
|
+
const seen = /* @__PURE__ */ new Set();
|
|
636
|
+
let current = error;
|
|
637
|
+
while (current && typeof current === "object" && chain.length < 8 && !seen.has(current)) {
|
|
638
|
+
seen.add(current);
|
|
639
|
+
const candidate = current;
|
|
640
|
+
chain.push(candidate);
|
|
641
|
+
current = candidate.cause;
|
|
642
|
+
}
|
|
643
|
+
return chain;
|
|
644
|
+
}
|
|
645
|
+
function hasTransactionErrorLabel(candidate, label) {
|
|
646
|
+
if (Array.isArray(candidate.errorLabels) && candidate.errorLabels.includes(label)) return true;
|
|
647
|
+
if (typeof candidate.hasErrorLabel === "function") {
|
|
648
|
+
try {
|
|
649
|
+
return Boolean(candidate.hasErrorLabel.call(candidate, label));
|
|
650
|
+
} catch {
|
|
651
|
+
}
|
|
532
652
|
}
|
|
533
|
-
return
|
|
653
|
+
return false;
|
|
534
654
|
}
|
|
535
655
|
function isUnknownTransactionCommitResult(error) {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
return
|
|
656
|
+
return transactionErrorChain(error).some((candidate) => hasTransactionErrorLabel(candidate, "UnknownTransactionCommitResult"));
|
|
657
|
+
}
|
|
658
|
+
function isTransientTransactionError(error) {
|
|
659
|
+
if (isUnknownTransactionCommitResult(error)) return false;
|
|
660
|
+
return transactionErrorChain(error).some((candidate) => hasTransactionErrorLabel(candidate, "TransientTransactionError") || candidate.code === 112 || candidate.code === 117 || candidate.codeName === "WriteConflict" || candidate.codeName === "LockTimeout");
|
|
541
661
|
}
|
|
542
662
|
async function commitTransactionWithRetry(session, logger) {
|
|
543
663
|
const maxAttempts = 3;
|