@event-driven-io/dumbo 0.13.0-beta.45 → 0.13.0-beta.47
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/cloudflare.cjs +432 -69
- package/dist/cloudflare.cjs.map +1 -1
- package/dist/cloudflare.d.cts +2 -2
- package/dist/cloudflare.d.ts +2 -2
- package/dist/cloudflare.js +432 -69
- package/dist/cloudflare.js.map +1 -1
- package/dist/{index-B5krjjrh.d.ts → index-4hcCG4SA.d.ts} +66 -30
- package/dist/{index-BaLXbc2l.d.ts → index-B7366zWR.d.ts} +1 -1
- package/dist/{index-_dj3upBo.d.cts → index-BJ-dm7dG.d.cts} +66 -30
- package/dist/{index-kYttgYkC.d.cts → index-BkJ1pipM.d.cts} +1 -1
- package/dist/{index-C9B46a1u.d.ts → index-CQokD3za.d.cts} +2 -2
- package/dist/{index-BoLWBnxd.d.cts → index-DIJv-L6M.d.ts} +2 -2
- package/dist/index.cjs +523 -269
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +522 -270
- package/dist/index.js.map +1 -1
- package/dist/pg.cjs +168 -28
- package/dist/pg.cjs.map +1 -1
- package/dist/pg.d.cts +2 -2
- package/dist/pg.d.ts +2 -2
- package/dist/pg.js +168 -28
- package/dist/pg.js.map +1 -1
- package/dist/postgresql.cjs +4 -0
- package/dist/postgresql.cjs.map +1 -1
- package/dist/postgresql.d.cts +2 -2
- package/dist/postgresql.d.ts +2 -2
- package/dist/postgresql.js +4 -0
- package/dist/postgresql.js.map +1 -1
- package/dist/sqlite.cjs +429 -68
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +2 -2
- package/dist/sqlite.d.ts +2 -2
- package/dist/sqlite.js +429 -68
- package/dist/sqlite.js.map +1 -1
- package/dist/sqlite3.cjs +639 -343
- package/dist/sqlite3.cjs.map +1 -1
- package/dist/sqlite3.d.cts +10 -10
- package/dist/sqlite3.d.ts +10 -10
- package/dist/sqlite3.js +639 -343
- package/dist/sqlite3.js.map +1 -1
- package/package.json +1 -1
package/dist/sqlite.cjs
CHANGED
|
@@ -201,6 +201,294 @@ var InvalidOperationError = class InvalidOperationError extends DumboError {
|
|
|
201
201
|
}
|
|
202
202
|
};
|
|
203
203
|
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/core/taskProcessing/abort.ts
|
|
206
|
+
const never = { signal: new AbortController().signal };
|
|
207
|
+
const from = (options) => options?.abort ?? never;
|
|
208
|
+
const getSignal = (abort) => "signal" in abort ? abort.signal : abort;
|
|
209
|
+
const reason = (abort) => {
|
|
210
|
+
const signal = getSignal(abort);
|
|
211
|
+
return signal.reason instanceof Error ? signal.reason : new DumboError(typeof signal.reason === "string" ? signal.reason : "Operation aborted");
|
|
212
|
+
};
|
|
213
|
+
const scope = (parent, onAbort) => {
|
|
214
|
+
const controller = new AbortController();
|
|
215
|
+
return {
|
|
216
|
+
abort: (reason) => controller.abort(reason),
|
|
217
|
+
dispose: onAbortSignal(parent, (reason) => {
|
|
218
|
+
controller.abort(reason);
|
|
219
|
+
onAbort?.(reason);
|
|
220
|
+
}),
|
|
221
|
+
signal: controller.signal
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
const execute = (operation, options) => {
|
|
225
|
+
const abort = options?.abort;
|
|
226
|
+
if (!abort) return operation();
|
|
227
|
+
const signal = abort.signal;
|
|
228
|
+
if (signal.aborted) return Promise.reject(reason(abort));
|
|
229
|
+
return new Promise((resolve, reject) => {
|
|
230
|
+
let finished = false;
|
|
231
|
+
const rejectOnAbort = () => {
|
|
232
|
+
if (finished) return;
|
|
233
|
+
finished = true;
|
|
234
|
+
reject(reason(abort));
|
|
235
|
+
};
|
|
236
|
+
signal.addEventListener("abort", rejectOnAbort, { once: true });
|
|
237
|
+
let operationPromise;
|
|
238
|
+
try {
|
|
239
|
+
operationPromise = operation();
|
|
240
|
+
} catch (error) {
|
|
241
|
+
finished = true;
|
|
242
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
243
|
+
reject(error);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
operationPromise.then((result) => {
|
|
247
|
+
if (finished) return;
|
|
248
|
+
finished = true;
|
|
249
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
250
|
+
resolve(result);
|
|
251
|
+
}).catch((error) => {
|
|
252
|
+
if (finished) return;
|
|
253
|
+
finished = true;
|
|
254
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
255
|
+
reject(error);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
const throwIfAborted = (options) => {
|
|
260
|
+
const abort = options?.abort;
|
|
261
|
+
if (abort?.signal.aborted) throw reason(abort);
|
|
262
|
+
};
|
|
263
|
+
const rejectIfAborted = (options) => {
|
|
264
|
+
const abort = options?.abort;
|
|
265
|
+
return abort?.signal.aborted ? Promise.reject(reason(abort)) : void 0;
|
|
266
|
+
};
|
|
267
|
+
const onAbortSignal = (abort, handle) => {
|
|
268
|
+
if (!abort) return () => {};
|
|
269
|
+
const signal = abort.signal;
|
|
270
|
+
if (signal.aborted) {
|
|
271
|
+
handle(reason(abort));
|
|
272
|
+
return () => {};
|
|
273
|
+
}
|
|
274
|
+
const abortListener = () => handle(reason(abort));
|
|
275
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
276
|
+
return () => signal.removeEventListener("abort", abortListener);
|
|
277
|
+
};
|
|
278
|
+
const Abort = {
|
|
279
|
+
execute,
|
|
280
|
+
from,
|
|
281
|
+
never,
|
|
282
|
+
onAbort: onAbortSignal,
|
|
283
|
+
reason,
|
|
284
|
+
rejectIfAborted,
|
|
285
|
+
scope,
|
|
286
|
+
throwIfAborted
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
//#endregion
|
|
290
|
+
//#region src/core/taskProcessing/taskProcessor.ts
|
|
291
|
+
var TaskProcessor = class {
|
|
292
|
+
queue = [];
|
|
293
|
+
isProcessing = false;
|
|
294
|
+
activeTasks = 0;
|
|
295
|
+
activeGroups = /* @__PURE__ */ new Set();
|
|
296
|
+
options;
|
|
297
|
+
stopped = false;
|
|
298
|
+
idleWaiters = [];
|
|
299
|
+
activeTaskAbortCallbacks = /* @__PURE__ */ new Set();
|
|
300
|
+
constructor(options) {
|
|
301
|
+
this.options = options;
|
|
302
|
+
}
|
|
303
|
+
enqueue(task, options) {
|
|
304
|
+
if (options?.abort?.signal.aborted) return Promise.reject(Abort.reason(options.abort.signal));
|
|
305
|
+
if (this.stopped) return Promise.reject(new DumboError("TaskProcessor has been stopped"));
|
|
306
|
+
if (this.queue.length >= this.options.maxQueueSize) return Promise.reject(new TransientDatabaseError("Too many pending connections. Please try again later."));
|
|
307
|
+
return this.schedule(task, options);
|
|
308
|
+
}
|
|
309
|
+
waitForEndOfProcessing() {
|
|
310
|
+
if (this.activeTasks === 0 && this.queue.length === 0) return Promise.resolve();
|
|
311
|
+
return new Promise((resolve) => {
|
|
312
|
+
this.idleWaiters.push(resolve);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
async stop(options) {
|
|
316
|
+
if (this.stopped) return;
|
|
317
|
+
this.stopped = true;
|
|
318
|
+
const stoppedError = new DumboError("TaskProcessor has been stopped");
|
|
319
|
+
for (const item of this.queue.splice(0)) {
|
|
320
|
+
item.abort(stoppedError);
|
|
321
|
+
item.reject(stoppedError);
|
|
322
|
+
}
|
|
323
|
+
this.activeGroups.clear();
|
|
324
|
+
if (options?.force) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
|
|
325
|
+
if (options?.force) return;
|
|
326
|
+
if (options?.closeDeadline === void 0) {
|
|
327
|
+
await this.waitForEndOfProcessing();
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (!await waitForProcessingOrDeadline(this.waitForEndOfProcessing(), options.closeDeadline)) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
|
|
331
|
+
}
|
|
332
|
+
schedule(task, options) {
|
|
333
|
+
return new Promise((resolve, reject) => {
|
|
334
|
+
let didQueueTimeout = false;
|
|
335
|
+
let didAbortBeforeStart = false;
|
|
336
|
+
let didStart = false;
|
|
337
|
+
let queueWaitTimer = noopQueueWaitTimer;
|
|
338
|
+
const abortScope = Abort.scope(options?.abort, (reason) => {
|
|
339
|
+
queueWaitTimer.cancel();
|
|
340
|
+
didAbortBeforeStart = !didStart;
|
|
341
|
+
reject(reason);
|
|
342
|
+
});
|
|
343
|
+
queueWaitTimer = createQueueWaitTimer(this.options.maxTaskIdleTime, (reason) => {
|
|
344
|
+
didQueueTimeout = true;
|
|
345
|
+
abortScope.abort(reason);
|
|
346
|
+
abortScope.dispose();
|
|
347
|
+
reject(reason);
|
|
348
|
+
});
|
|
349
|
+
const taskWithContext = () => {
|
|
350
|
+
return new Promise((resolveTask) => {
|
|
351
|
+
if (didQueueTimeout || didAbortBeforeStart) {
|
|
352
|
+
resolveTask();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
let taskPromise;
|
|
356
|
+
try {
|
|
357
|
+
taskPromise = task({
|
|
358
|
+
ack: resolveTask,
|
|
359
|
+
abort: abortScope
|
|
360
|
+
});
|
|
361
|
+
} catch (err) {
|
|
362
|
+
abortScope.dispose();
|
|
363
|
+
resolveTask();
|
|
364
|
+
reject(err);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
taskPromise.then((result) => {
|
|
368
|
+
abortScope.dispose();
|
|
369
|
+
resolve(result);
|
|
370
|
+
}).catch((err) => {
|
|
371
|
+
abortScope.dispose();
|
|
372
|
+
resolveTask();
|
|
373
|
+
reject(err);
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
};
|
|
377
|
+
this.queue.push({
|
|
378
|
+
task: taskWithContext,
|
|
379
|
+
options,
|
|
380
|
+
reject: (reason) => {
|
|
381
|
+
queueWaitTimer.cancel();
|
|
382
|
+
abortScope.dispose();
|
|
383
|
+
reject(reason);
|
|
384
|
+
},
|
|
385
|
+
markStarted: () => {
|
|
386
|
+
didStart = true;
|
|
387
|
+
queueWaitTimer.cancel();
|
|
388
|
+
},
|
|
389
|
+
abort: (reason) => {
|
|
390
|
+
abortScope.dispose();
|
|
391
|
+
abortScope.abort(reason);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
if (!this.isProcessing) this.ensureProcessing();
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
ensureProcessing() {
|
|
398
|
+
if (this.isProcessing) return;
|
|
399
|
+
this.isProcessing = true;
|
|
400
|
+
this.processQueue();
|
|
401
|
+
}
|
|
402
|
+
processQueue() {
|
|
403
|
+
try {
|
|
404
|
+
while (this.activeTasks < this.options.maxActiveTasks && this.queue.length > 0) {
|
|
405
|
+
const item = this.takeFirstAvailableItem();
|
|
406
|
+
if (item === null) return;
|
|
407
|
+
const groupId = item.options?.taskGroupId;
|
|
408
|
+
if (groupId) this.activeGroups.add(groupId);
|
|
409
|
+
this.activeTasks++;
|
|
410
|
+
this.executeItem(item);
|
|
411
|
+
}
|
|
412
|
+
} catch (error) {
|
|
413
|
+
console.error(error);
|
|
414
|
+
throw error;
|
|
415
|
+
} finally {
|
|
416
|
+
this.isProcessing = false;
|
|
417
|
+
if (this.hasItemsToProcess() && this.activeTasks < this.options.maxActiveTasks) this.ensureProcessing();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async executeItem({ task, options, markStarted, abort }) {
|
|
421
|
+
markStarted();
|
|
422
|
+
this.activeTaskAbortCallbacks.add(abort);
|
|
423
|
+
try {
|
|
424
|
+
await task();
|
|
425
|
+
} finally {
|
|
426
|
+
this.activeTaskAbortCallbacks.delete(abort);
|
|
427
|
+
this.activeTasks--;
|
|
428
|
+
if (options && options.taskGroupId) this.activeGroups.delete(options.taskGroupId);
|
|
429
|
+
this.resolveIdleWaiters();
|
|
430
|
+
this.ensureProcessing();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
takeFirstAvailableItem = () => {
|
|
434
|
+
const taskIndex = this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId));
|
|
435
|
+
if (taskIndex === -1) return null;
|
|
436
|
+
const [item] = this.queue.splice(taskIndex, 1);
|
|
437
|
+
return item ?? null;
|
|
438
|
+
};
|
|
439
|
+
hasItemsToProcess = () => this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId)) !== -1;
|
|
440
|
+
resolveIdleWaiters = () => {
|
|
441
|
+
if (this.activeTasks > 0 || this.queue.length > 0) return;
|
|
442
|
+
const waiters = this.idleWaiters.splice(0);
|
|
443
|
+
for (const resolve of waiters) resolve();
|
|
444
|
+
};
|
|
445
|
+
};
|
|
446
|
+
const noopQueueWaitTimer = { cancel: () => {} };
|
|
447
|
+
const createQueueWaitTimer = (timeoutMs, reject) => {
|
|
448
|
+
if (timeoutMs === void 0) return noopQueueWaitTimer;
|
|
449
|
+
let timeoutId = setTimeout(() => {
|
|
450
|
+
reject(/* @__PURE__ */ new Error("Task was not started within the maximum waiting time"));
|
|
451
|
+
}, timeoutMs);
|
|
452
|
+
timeoutId.unref();
|
|
453
|
+
return { cancel: () => {
|
|
454
|
+
if (!timeoutId) return;
|
|
455
|
+
clearTimeout(timeoutId);
|
|
456
|
+
timeoutId = null;
|
|
457
|
+
} };
|
|
458
|
+
};
|
|
459
|
+
const waitForProcessingOrDeadline = async (processing, closeDeadline) => {
|
|
460
|
+
let timeoutId = null;
|
|
461
|
+
try {
|
|
462
|
+
return await Promise.race([processing.then(() => true), new Promise((resolve) => {
|
|
463
|
+
timeoutId = setTimeout(() => resolve(false), closeDeadline);
|
|
464
|
+
timeoutId.unref();
|
|
465
|
+
})]);
|
|
466
|
+
} finally {
|
|
467
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
//#endregion
|
|
472
|
+
//#region src/core/taskProcessing/executionGuards.ts
|
|
473
|
+
const guardConcurrentAccess = (options) => {
|
|
474
|
+
const taskProcessor = new TaskProcessor({
|
|
475
|
+
maxActiveTasks: options?.maxActiveTasks ?? Number.MAX_SAFE_INTEGER,
|
|
476
|
+
maxQueueSize: options?.maxQueueSize ?? Number.MAX_SAFE_INTEGER,
|
|
477
|
+
...options?.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: options.maxTaskIdleTime } : {}
|
|
478
|
+
});
|
|
479
|
+
return {
|
|
480
|
+
execute: (operation, options) => taskProcessor.enqueue(async (context) => {
|
|
481
|
+
try {
|
|
482
|
+
return await operation({ abort: context.abort });
|
|
483
|
+
} finally {
|
|
484
|
+
context.ack();
|
|
485
|
+
}
|
|
486
|
+
}, options),
|
|
487
|
+
waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
|
|
488
|
+
stop: (options) => taskProcessor.stop(options)
|
|
489
|
+
};
|
|
490
|
+
};
|
|
491
|
+
|
|
204
492
|
//#endregion
|
|
205
493
|
//#region src/core/execute/execute.ts
|
|
206
494
|
const mapSQLQueryResult = (result, mapping) => {
|
|
@@ -210,26 +498,63 @@ const mapSQLQueryResult = (result, mapping) => {
|
|
|
210
498
|
return mappedResult;
|
|
211
499
|
};
|
|
212
500
|
const sqlExecutor = (sqlExecutor, options) => ({
|
|
213
|
-
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions),
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
501
|
+
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions), {
|
|
502
|
+
...options,
|
|
503
|
+
...queryOptions
|
|
504
|
+
}),
|
|
505
|
+
batchQuery: (sqls, queryOptions) => executeInNewDbClient((client) => sqlExecutor.batchQuery(client, sqls, queryOptions), {
|
|
506
|
+
...options,
|
|
507
|
+
...queryOptions
|
|
508
|
+
}),
|
|
509
|
+
command: (sql, commandOptions) => executeInNewDbClient((client) => sqlExecutor.command(client, sql, commandOptions), {
|
|
510
|
+
...options,
|
|
511
|
+
...commandOptions
|
|
512
|
+
}),
|
|
513
|
+
batchCommand: (sqls, commandOptions) => executeInNewDbClient((client) => sqlExecutor.batchCommand(client, sqls, commandOptions), {
|
|
514
|
+
...options,
|
|
515
|
+
...commandOptions
|
|
516
|
+
})
|
|
217
517
|
});
|
|
218
518
|
const sqlExecutorInNewConnection = (options) => ({
|
|
219
|
-
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
519
|
+
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
520
|
+
...options,
|
|
521
|
+
...queryOptions
|
|
522
|
+
}),
|
|
523
|
+
batchQuery: (sqls, queryOptions) => executeInNewConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
524
|
+
...options,
|
|
525
|
+
...queryOptions
|
|
526
|
+
}),
|
|
527
|
+
command: (sql, commandOptions) => executeInNewConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
528
|
+
...options,
|
|
529
|
+
...commandOptions
|
|
530
|
+
}),
|
|
531
|
+
batchCommand: (sqls, commandOptions) => executeInNewConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
532
|
+
...options,
|
|
533
|
+
...commandOptions
|
|
534
|
+
})
|
|
223
535
|
});
|
|
224
536
|
const sqlExecutorInAmbientConnection = (options) => ({
|
|
225
|
-
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
537
|
+
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
538
|
+
...options,
|
|
539
|
+
...queryOptions
|
|
540
|
+
}),
|
|
541
|
+
batchQuery: (sqls, queryOptions) => executeInAmbientConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
542
|
+
...options,
|
|
543
|
+
...queryOptions
|
|
544
|
+
}),
|
|
545
|
+
command: (sql, commandOptions) => executeInAmbientConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
546
|
+
...options,
|
|
547
|
+
...commandOptions
|
|
548
|
+
}),
|
|
549
|
+
batchCommand: (sqls, commandOptions) => executeInAmbientConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
550
|
+
...options,
|
|
551
|
+
...commandOptions
|
|
552
|
+
})
|
|
229
553
|
});
|
|
230
554
|
const executeInNewDbClient = async (handle, options) => {
|
|
555
|
+
Abort.throwIfAborted(options);
|
|
231
556
|
const { connect, close } = options;
|
|
232
|
-
const client = await connect();
|
|
557
|
+
const client = await connect({ abort: Abort.from(options) });
|
|
233
558
|
try {
|
|
234
559
|
return await handle(client);
|
|
235
560
|
} catch (error) {
|
|
@@ -238,7 +563,8 @@ const executeInNewDbClient = async (handle, options) => {
|
|
|
238
563
|
}
|
|
239
564
|
};
|
|
240
565
|
const executeInNewConnection = async (handle, options) => {
|
|
241
|
-
|
|
566
|
+
Abort.throwIfAborted(options);
|
|
567
|
+
const connection = await options.connection({ abort: Abort.from(options) });
|
|
242
568
|
try {
|
|
243
569
|
return await handle(connection);
|
|
244
570
|
} finally {
|
|
@@ -246,10 +572,8 @@ const executeInNewConnection = async (handle, options) => {
|
|
|
246
572
|
}
|
|
247
573
|
};
|
|
248
574
|
const executeInAmbientConnection = async (handle, options) => {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return await handle(connection);
|
|
252
|
-
} finally {}
|
|
575
|
+
Abort.throwIfAborted(options);
|
|
576
|
+
return handle(await options.connection({ abort: Abort.from(options) }));
|
|
253
577
|
};
|
|
254
578
|
|
|
255
579
|
//#endregion
|
|
@@ -317,10 +641,10 @@ const toTransactionResult = (transactionResult) => transactionResult !== void 0
|
|
|
317
641
|
success: true,
|
|
318
642
|
result: transactionResult
|
|
319
643
|
};
|
|
320
|
-
const executeInTransaction = async (transaction, handle) => {
|
|
644
|
+
const executeInTransaction = async (transaction, handle, context = { abort: Abort.never }) => {
|
|
321
645
|
await transaction.begin();
|
|
322
646
|
try {
|
|
323
|
-
const { success, result } = toTransactionResult(await handle(transaction));
|
|
647
|
+
const { success, result } = toTransactionResult(await handle(transaction, context));
|
|
324
648
|
if (success) await transaction.commit();
|
|
325
649
|
else await transaction.rollback();
|
|
326
650
|
return result;
|
|
@@ -329,18 +653,32 @@ const executeInTransaction = async (transaction, handle) => {
|
|
|
329
653
|
throw e;
|
|
330
654
|
}
|
|
331
655
|
};
|
|
656
|
+
const executeInNestedTransaction = async (transaction, handle, options, context) => {
|
|
657
|
+
Abort.throwIfAborted(options);
|
|
658
|
+
if (!(options?.allowNestedTransactions ?? transaction._transactionOptions.allowNestedTransactions ?? false)) throw new InvalidOperationError("Cannot start a nested transaction: allowNestedTransactions is false. Set transactionOptions: { allowNestedTransactions: true } on your pool or connection.");
|
|
659
|
+
return executeInTransaction(transaction, handle, context);
|
|
660
|
+
};
|
|
332
661
|
const transactionFactoryWithDbClient = (connect, initTransaction) => {
|
|
333
662
|
let currentTransaction = void 0;
|
|
334
|
-
const getOrInitCurrentTransaction = (options) =>
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
663
|
+
const getOrInitCurrentTransaction = (options) => {
|
|
664
|
+
Abort.throwIfAborted(options);
|
|
665
|
+
if (currentTransaction) return currentTransaction;
|
|
666
|
+
currentTransaction = initTransaction(connect({ abort: Abort.from(options) }), {
|
|
667
|
+
close: () => {
|
|
668
|
+
currentTransaction = void 0;
|
|
669
|
+
return Promise.resolve();
|
|
670
|
+
},
|
|
671
|
+
...options ?? {}
|
|
672
|
+
});
|
|
673
|
+
return currentTransaction;
|
|
674
|
+
};
|
|
341
675
|
return {
|
|
342
676
|
transaction: getOrInitCurrentTransaction,
|
|
343
|
-
withTransaction: (handle, options) =>
|
|
677
|
+
withTransaction: (handle, options) => {
|
|
678
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
679
|
+
if (abortRejection) return abortRejection;
|
|
680
|
+
return executeInTransaction(getOrInitCurrentTransaction(options), handle, { abort: Abort.from(options) });
|
|
681
|
+
}
|
|
344
682
|
};
|
|
345
683
|
};
|
|
346
684
|
const wrapInConnectionClosure = async (connection, handle) => {
|
|
@@ -352,7 +690,8 @@ const wrapInConnectionClosure = async (connection, handle) => {
|
|
|
352
690
|
};
|
|
353
691
|
const transactionFactoryWithNewConnection = (connect) => ({
|
|
354
692
|
transaction: (options) => {
|
|
355
|
-
|
|
693
|
+
Abort.throwIfAborted(options);
|
|
694
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
356
695
|
const transaction = connection.transaction(options);
|
|
357
696
|
return {
|
|
358
697
|
...transaction,
|
|
@@ -361,7 +700,9 @@ const transactionFactoryWithNewConnection = (connect) => ({
|
|
|
361
700
|
};
|
|
362
701
|
},
|
|
363
702
|
withTransaction: (handle, options) => {
|
|
364
|
-
const
|
|
703
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
704
|
+
if (abortRejection) return abortRejection;
|
|
705
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
365
706
|
const withTx = connection.withTransaction;
|
|
366
707
|
return wrapInConnectionClosure(connection, () => withTx(handle, options));
|
|
367
708
|
}
|
|
@@ -370,19 +711,21 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
370
711
|
close ??= () => Promise.resolve();
|
|
371
712
|
return {
|
|
372
713
|
transaction: (options) => {
|
|
714
|
+
Abort.throwIfAborted(options);
|
|
373
715
|
let conn = null;
|
|
374
716
|
let innerTx = null;
|
|
375
717
|
let connectingPromise = null;
|
|
376
718
|
const ensureConnection = async () => {
|
|
377
719
|
if (conn) return innerTx;
|
|
378
720
|
if (!connectingPromise) connectingPromise = (async () => {
|
|
379
|
-
|
|
721
|
+
Abort.throwIfAborted(options);
|
|
722
|
+
conn = await connect({ abort: Abort.from(options) });
|
|
380
723
|
innerTx = conn.transaction(options);
|
|
381
724
|
})();
|
|
382
725
|
await connectingPromise;
|
|
383
726
|
return innerTx;
|
|
384
727
|
};
|
|
385
|
-
|
|
728
|
+
const tx = {
|
|
386
729
|
driverType,
|
|
387
730
|
get connection() {
|
|
388
731
|
if (!conn) throw new Error("Transaction not started - call begin() first");
|
|
@@ -424,11 +767,14 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
424
767
|
if (conn) await close(conn);
|
|
425
768
|
}
|
|
426
769
|
},
|
|
427
|
-
|
|
770
|
+
withTransaction: (handle, options) => executeInNestedTransaction(tx, handle, options, { abort: Abort.from(options) }),
|
|
771
|
+
_transactionOptions: options ?? {}
|
|
428
772
|
};
|
|
773
|
+
return tx;
|
|
429
774
|
},
|
|
430
775
|
withTransaction: async (handle, options) => {
|
|
431
|
-
|
|
776
|
+
Abort.throwIfAborted(options);
|
|
777
|
+
const conn = await connect({ abort: Abort.from(options) });
|
|
432
778
|
try {
|
|
433
779
|
const withTx = conn.withTransaction;
|
|
434
780
|
return await withTx(handle, options);
|
|
@@ -445,7 +791,10 @@ const createAmbientConnection = (options) => {
|
|
|
445
791
|
const { driverType, client, executor, initTransaction, serializer } = options;
|
|
446
792
|
const clientPromise = Promise.resolve(client);
|
|
447
793
|
const closePromise = Promise.resolve();
|
|
448
|
-
const open = () =>
|
|
794
|
+
const open = (context) => {
|
|
795
|
+
Abort.throwIfAborted(context);
|
|
796
|
+
return clientPromise;
|
|
797
|
+
};
|
|
449
798
|
const close = () => closePromise;
|
|
450
799
|
const typedConnection = {
|
|
451
800
|
driverType,
|
|
@@ -461,9 +810,10 @@ const createConnection = (options) => {
|
|
|
461
810
|
const { driverType, connect, close, initTransaction, executor, serializer } = options;
|
|
462
811
|
let client = null;
|
|
463
812
|
let connectPromise = null;
|
|
464
|
-
const getClient = async () => {
|
|
813
|
+
const getClient = async (context) => {
|
|
814
|
+
Abort.throwIfAborted(context);
|
|
465
815
|
if (client) return client;
|
|
466
|
-
if (!connectPromise) connectPromise = connect().then((c) => {
|
|
816
|
+
if (!connectPromise) connectPromise = connect(context ?? { abort: Abort.never }).then((c) => {
|
|
467
817
|
client = c;
|
|
468
818
|
return c;
|
|
469
819
|
});
|
|
@@ -493,7 +843,10 @@ const createAmbientConnectionPool = (options) => {
|
|
|
493
843
|
getConnection: () => connection,
|
|
494
844
|
execute: connection.execute,
|
|
495
845
|
transaction: (options) => connection.transaction(options),
|
|
496
|
-
withConnection: (handle,
|
|
846
|
+
withConnection: (handle, options) => {
|
|
847
|
+
Abort.throwIfAborted(options);
|
|
848
|
+
return handle(connection, { abort: Abort.from(options) });
|
|
849
|
+
},
|
|
497
850
|
withTransaction: (handle, options) => {
|
|
498
851
|
const withTx = connection.withTransaction;
|
|
499
852
|
return withTx(handle, options);
|
|
@@ -504,48 +857,45 @@ const createSingletonConnectionPool = (options) => {
|
|
|
504
857
|
const { driverType, getConnection } = options;
|
|
505
858
|
let connectionPromise = null;
|
|
506
859
|
let closed = false;
|
|
507
|
-
const
|
|
860
|
+
const operationGuard = guardConcurrentAccess();
|
|
508
861
|
const closedError = () => /* @__PURE__ */ new Error("Singleton connection pool has been closed");
|
|
509
|
-
const
|
|
862
|
+
const executeIfOpen = async (operation, operationOptions) => {
|
|
510
863
|
if (closed) throw closedError();
|
|
511
|
-
|
|
512
|
-
activeOperations.add(activeOperation);
|
|
513
|
-
try {
|
|
514
|
-
return await activeOperation;
|
|
515
|
-
} finally {
|
|
516
|
-
activeOperations.delete(activeOperation);
|
|
517
|
-
}
|
|
864
|
+
return operationGuard.execute(operation, operationOptions);
|
|
518
865
|
};
|
|
519
|
-
const getExistingOrNewConnection = () => {
|
|
520
|
-
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection());
|
|
866
|
+
const getExistingOrNewConnection = (context) => {
|
|
867
|
+
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection(context));
|
|
521
868
|
return connectionPromise;
|
|
522
869
|
};
|
|
523
870
|
const innerTransactionFactory = transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection);
|
|
524
871
|
return {
|
|
525
872
|
driverType,
|
|
526
|
-
connection: () =>
|
|
873
|
+
connection: (connectionOptions) => executeIfOpen((context) => getExistingOrNewConnection(context).then((conn) => wrapPooledConnection(conn, () => Promise.resolve())), connectionOptions),
|
|
527
874
|
execute: (() => {
|
|
528
875
|
const ambientExecutor = sqlExecutorInAmbientConnection({
|
|
529
876
|
driverType,
|
|
530
877
|
connection: getExistingOrNewConnection
|
|
531
878
|
});
|
|
532
879
|
return {
|
|
533
|
-
query: (sql, opts) =>
|
|
534
|
-
batchQuery: (sqls, opts) =>
|
|
535
|
-
command: (sql, opts) =>
|
|
536
|
-
batchCommand: (sqls, opts) =>
|
|
880
|
+
query: (sql, opts) => executeIfOpen(() => ambientExecutor.query(sql, opts), opts),
|
|
881
|
+
batchQuery: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchQuery(sqls, opts), opts),
|
|
882
|
+
command: (sql, opts) => executeIfOpen(() => ambientExecutor.command(sql, opts), opts),
|
|
883
|
+
batchCommand: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchCommand(sqls, opts), opts)
|
|
537
884
|
};
|
|
538
885
|
})(),
|
|
539
|
-
withConnection: (handle,
|
|
886
|
+
withConnection: (handle, options) => executeIfOpen((context) => executeInAmbientConnection((connection) => handle(connection, context), {
|
|
887
|
+
connection: getExistingOrNewConnection,
|
|
888
|
+
...options
|
|
889
|
+
}), options),
|
|
540
890
|
transaction: (transactionOptions) => {
|
|
541
891
|
if (closed) throw closedError();
|
|
542
892
|
return innerTransactionFactory.transaction(transactionOptions);
|
|
543
893
|
},
|
|
544
|
-
withTransaction: (handle, transactionOptions) =>
|
|
894
|
+
withTransaction: (handle, transactionOptions) => executeIfOpen((context) => innerTransactionFactory.withTransaction((tx) => handle(tx, context), transactionOptions), transactionOptions),
|
|
545
895
|
close: async (closeOptions) => {
|
|
546
896
|
if (closed) return;
|
|
547
897
|
closed = true;
|
|
548
|
-
|
|
898
|
+
await operationGuard.stop(closeOptions);
|
|
549
899
|
if (!connectionPromise) return;
|
|
550
900
|
const connection = await connectionPromise;
|
|
551
901
|
connectionPromise = null;
|
|
@@ -557,16 +907,22 @@ const createAlwaysNewConnectionPool = (options) => {
|
|
|
557
907
|
const { driverType, getConnection, connectionOptions } = options;
|
|
558
908
|
return createConnectionPool({
|
|
559
909
|
driverType,
|
|
560
|
-
getConnection: () => connectionOptions ? getConnection(connectionOptions) : getConnection()
|
|
910
|
+
getConnection: (context) => connectionOptions ? getConnection(connectionOptions, context) : getConnection(context)
|
|
561
911
|
});
|
|
562
912
|
};
|
|
563
913
|
const createConnectionPool = (pool) => {
|
|
564
914
|
const { driverType, getConnection } = pool;
|
|
565
|
-
const connection = "connection" in pool ? pool.connection : () =>
|
|
915
|
+
const connection = "connection" in pool ? pool.connection : (options) => {
|
|
916
|
+
Abort.throwIfAborted(options);
|
|
917
|
+
return Promise.resolve(getConnection({ abort: Abort.from(options) }));
|
|
918
|
+
};
|
|
566
919
|
return {
|
|
567
920
|
driverType,
|
|
568
921
|
connection,
|
|
569
|
-
withConnection: "withConnection" in pool ? pool.withConnection : (handle,
|
|
922
|
+
withConnection: "withConnection" in pool ? pool.withConnection : (handle, options) => executeInNewConnection((connection) => handle(connection, { abort: Abort.from(options) }), {
|
|
923
|
+
connection,
|
|
924
|
+
...options
|
|
925
|
+
}),
|
|
570
926
|
close: "close" in pool ? pool.close : () => Promise.resolve(),
|
|
571
927
|
execute: "execute" in pool ? pool.execute : sqlExecutorInNewConnection({
|
|
572
928
|
driverType,
|
|
@@ -1901,9 +2257,9 @@ const sqliteSQLExecutor = (driverType, serializer, formatter, errorMapper) => ({
|
|
|
1901
2257
|
|
|
1902
2258
|
//#endregion
|
|
1903
2259
|
//#region src/storage/sqlite/core/transactions/index.ts
|
|
1904
|
-
const sqliteTransaction = (driverType, connection,
|
|
1905
|
-
|
|
1906
|
-
const
|
|
2260
|
+
const sqliteTransaction = (driverType, connection, defaultOptions, serializer, defaultTransactionMode) => (getClient, options) => {
|
|
2261
|
+
const defaultTransactionOptions = typeof defaultOptions === "boolean" ? { allowNestedTransactions: defaultOptions } : defaultOptions;
|
|
2262
|
+
const allowNestedTransactions = options?.allowNestedTransactions ?? defaultTransactionOptions.allowNestedTransactions ?? false;
|
|
1907
2263
|
const tx = databaseTransaction({
|
|
1908
2264
|
begin: async () => {
|
|
1909
2265
|
const client = await getClient;
|
|
@@ -1931,23 +2287,28 @@ const sqliteTransaction = (driverType, connection, allowNestedTransactions, seri
|
|
|
1931
2287
|
},
|
|
1932
2288
|
releaseSavepoint: async (level) => {
|
|
1933
2289
|
await (await getClient).command(SQL`RELEASE transaction${SQL.plain(level.toString())}`);
|
|
2290
|
+
},
|
|
2291
|
+
rollbackToSavepoint: async (level) => {
|
|
2292
|
+
await (await getClient).command(SQL`ROLLBACK TO transaction${SQL.plain(level.toString())}`);
|
|
1934
2293
|
}
|
|
1935
2294
|
}, {
|
|
1936
2295
|
allowNestedTransactions,
|
|
1937
|
-
useSavepoints
|
|
2296
|
+
useSavepoints: options?.useSavepoints ?? defaultTransactionOptions.useSavepoints ?? false
|
|
1938
2297
|
});
|
|
1939
|
-
|
|
2298
|
+
const transaction = {
|
|
1940
2299
|
connection: connection(),
|
|
1941
2300
|
driverType,
|
|
1942
2301
|
begin: tx.begin,
|
|
1943
2302
|
commit: tx.commit,
|
|
1944
2303
|
rollback: tx.rollback,
|
|
1945
2304
|
execute: sqlExecutor(sqliteSQLExecutor(driverType, serializer), { connect: () => getClient }),
|
|
2305
|
+
withTransaction: (handle, options) => executeInNestedTransaction(transaction, handle, options),
|
|
1946
2306
|
_transactionOptions: {
|
|
1947
2307
|
...options,
|
|
1948
2308
|
allowNestedTransactions
|
|
1949
2309
|
}
|
|
1950
2310
|
};
|
|
2311
|
+
return transaction;
|
|
1951
2312
|
};
|
|
1952
2313
|
|
|
1953
2314
|
//#endregion
|
|
@@ -1992,7 +2353,7 @@ const sqliteAmbientClientConnection = (options) => {
|
|
|
1992
2353
|
return createAmbientConnection({
|
|
1993
2354
|
driverType,
|
|
1994
2355
|
client,
|
|
1995
|
-
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, allowNestedTransactions ?? false, serializer, defaultTransactionMode)),
|
|
2356
|
+
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, { allowNestedTransactions: allowNestedTransactions ?? false }, serializer, defaultTransactionMode)),
|
|
1996
2357
|
executor: ({ serializer }) => sqliteSQLExecutor(driverType, serializer, void 0, errorMapper),
|
|
1997
2358
|
serializer
|
|
1998
2359
|
});
|
|
@@ -2017,7 +2378,7 @@ const sqliteClientConnection = (options) => {
|
|
|
2017
2378
|
if (client && "close" in client && typeof client.close === "function") await client.close();
|
|
2018
2379
|
else if (client && "release" in client && typeof client.release === "function") client.release();
|
|
2019
2380
|
},
|
|
2020
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2381
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
2021
2382
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
2022
2383
|
serializer
|
|
2023
2384
|
});
|
|
@@ -2039,7 +2400,7 @@ const sqlitePoolClientConnection = (options) => {
|
|
|
2039
2400
|
driverType: options.driverType,
|
|
2040
2401
|
connect,
|
|
2041
2402
|
close: () => client !== null ? Promise.resolve(client.release()) : Promise.resolve(),
|
|
2042
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2403
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
2043
2404
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
2044
2405
|
serializer
|
|
2045
2406
|
});
|