@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/sqlite3.cjs
CHANGED
|
@@ -32,7 +32,6 @@ ansis = __toESM(ansis, 1);
|
|
|
32
32
|
let sqlite3 = require("sqlite3");
|
|
33
33
|
sqlite3 = __toESM(sqlite3, 1);
|
|
34
34
|
let os = require("os");
|
|
35
|
-
let node_async_hooks = require("node:async_hooks");
|
|
36
35
|
|
|
37
36
|
//#region src/core/errors/index.ts
|
|
38
37
|
const isNumber = (val) => typeof val === "number" && val === val;
|
|
@@ -205,6 +204,420 @@ var InvalidOperationError = class InvalidOperationError extends DumboError {
|
|
|
205
204
|
}
|
|
206
205
|
};
|
|
207
206
|
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region src/core/taskProcessing/abort.ts
|
|
209
|
+
const never = { signal: new AbortController().signal };
|
|
210
|
+
const from = (options) => options?.abort ?? never;
|
|
211
|
+
const getSignal = (abort) => "signal" in abort ? abort.signal : abort;
|
|
212
|
+
const reason = (abort) => {
|
|
213
|
+
const signal = getSignal(abort);
|
|
214
|
+
return signal.reason instanceof Error ? signal.reason : new DumboError(typeof signal.reason === "string" ? signal.reason : "Operation aborted");
|
|
215
|
+
};
|
|
216
|
+
const scope = (parent, onAbort) => {
|
|
217
|
+
const controller = new AbortController();
|
|
218
|
+
return {
|
|
219
|
+
abort: (reason) => controller.abort(reason),
|
|
220
|
+
dispose: onAbortSignal(parent, (reason) => {
|
|
221
|
+
controller.abort(reason);
|
|
222
|
+
onAbort?.(reason);
|
|
223
|
+
}),
|
|
224
|
+
signal: controller.signal
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
const execute = (operation, options) => {
|
|
228
|
+
const abort = options?.abort;
|
|
229
|
+
if (!abort) return operation();
|
|
230
|
+
const signal = abort.signal;
|
|
231
|
+
if (signal.aborted) return Promise.reject(reason(abort));
|
|
232
|
+
return new Promise((resolve, reject) => {
|
|
233
|
+
let finished = false;
|
|
234
|
+
const rejectOnAbort = () => {
|
|
235
|
+
if (finished) return;
|
|
236
|
+
finished = true;
|
|
237
|
+
reject(reason(abort));
|
|
238
|
+
};
|
|
239
|
+
signal.addEventListener("abort", rejectOnAbort, { once: true });
|
|
240
|
+
let operationPromise;
|
|
241
|
+
try {
|
|
242
|
+
operationPromise = operation();
|
|
243
|
+
} catch (error) {
|
|
244
|
+
finished = true;
|
|
245
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
246
|
+
reject(error);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
operationPromise.then((result) => {
|
|
250
|
+
if (finished) return;
|
|
251
|
+
finished = true;
|
|
252
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
253
|
+
resolve(result);
|
|
254
|
+
}).catch((error) => {
|
|
255
|
+
if (finished) return;
|
|
256
|
+
finished = true;
|
|
257
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
258
|
+
reject(error);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
const throwIfAborted = (options) => {
|
|
263
|
+
const abort = options?.abort;
|
|
264
|
+
if (abort?.signal.aborted) throw reason(abort);
|
|
265
|
+
};
|
|
266
|
+
const rejectIfAborted = (options) => {
|
|
267
|
+
const abort = options?.abort;
|
|
268
|
+
return abort?.signal.aborted ? Promise.reject(reason(abort)) : void 0;
|
|
269
|
+
};
|
|
270
|
+
const onAbortSignal = (abort, handle) => {
|
|
271
|
+
if (!abort) return () => {};
|
|
272
|
+
const signal = abort.signal;
|
|
273
|
+
if (signal.aborted) {
|
|
274
|
+
handle(reason(abort));
|
|
275
|
+
return () => {};
|
|
276
|
+
}
|
|
277
|
+
const abortListener = () => handle(reason(abort));
|
|
278
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
279
|
+
return () => signal.removeEventListener("abort", abortListener);
|
|
280
|
+
};
|
|
281
|
+
const Abort = {
|
|
282
|
+
execute,
|
|
283
|
+
from,
|
|
284
|
+
never,
|
|
285
|
+
onAbort: onAbortSignal,
|
|
286
|
+
reason,
|
|
287
|
+
rejectIfAborted,
|
|
288
|
+
scope,
|
|
289
|
+
throwIfAborted
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/core/taskProcessing/taskProcessor.ts
|
|
294
|
+
var TaskProcessor = class {
|
|
295
|
+
queue = [];
|
|
296
|
+
isProcessing = false;
|
|
297
|
+
activeTasks = 0;
|
|
298
|
+
activeGroups = /* @__PURE__ */ new Set();
|
|
299
|
+
options;
|
|
300
|
+
stopped = false;
|
|
301
|
+
idleWaiters = [];
|
|
302
|
+
activeTaskAbortCallbacks = /* @__PURE__ */ new Set();
|
|
303
|
+
constructor(options) {
|
|
304
|
+
this.options = options;
|
|
305
|
+
}
|
|
306
|
+
enqueue(task, options) {
|
|
307
|
+
if (options?.abort?.signal.aborted) return Promise.reject(Abort.reason(options.abort.signal));
|
|
308
|
+
if (this.stopped) return Promise.reject(new DumboError("TaskProcessor has been stopped"));
|
|
309
|
+
if (this.queue.length >= this.options.maxQueueSize) return Promise.reject(new TransientDatabaseError("Too many pending connections. Please try again later."));
|
|
310
|
+
return this.schedule(task, options);
|
|
311
|
+
}
|
|
312
|
+
waitForEndOfProcessing() {
|
|
313
|
+
if (this.activeTasks === 0 && this.queue.length === 0) return Promise.resolve();
|
|
314
|
+
return new Promise((resolve) => {
|
|
315
|
+
this.idleWaiters.push(resolve);
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
async stop(options) {
|
|
319
|
+
if (this.stopped) return;
|
|
320
|
+
this.stopped = true;
|
|
321
|
+
const stoppedError = new DumboError("TaskProcessor has been stopped");
|
|
322
|
+
for (const item of this.queue.splice(0)) {
|
|
323
|
+
item.abort(stoppedError);
|
|
324
|
+
item.reject(stoppedError);
|
|
325
|
+
}
|
|
326
|
+
this.activeGroups.clear();
|
|
327
|
+
if (options?.force) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
|
|
328
|
+
if (options?.force) return;
|
|
329
|
+
if (options?.closeDeadline === void 0) {
|
|
330
|
+
await this.waitForEndOfProcessing();
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (!await waitForProcessingOrDeadline(this.waitForEndOfProcessing(), options.closeDeadline)) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
|
|
334
|
+
}
|
|
335
|
+
schedule(task, options) {
|
|
336
|
+
return new Promise((resolve, reject) => {
|
|
337
|
+
let didQueueTimeout = false;
|
|
338
|
+
let didAbortBeforeStart = false;
|
|
339
|
+
let didStart = false;
|
|
340
|
+
let queueWaitTimer = noopQueueWaitTimer;
|
|
341
|
+
const abortScope = Abort.scope(options?.abort, (reason) => {
|
|
342
|
+
queueWaitTimer.cancel();
|
|
343
|
+
didAbortBeforeStart = !didStart;
|
|
344
|
+
reject(reason);
|
|
345
|
+
});
|
|
346
|
+
queueWaitTimer = createQueueWaitTimer(this.options.maxTaskIdleTime, (reason) => {
|
|
347
|
+
didQueueTimeout = true;
|
|
348
|
+
abortScope.abort(reason);
|
|
349
|
+
abortScope.dispose();
|
|
350
|
+
reject(reason);
|
|
351
|
+
});
|
|
352
|
+
const taskWithContext = () => {
|
|
353
|
+
return new Promise((resolveTask) => {
|
|
354
|
+
if (didQueueTimeout || didAbortBeforeStart) {
|
|
355
|
+
resolveTask();
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
let taskPromise;
|
|
359
|
+
try {
|
|
360
|
+
taskPromise = task({
|
|
361
|
+
ack: resolveTask,
|
|
362
|
+
abort: abortScope
|
|
363
|
+
});
|
|
364
|
+
} catch (err) {
|
|
365
|
+
abortScope.dispose();
|
|
366
|
+
resolveTask();
|
|
367
|
+
reject(err);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
taskPromise.then((result) => {
|
|
371
|
+
abortScope.dispose();
|
|
372
|
+
resolve(result);
|
|
373
|
+
}).catch((err) => {
|
|
374
|
+
abortScope.dispose();
|
|
375
|
+
resolveTask();
|
|
376
|
+
reject(err);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
};
|
|
380
|
+
this.queue.push({
|
|
381
|
+
task: taskWithContext,
|
|
382
|
+
options,
|
|
383
|
+
reject: (reason) => {
|
|
384
|
+
queueWaitTimer.cancel();
|
|
385
|
+
abortScope.dispose();
|
|
386
|
+
reject(reason);
|
|
387
|
+
},
|
|
388
|
+
markStarted: () => {
|
|
389
|
+
didStart = true;
|
|
390
|
+
queueWaitTimer.cancel();
|
|
391
|
+
},
|
|
392
|
+
abort: (reason) => {
|
|
393
|
+
abortScope.dispose();
|
|
394
|
+
abortScope.abort(reason);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
if (!this.isProcessing) this.ensureProcessing();
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
ensureProcessing() {
|
|
401
|
+
if (this.isProcessing) return;
|
|
402
|
+
this.isProcessing = true;
|
|
403
|
+
this.processQueue();
|
|
404
|
+
}
|
|
405
|
+
processQueue() {
|
|
406
|
+
try {
|
|
407
|
+
while (this.activeTasks < this.options.maxActiveTasks && this.queue.length > 0) {
|
|
408
|
+
const item = this.takeFirstAvailableItem();
|
|
409
|
+
if (item === null) return;
|
|
410
|
+
const groupId = item.options?.taskGroupId;
|
|
411
|
+
if (groupId) this.activeGroups.add(groupId);
|
|
412
|
+
this.activeTasks++;
|
|
413
|
+
this.executeItem(item);
|
|
414
|
+
}
|
|
415
|
+
} catch (error) {
|
|
416
|
+
console.error(error);
|
|
417
|
+
throw error;
|
|
418
|
+
} finally {
|
|
419
|
+
this.isProcessing = false;
|
|
420
|
+
if (this.hasItemsToProcess() && this.activeTasks < this.options.maxActiveTasks) this.ensureProcessing();
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
async executeItem({ task, options, markStarted, abort }) {
|
|
424
|
+
markStarted();
|
|
425
|
+
this.activeTaskAbortCallbacks.add(abort);
|
|
426
|
+
try {
|
|
427
|
+
await task();
|
|
428
|
+
} finally {
|
|
429
|
+
this.activeTaskAbortCallbacks.delete(abort);
|
|
430
|
+
this.activeTasks--;
|
|
431
|
+
if (options && options.taskGroupId) this.activeGroups.delete(options.taskGroupId);
|
|
432
|
+
this.resolveIdleWaiters();
|
|
433
|
+
this.ensureProcessing();
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
takeFirstAvailableItem = () => {
|
|
437
|
+
const taskIndex = this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId));
|
|
438
|
+
if (taskIndex === -1) return null;
|
|
439
|
+
const [item] = this.queue.splice(taskIndex, 1);
|
|
440
|
+
return item ?? null;
|
|
441
|
+
};
|
|
442
|
+
hasItemsToProcess = () => this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId)) !== -1;
|
|
443
|
+
resolveIdleWaiters = () => {
|
|
444
|
+
if (this.activeTasks > 0 || this.queue.length > 0) return;
|
|
445
|
+
const waiters = this.idleWaiters.splice(0);
|
|
446
|
+
for (const resolve of waiters) resolve();
|
|
447
|
+
};
|
|
448
|
+
};
|
|
449
|
+
const noopQueueWaitTimer = { cancel: () => {} };
|
|
450
|
+
const createQueueWaitTimer = (timeoutMs, reject) => {
|
|
451
|
+
if (timeoutMs === void 0) return noopQueueWaitTimer;
|
|
452
|
+
let timeoutId = setTimeout(() => {
|
|
453
|
+
reject(/* @__PURE__ */ new Error("Task was not started within the maximum waiting time"));
|
|
454
|
+
}, timeoutMs);
|
|
455
|
+
timeoutId.unref();
|
|
456
|
+
return { cancel: () => {
|
|
457
|
+
if (!timeoutId) return;
|
|
458
|
+
clearTimeout(timeoutId);
|
|
459
|
+
timeoutId = null;
|
|
460
|
+
} };
|
|
461
|
+
};
|
|
462
|
+
const waitForProcessingOrDeadline = async (processing, closeDeadline) => {
|
|
463
|
+
let timeoutId = null;
|
|
464
|
+
try {
|
|
465
|
+
return await Promise.race([processing.then(() => true), new Promise((resolve) => {
|
|
466
|
+
timeoutId = setTimeout(() => resolve(false), closeDeadline);
|
|
467
|
+
timeoutId.unref();
|
|
468
|
+
})]);
|
|
469
|
+
} finally {
|
|
470
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/core/taskProcessing/executionGuards.ts
|
|
476
|
+
const guardExclusiveAccess = (options) => {
|
|
477
|
+
const taskProcessor = new TaskProcessor({
|
|
478
|
+
maxActiveTasks: 1,
|
|
479
|
+
maxQueueSize: options?.maxQueueSize ?? 1e3,
|
|
480
|
+
...options?.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: options.maxTaskIdleTime } : {}
|
|
481
|
+
});
|
|
482
|
+
return {
|
|
483
|
+
execute: (operation, options) => taskProcessor.enqueue(async (context) => {
|
|
484
|
+
try {
|
|
485
|
+
return await operation(context);
|
|
486
|
+
} finally {
|
|
487
|
+
context.ack();
|
|
488
|
+
}
|
|
489
|
+
}, options),
|
|
490
|
+
waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
|
|
491
|
+
stop: (options) => taskProcessor.stop(options)
|
|
492
|
+
};
|
|
493
|
+
};
|
|
494
|
+
const guardConcurrentAccess = (options) => {
|
|
495
|
+
const taskProcessor = new TaskProcessor({
|
|
496
|
+
maxActiveTasks: options?.maxActiveTasks ?? Number.MAX_SAFE_INTEGER,
|
|
497
|
+
maxQueueSize: options?.maxQueueSize ?? Number.MAX_SAFE_INTEGER,
|
|
498
|
+
...options?.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: options.maxTaskIdleTime } : {}
|
|
499
|
+
});
|
|
500
|
+
return {
|
|
501
|
+
execute: (operation, options) => taskProcessor.enqueue(async (context) => {
|
|
502
|
+
try {
|
|
503
|
+
return await operation({ abort: context.abort });
|
|
504
|
+
} finally {
|
|
505
|
+
context.ack();
|
|
506
|
+
}
|
|
507
|
+
}, options),
|
|
508
|
+
waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
|
|
509
|
+
stop: (options) => taskProcessor.stop(options)
|
|
510
|
+
};
|
|
511
|
+
};
|
|
512
|
+
const guardBoundedAccess = (getResource, options) => {
|
|
513
|
+
let isStopped = false;
|
|
514
|
+
const taskProcessor = new TaskProcessor({
|
|
515
|
+
maxActiveTasks: options.maxResources,
|
|
516
|
+
maxQueueSize: options.maxQueueSize ?? 1e3
|
|
517
|
+
});
|
|
518
|
+
const resourcePool = [];
|
|
519
|
+
const allResources = /* @__PURE__ */ new Set();
|
|
520
|
+
const activeResourceContexts = /* @__PURE__ */ new Map();
|
|
521
|
+
const acquireResource = async (taskContext) => {
|
|
522
|
+
try {
|
|
523
|
+
let resource;
|
|
524
|
+
if (options.reuseResources) resource = resourcePool.pop();
|
|
525
|
+
if (!resource) {
|
|
526
|
+
resource = await getResource({ abort: taskContext.abort });
|
|
527
|
+
allResources.add(resource);
|
|
528
|
+
}
|
|
529
|
+
activeResourceContexts.set(resource, {
|
|
530
|
+
ack: taskContext.ack,
|
|
531
|
+
taskContext
|
|
532
|
+
});
|
|
533
|
+
return resource;
|
|
534
|
+
} catch (e) {
|
|
535
|
+
taskContext.ack();
|
|
536
|
+
throw e;
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
const acquire = async (operationOptions) => taskProcessor.enqueue((taskContext) => acquireResource(taskContext), operationOptions);
|
|
540
|
+
const getActiveResourceContext = (resource) => {
|
|
541
|
+
const activeResourceContext = activeResourceContexts.get(resource);
|
|
542
|
+
if (!activeResourceContext) throw new Error("Acquired resource is not active");
|
|
543
|
+
return activeResourceContext;
|
|
544
|
+
};
|
|
545
|
+
const release = (resource) => {
|
|
546
|
+
const activeResourceContext = activeResourceContexts.get(resource);
|
|
547
|
+
if (activeResourceContext) {
|
|
548
|
+
activeResourceContexts.delete(resource);
|
|
549
|
+
if (options.reuseResources) resourcePool.push(resource);
|
|
550
|
+
activeResourceContext.ack();
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
const execute = async (operation, operationOptions) => {
|
|
554
|
+
return taskProcessor.enqueue(async (taskContext) => {
|
|
555
|
+
const resource = await acquireResource(taskContext);
|
|
556
|
+
const activeResourceContext = getActiveResourceContext(resource);
|
|
557
|
+
try {
|
|
558
|
+
return await operation(resource, { abort: activeResourceContext.taskContext.abort });
|
|
559
|
+
} finally {
|
|
560
|
+
release(resource);
|
|
561
|
+
}
|
|
562
|
+
}, operationOptions);
|
|
563
|
+
};
|
|
564
|
+
return {
|
|
565
|
+
acquire,
|
|
566
|
+
release,
|
|
567
|
+
execute,
|
|
568
|
+
waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
|
|
569
|
+
stop: async (stopOptions) => {
|
|
570
|
+
if (isStopped) return;
|
|
571
|
+
isStopped = true;
|
|
572
|
+
await taskProcessor.stop(stopOptions);
|
|
573
|
+
if (options?.closeResource) {
|
|
574
|
+
const resources = [...allResources];
|
|
575
|
+
allResources.clear();
|
|
576
|
+
resourcePool.length = 0;
|
|
577
|
+
await Promise.all(resources.map(async (resource) => await options.closeResource(resource)));
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
};
|
|
582
|
+
const guardInitializedOnce = (initialize, options) => {
|
|
583
|
+
let initPromise = null;
|
|
584
|
+
const taskProcessor = new TaskProcessor({
|
|
585
|
+
maxActiveTasks: 1,
|
|
586
|
+
maxQueueSize: options?.maxQueueSize ?? 1e3
|
|
587
|
+
});
|
|
588
|
+
const ensureInitialized = async (operationOptions, retryCount = 0) => {
|
|
589
|
+
if (initPromise !== null) return initPromise;
|
|
590
|
+
return taskProcessor.enqueue(async ({ abort, ack }) => {
|
|
591
|
+
if (initPromise !== null) {
|
|
592
|
+
ack();
|
|
593
|
+
return initPromise;
|
|
594
|
+
}
|
|
595
|
+
try {
|
|
596
|
+
const promise = initialize({ abort });
|
|
597
|
+
initPromise = promise;
|
|
598
|
+
const result = await promise;
|
|
599
|
+
ack();
|
|
600
|
+
return result;
|
|
601
|
+
} catch (error) {
|
|
602
|
+
initPromise = null;
|
|
603
|
+
ack();
|
|
604
|
+
if (retryCount < (options?.maxRetries ?? 3)) return ensureInitialized(operationOptions, retryCount + 1);
|
|
605
|
+
throw error;
|
|
606
|
+
}
|
|
607
|
+
}, {
|
|
608
|
+
...operationOptions,
|
|
609
|
+
taskGroupId: (0, uuid.v7)()
|
|
610
|
+
});
|
|
611
|
+
};
|
|
612
|
+
return {
|
|
613
|
+
ensureInitialized,
|
|
614
|
+
reset: () => {
|
|
615
|
+
initPromise = null;
|
|
616
|
+
},
|
|
617
|
+
stop: (options) => taskProcessor.stop(options)
|
|
618
|
+
};
|
|
619
|
+
};
|
|
620
|
+
|
|
208
621
|
//#endregion
|
|
209
622
|
//#region src/core/execute/execute.ts
|
|
210
623
|
const mapSQLQueryResult = (result, mapping) => {
|
|
@@ -229,26 +642,63 @@ var BatchCommandNoChangesError = class BatchCommandNoChangesError extends DumboE
|
|
|
229
642
|
}
|
|
230
643
|
};
|
|
231
644
|
const sqlExecutor = (sqlExecutor, options) => ({
|
|
232
|
-
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions),
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
645
|
+
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions), {
|
|
646
|
+
...options,
|
|
647
|
+
...queryOptions
|
|
648
|
+
}),
|
|
649
|
+
batchQuery: (sqls, queryOptions) => executeInNewDbClient((client) => sqlExecutor.batchQuery(client, sqls, queryOptions), {
|
|
650
|
+
...options,
|
|
651
|
+
...queryOptions
|
|
652
|
+
}),
|
|
653
|
+
command: (sql, commandOptions) => executeInNewDbClient((client) => sqlExecutor.command(client, sql, commandOptions), {
|
|
654
|
+
...options,
|
|
655
|
+
...commandOptions
|
|
656
|
+
}),
|
|
657
|
+
batchCommand: (sqls, commandOptions) => executeInNewDbClient((client) => sqlExecutor.batchCommand(client, sqls, commandOptions), {
|
|
658
|
+
...options,
|
|
659
|
+
...commandOptions
|
|
660
|
+
})
|
|
236
661
|
});
|
|
237
662
|
const sqlExecutorInNewConnection = (options) => ({
|
|
238
|
-
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
663
|
+
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
664
|
+
...options,
|
|
665
|
+
...queryOptions
|
|
666
|
+
}),
|
|
667
|
+
batchQuery: (sqls, queryOptions) => executeInNewConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
668
|
+
...options,
|
|
669
|
+
...queryOptions
|
|
670
|
+
}),
|
|
671
|
+
command: (sql, commandOptions) => executeInNewConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
672
|
+
...options,
|
|
673
|
+
...commandOptions
|
|
674
|
+
}),
|
|
675
|
+
batchCommand: (sqls, commandOptions) => executeInNewConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
676
|
+
...options,
|
|
677
|
+
...commandOptions
|
|
678
|
+
})
|
|
242
679
|
});
|
|
243
680
|
const sqlExecutorInAmbientConnection = (options) => ({
|
|
244
|
-
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
681
|
+
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
682
|
+
...options,
|
|
683
|
+
...queryOptions
|
|
684
|
+
}),
|
|
685
|
+
batchQuery: (sqls, queryOptions) => executeInAmbientConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
686
|
+
...options,
|
|
687
|
+
...queryOptions
|
|
688
|
+
}),
|
|
689
|
+
command: (sql, commandOptions) => executeInAmbientConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
690
|
+
...options,
|
|
691
|
+
...commandOptions
|
|
692
|
+
}),
|
|
693
|
+
batchCommand: (sqls, commandOptions) => executeInAmbientConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
694
|
+
...options,
|
|
695
|
+
...commandOptions
|
|
696
|
+
})
|
|
248
697
|
});
|
|
249
698
|
const executeInNewDbClient = async (handle, options) => {
|
|
699
|
+
Abort.throwIfAborted(options);
|
|
250
700
|
const { connect, close } = options;
|
|
251
|
-
const client = await connect();
|
|
701
|
+
const client = await connect({ abort: Abort.from(options) });
|
|
252
702
|
try {
|
|
253
703
|
return await handle(client);
|
|
254
704
|
} catch (error) {
|
|
@@ -257,7 +707,8 @@ const executeInNewDbClient = async (handle, options) => {
|
|
|
257
707
|
}
|
|
258
708
|
};
|
|
259
709
|
const executeInNewConnection = async (handle, options) => {
|
|
260
|
-
|
|
710
|
+
Abort.throwIfAborted(options);
|
|
711
|
+
const connection = await options.connection({ abort: Abort.from(options) });
|
|
261
712
|
try {
|
|
262
713
|
return await handle(connection);
|
|
263
714
|
} finally {
|
|
@@ -265,10 +716,8 @@ const executeInNewConnection = async (handle, options) => {
|
|
|
265
716
|
}
|
|
266
717
|
};
|
|
267
718
|
const executeInAmbientConnection = async (handle, options) => {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return await handle(connection);
|
|
271
|
-
} finally {}
|
|
719
|
+
Abort.throwIfAborted(options);
|
|
720
|
+
return handle(await options.connection({ abort: Abort.from(options) }));
|
|
272
721
|
};
|
|
273
722
|
|
|
274
723
|
//#endregion
|
|
@@ -336,10 +785,10 @@ const toTransactionResult = (transactionResult) => transactionResult !== void 0
|
|
|
336
785
|
success: true,
|
|
337
786
|
result: transactionResult
|
|
338
787
|
};
|
|
339
|
-
const executeInTransaction = async (transaction, handle) => {
|
|
788
|
+
const executeInTransaction = async (transaction, handle, context = { abort: Abort.never }) => {
|
|
340
789
|
await transaction.begin();
|
|
341
790
|
try {
|
|
342
|
-
const { success, result } = toTransactionResult(await handle(transaction));
|
|
791
|
+
const { success, result } = toTransactionResult(await handle(transaction, context));
|
|
343
792
|
if (success) await transaction.commit();
|
|
344
793
|
else await transaction.rollback();
|
|
345
794
|
return result;
|
|
@@ -348,18 +797,32 @@ const executeInTransaction = async (transaction, handle) => {
|
|
|
348
797
|
throw e;
|
|
349
798
|
}
|
|
350
799
|
};
|
|
800
|
+
const executeInNestedTransaction = async (transaction, handle, options, context) => {
|
|
801
|
+
Abort.throwIfAborted(options);
|
|
802
|
+
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.");
|
|
803
|
+
return executeInTransaction(transaction, handle, context);
|
|
804
|
+
};
|
|
351
805
|
const transactionFactoryWithDbClient = (connect, initTransaction) => {
|
|
352
806
|
let currentTransaction = void 0;
|
|
353
|
-
const getOrInitCurrentTransaction = (options) =>
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
807
|
+
const getOrInitCurrentTransaction = (options) => {
|
|
808
|
+
Abort.throwIfAborted(options);
|
|
809
|
+
if (currentTransaction) return currentTransaction;
|
|
810
|
+
currentTransaction = initTransaction(connect({ abort: Abort.from(options) }), {
|
|
811
|
+
close: () => {
|
|
812
|
+
currentTransaction = void 0;
|
|
813
|
+
return Promise.resolve();
|
|
814
|
+
},
|
|
815
|
+
...options ?? {}
|
|
816
|
+
});
|
|
817
|
+
return currentTransaction;
|
|
818
|
+
};
|
|
360
819
|
return {
|
|
361
820
|
transaction: getOrInitCurrentTransaction,
|
|
362
|
-
withTransaction: (handle, options) =>
|
|
821
|
+
withTransaction: (handle, options) => {
|
|
822
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
823
|
+
if (abortRejection) return abortRejection;
|
|
824
|
+
return executeInTransaction(getOrInitCurrentTransaction(options), handle, { abort: Abort.from(options) });
|
|
825
|
+
}
|
|
363
826
|
};
|
|
364
827
|
};
|
|
365
828
|
const wrapInConnectionClosure = async (connection, handle) => {
|
|
@@ -371,7 +834,8 @@ const wrapInConnectionClosure = async (connection, handle) => {
|
|
|
371
834
|
};
|
|
372
835
|
const transactionFactoryWithNewConnection = (connect) => ({
|
|
373
836
|
transaction: (options) => {
|
|
374
|
-
|
|
837
|
+
Abort.throwIfAborted(options);
|
|
838
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
375
839
|
const transaction = connection.transaction(options);
|
|
376
840
|
return {
|
|
377
841
|
...transaction,
|
|
@@ -380,7 +844,9 @@ const transactionFactoryWithNewConnection = (connect) => ({
|
|
|
380
844
|
};
|
|
381
845
|
},
|
|
382
846
|
withTransaction: (handle, options) => {
|
|
383
|
-
const
|
|
847
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
848
|
+
if (abortRejection) return abortRejection;
|
|
849
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
384
850
|
const withTx = connection.withTransaction;
|
|
385
851
|
return wrapInConnectionClosure(connection, () => withTx(handle, options));
|
|
386
852
|
}
|
|
@@ -389,19 +855,21 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
389
855
|
close ??= () => Promise.resolve();
|
|
390
856
|
return {
|
|
391
857
|
transaction: (options) => {
|
|
858
|
+
Abort.throwIfAborted(options);
|
|
392
859
|
let conn = null;
|
|
393
860
|
let innerTx = null;
|
|
394
861
|
let connectingPromise = null;
|
|
395
862
|
const ensureConnection = async () => {
|
|
396
863
|
if (conn) return innerTx;
|
|
397
864
|
if (!connectingPromise) connectingPromise = (async () => {
|
|
398
|
-
|
|
865
|
+
Abort.throwIfAborted(options);
|
|
866
|
+
conn = await connect({ abort: Abort.from(options) });
|
|
399
867
|
innerTx = conn.transaction(options);
|
|
400
868
|
})();
|
|
401
869
|
await connectingPromise;
|
|
402
870
|
return innerTx;
|
|
403
871
|
};
|
|
404
|
-
|
|
872
|
+
const tx = {
|
|
405
873
|
driverType,
|
|
406
874
|
get connection() {
|
|
407
875
|
if (!conn) throw new Error("Transaction not started - call begin() first");
|
|
@@ -443,11 +911,14 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
443
911
|
if (conn) await close(conn);
|
|
444
912
|
}
|
|
445
913
|
},
|
|
446
|
-
|
|
914
|
+
withTransaction: (handle, options) => executeInNestedTransaction(tx, handle, options, { abort: Abort.from(options) }),
|
|
915
|
+
_transactionOptions: options ?? {}
|
|
447
916
|
};
|
|
917
|
+
return tx;
|
|
448
918
|
},
|
|
449
919
|
withTransaction: async (handle, options) => {
|
|
450
|
-
|
|
920
|
+
Abort.throwIfAborted(options);
|
|
921
|
+
const conn = await connect({ abort: Abort.from(options) });
|
|
451
922
|
try {
|
|
452
923
|
const withTx = conn.withTransaction;
|
|
453
924
|
return await withTx(handle, options);
|
|
@@ -464,7 +935,10 @@ const createAmbientConnection = (options) => {
|
|
|
464
935
|
const { driverType, client, executor, initTransaction, serializer } = options;
|
|
465
936
|
const clientPromise = Promise.resolve(client);
|
|
466
937
|
const closePromise = Promise.resolve();
|
|
467
|
-
const open = () =>
|
|
938
|
+
const open = (context) => {
|
|
939
|
+
Abort.throwIfAborted(context);
|
|
940
|
+
return clientPromise;
|
|
941
|
+
};
|
|
468
942
|
const close = () => closePromise;
|
|
469
943
|
const typedConnection = {
|
|
470
944
|
driverType,
|
|
@@ -480,9 +954,10 @@ const createConnection = (options) => {
|
|
|
480
954
|
const { driverType, connect, close, initTransaction, executor, serializer } = options;
|
|
481
955
|
let client = null;
|
|
482
956
|
let connectPromise = null;
|
|
483
|
-
const getClient = async () => {
|
|
957
|
+
const getClient = async (context) => {
|
|
958
|
+
Abort.throwIfAborted(context);
|
|
484
959
|
if (client) return client;
|
|
485
|
-
if (!connectPromise) connectPromise = connect().then((c) => {
|
|
960
|
+
if (!connectPromise) connectPromise = connect(context ?? { abort: Abort.never }).then((c) => {
|
|
486
961
|
client = c;
|
|
487
962
|
return c;
|
|
488
963
|
});
|
|
@@ -499,209 +974,6 @@ const createConnection = (options) => {
|
|
|
499
974
|
return typedConnection;
|
|
500
975
|
};
|
|
501
976
|
|
|
502
|
-
//#endregion
|
|
503
|
-
//#region src/core/taskProcessing/taskProcessor.ts
|
|
504
|
-
var TaskProcessor = class {
|
|
505
|
-
queue = [];
|
|
506
|
-
isProcessing = false;
|
|
507
|
-
activeTasks = 0;
|
|
508
|
-
activeGroups = /* @__PURE__ */ new Set();
|
|
509
|
-
options;
|
|
510
|
-
stopped = false;
|
|
511
|
-
constructor(options) {
|
|
512
|
-
this.options = options;
|
|
513
|
-
}
|
|
514
|
-
enqueue(task, options) {
|
|
515
|
-
if (this.stopped) return Promise.reject(new DumboError("TaskProcessor has been stopped"));
|
|
516
|
-
if (this.queue.length >= this.options.maxQueueSize) return Promise.reject(new TransientDatabaseError("Too many pending connections. Please try again later."));
|
|
517
|
-
return this.schedule(task, options);
|
|
518
|
-
}
|
|
519
|
-
waitForEndOfProcessing() {
|
|
520
|
-
return this.schedule(({ ack }) => Promise.resolve(ack()));
|
|
521
|
-
}
|
|
522
|
-
async stop(options) {
|
|
523
|
-
if (this.stopped) return;
|
|
524
|
-
this.stopped = true;
|
|
525
|
-
this.queue.length = 0;
|
|
526
|
-
this.activeGroups.clear();
|
|
527
|
-
if (!options?.force) await this.waitForEndOfProcessing();
|
|
528
|
-
}
|
|
529
|
-
schedule(task, options) {
|
|
530
|
-
return promiseWithDeadline((resolve, reject) => {
|
|
531
|
-
const taskWithContext = () => {
|
|
532
|
-
return new Promise((resolveTask, failTask) => {
|
|
533
|
-
task({ ack: resolveTask }).then(resolve).catch((err) => {
|
|
534
|
-
failTask(err);
|
|
535
|
-
reject(err);
|
|
536
|
-
});
|
|
537
|
-
});
|
|
538
|
-
};
|
|
539
|
-
this.queue.push({
|
|
540
|
-
task: taskWithContext,
|
|
541
|
-
options
|
|
542
|
-
});
|
|
543
|
-
if (!this.isProcessing) this.ensureProcessing();
|
|
544
|
-
}, { deadline: this.options.maxTaskIdleTime });
|
|
545
|
-
}
|
|
546
|
-
ensureProcessing() {
|
|
547
|
-
if (this.isProcessing) return;
|
|
548
|
-
this.isProcessing = true;
|
|
549
|
-
this.processQueue();
|
|
550
|
-
}
|
|
551
|
-
processQueue() {
|
|
552
|
-
try {
|
|
553
|
-
while (this.activeTasks < this.options.maxActiveTasks && this.queue.length > 0) {
|
|
554
|
-
const item = this.takeFirstAvailableItem();
|
|
555
|
-
if (item === null) return;
|
|
556
|
-
const groupId = item.options?.taskGroupId;
|
|
557
|
-
if (groupId) this.activeGroups.add(groupId);
|
|
558
|
-
this.activeTasks++;
|
|
559
|
-
this.executeItem(item);
|
|
560
|
-
}
|
|
561
|
-
} catch (error) {
|
|
562
|
-
console.error(error);
|
|
563
|
-
throw error;
|
|
564
|
-
} finally {
|
|
565
|
-
this.isProcessing = false;
|
|
566
|
-
if (this.hasItemsToProcess() && this.activeTasks < this.options.maxActiveTasks) this.ensureProcessing();
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
async executeItem({ task, options }) {
|
|
570
|
-
try {
|
|
571
|
-
await task();
|
|
572
|
-
} finally {
|
|
573
|
-
this.activeTasks--;
|
|
574
|
-
if (options && options.taskGroupId) this.activeGroups.delete(options.taskGroupId);
|
|
575
|
-
this.ensureProcessing();
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
takeFirstAvailableItem = () => {
|
|
579
|
-
const taskIndex = this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId));
|
|
580
|
-
if (taskIndex === -1) return null;
|
|
581
|
-
const [item] = this.queue.splice(taskIndex, 1);
|
|
582
|
-
return item ?? null;
|
|
583
|
-
};
|
|
584
|
-
hasItemsToProcess = () => this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId)) !== -1;
|
|
585
|
-
};
|
|
586
|
-
const DEFAULT_PROMISE_DEADLINE = 2147483647;
|
|
587
|
-
const promiseWithDeadline = (executor, options) => {
|
|
588
|
-
return new Promise((resolve, reject) => {
|
|
589
|
-
let taskStarted = false;
|
|
590
|
-
let timeoutId = null;
|
|
591
|
-
const deadline = options.deadline ?? DEFAULT_PROMISE_DEADLINE;
|
|
592
|
-
timeoutId = setTimeout(() => {
|
|
593
|
-
if (!taskStarted) reject(/* @__PURE__ */ new Error("Task was not started within the maximum waiting time"));
|
|
594
|
-
}, deadline);
|
|
595
|
-
timeoutId.unref();
|
|
596
|
-
executor((value) => {
|
|
597
|
-
taskStarted = true;
|
|
598
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
599
|
-
timeoutId = null;
|
|
600
|
-
resolve(value);
|
|
601
|
-
}, (reason) => {
|
|
602
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
603
|
-
timeoutId = null;
|
|
604
|
-
reject(reason);
|
|
605
|
-
});
|
|
606
|
-
});
|
|
607
|
-
};
|
|
608
|
-
|
|
609
|
-
//#endregion
|
|
610
|
-
//#region src/core/taskProcessing/executionGuards.ts
|
|
611
|
-
const guardBoundedAccess = (getResource, options) => {
|
|
612
|
-
let isStopped = false;
|
|
613
|
-
const taskProcessor = new TaskProcessor({
|
|
614
|
-
maxActiveTasks: options.maxResources,
|
|
615
|
-
maxQueueSize: options.maxQueueSize ?? 1e3
|
|
616
|
-
});
|
|
617
|
-
const resourcePool = [];
|
|
618
|
-
const allResources = /* @__PURE__ */ new Set();
|
|
619
|
-
const ackCallbacks = /* @__PURE__ */ new Map();
|
|
620
|
-
const acquire = async () => taskProcessor.enqueue(async ({ ack }) => {
|
|
621
|
-
try {
|
|
622
|
-
let resource;
|
|
623
|
-
if (options.reuseResources) resource = resourcePool.pop();
|
|
624
|
-
if (!resource) {
|
|
625
|
-
resource = await getResource();
|
|
626
|
-
allResources.add(resource);
|
|
627
|
-
}
|
|
628
|
-
ackCallbacks.set(resource, ack);
|
|
629
|
-
return resource;
|
|
630
|
-
} catch (e) {
|
|
631
|
-
ack();
|
|
632
|
-
throw e;
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
const release = (resource) => {
|
|
636
|
-
const ack = ackCallbacks.get(resource);
|
|
637
|
-
if (ack) {
|
|
638
|
-
ackCallbacks.delete(resource);
|
|
639
|
-
if (options.reuseResources) resourcePool.push(resource);
|
|
640
|
-
ack();
|
|
641
|
-
}
|
|
642
|
-
};
|
|
643
|
-
const execute = async (operation) => {
|
|
644
|
-
const resource = await acquire();
|
|
645
|
-
try {
|
|
646
|
-
return await operation(resource);
|
|
647
|
-
} finally {
|
|
648
|
-
release(resource);
|
|
649
|
-
}
|
|
650
|
-
};
|
|
651
|
-
return {
|
|
652
|
-
acquire,
|
|
653
|
-
release,
|
|
654
|
-
execute,
|
|
655
|
-
waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
|
|
656
|
-
stop: async (stopOptions) => {
|
|
657
|
-
if (isStopped) return;
|
|
658
|
-
isStopped = true;
|
|
659
|
-
if (options?.closeResource) {
|
|
660
|
-
const resources = [...allResources];
|
|
661
|
-
allResources.clear();
|
|
662
|
-
resourcePool.length = 0;
|
|
663
|
-
await Promise.all(resources.map(async (resource) => await options.closeResource(resource)));
|
|
664
|
-
}
|
|
665
|
-
await taskProcessor.stop(stopOptions);
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
};
|
|
669
|
-
const guardInitializedOnce = (initialize, options) => {
|
|
670
|
-
let initPromise = null;
|
|
671
|
-
const taskProcessor = new TaskProcessor({
|
|
672
|
-
maxActiveTasks: 1,
|
|
673
|
-
maxQueueSize: options?.maxQueueSize ?? 1e3
|
|
674
|
-
});
|
|
675
|
-
const ensureInitialized = async (retryCount = 0) => {
|
|
676
|
-
if (initPromise !== null) return initPromise;
|
|
677
|
-
return taskProcessor.enqueue(async ({ ack }) => {
|
|
678
|
-
if (initPromise !== null) {
|
|
679
|
-
ack();
|
|
680
|
-
return initPromise;
|
|
681
|
-
}
|
|
682
|
-
try {
|
|
683
|
-
const promise = initialize();
|
|
684
|
-
initPromise = promise;
|
|
685
|
-
const result = await promise;
|
|
686
|
-
ack();
|
|
687
|
-
return result;
|
|
688
|
-
} catch (error) {
|
|
689
|
-
initPromise = null;
|
|
690
|
-
ack();
|
|
691
|
-
if (retryCount < (options?.maxRetries ?? 3)) return ensureInitialized(retryCount + 1);
|
|
692
|
-
throw error;
|
|
693
|
-
}
|
|
694
|
-
}, { taskGroupId: (0, uuid.v7)() });
|
|
695
|
-
};
|
|
696
|
-
return {
|
|
697
|
-
ensureInitialized,
|
|
698
|
-
reset: () => {
|
|
699
|
-
initPromise = null;
|
|
700
|
-
},
|
|
701
|
-
stop: (options) => taskProcessor.stop(options)
|
|
702
|
-
};
|
|
703
|
-
};
|
|
704
|
-
|
|
705
977
|
//#endregion
|
|
706
978
|
//#region src/core/connections/pool.ts
|
|
707
979
|
const wrapPooledConnection = (conn, onClose) => ({
|
|
@@ -715,7 +987,10 @@ const createAmbientConnectionPool = (options) => {
|
|
|
715
987
|
getConnection: () => connection,
|
|
716
988
|
execute: connection.execute,
|
|
717
989
|
transaction: (options) => connection.transaction(options),
|
|
718
|
-
withConnection: (handle,
|
|
990
|
+
withConnection: (handle, options) => {
|
|
991
|
+
Abort.throwIfAborted(options);
|
|
992
|
+
return handle(connection, { abort: Abort.from(options) });
|
|
993
|
+
},
|
|
719
994
|
withTransaction: (handle, options) => {
|
|
720
995
|
const withTx = connection.withTransaction;
|
|
721
996
|
return withTx(handle, options);
|
|
@@ -726,48 +1001,45 @@ const createSingletonConnectionPool = (options) => {
|
|
|
726
1001
|
const { driverType, getConnection } = options;
|
|
727
1002
|
let connectionPromise = null;
|
|
728
1003
|
let closed = false;
|
|
729
|
-
const
|
|
1004
|
+
const operationGuard = guardConcurrentAccess();
|
|
730
1005
|
const closedError = () => /* @__PURE__ */ new Error("Singleton connection pool has been closed");
|
|
731
|
-
const
|
|
1006
|
+
const executeIfOpen = async (operation, operationOptions) => {
|
|
732
1007
|
if (closed) throw closedError();
|
|
733
|
-
|
|
734
|
-
activeOperations.add(activeOperation);
|
|
735
|
-
try {
|
|
736
|
-
return await activeOperation;
|
|
737
|
-
} finally {
|
|
738
|
-
activeOperations.delete(activeOperation);
|
|
739
|
-
}
|
|
1008
|
+
return operationGuard.execute(operation, operationOptions);
|
|
740
1009
|
};
|
|
741
|
-
const getExistingOrNewConnection = () => {
|
|
742
|
-
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection());
|
|
1010
|
+
const getExistingOrNewConnection = (context) => {
|
|
1011
|
+
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection(context));
|
|
743
1012
|
return connectionPromise;
|
|
744
1013
|
};
|
|
745
1014
|
const innerTransactionFactory = transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection);
|
|
746
1015
|
return {
|
|
747
1016
|
driverType,
|
|
748
|
-
connection: () =>
|
|
1017
|
+
connection: (connectionOptions) => executeIfOpen((context) => getExistingOrNewConnection(context).then((conn) => wrapPooledConnection(conn, () => Promise.resolve())), connectionOptions),
|
|
749
1018
|
execute: (() => {
|
|
750
1019
|
const ambientExecutor = sqlExecutorInAmbientConnection({
|
|
751
1020
|
driverType,
|
|
752
1021
|
connection: getExistingOrNewConnection
|
|
753
1022
|
});
|
|
754
1023
|
return {
|
|
755
|
-
query: (sql, opts) =>
|
|
756
|
-
batchQuery: (sqls, opts) =>
|
|
757
|
-
command: (sql, opts) =>
|
|
758
|
-
batchCommand: (sqls, opts) =>
|
|
1024
|
+
query: (sql, opts) => executeIfOpen(() => ambientExecutor.query(sql, opts), opts),
|
|
1025
|
+
batchQuery: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchQuery(sqls, opts), opts),
|
|
1026
|
+
command: (sql, opts) => executeIfOpen(() => ambientExecutor.command(sql, opts), opts),
|
|
1027
|
+
batchCommand: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchCommand(sqls, opts), opts)
|
|
759
1028
|
};
|
|
760
1029
|
})(),
|
|
761
|
-
withConnection: (handle,
|
|
1030
|
+
withConnection: (handle, options) => executeIfOpen((context) => executeInAmbientConnection((connection) => handle(connection, context), {
|
|
1031
|
+
connection: getExistingOrNewConnection,
|
|
1032
|
+
...options
|
|
1033
|
+
}), options),
|
|
762
1034
|
transaction: (transactionOptions) => {
|
|
763
1035
|
if (closed) throw closedError();
|
|
764
1036
|
return innerTransactionFactory.transaction(transactionOptions);
|
|
765
1037
|
},
|
|
766
|
-
withTransaction: (handle, transactionOptions) =>
|
|
1038
|
+
withTransaction: (handle, transactionOptions) => executeIfOpen((context) => innerTransactionFactory.withTransaction((tx) => handle(tx, context), transactionOptions), transactionOptions),
|
|
767
1039
|
close: async (closeOptions) => {
|
|
768
1040
|
if (closed) return;
|
|
769
1041
|
closed = true;
|
|
770
|
-
|
|
1042
|
+
await operationGuard.stop(closeOptions);
|
|
771
1043
|
if (!connectionPromise) return;
|
|
772
1044
|
const connection = await connectionPromise;
|
|
773
1045
|
connectionPromise = null;
|
|
@@ -777,63 +1049,49 @@ const createSingletonConnectionPool = (options) => {
|
|
|
777
1049
|
};
|
|
778
1050
|
const createBoundedConnectionPool = (options) => {
|
|
779
1051
|
const { driverType, maxConnections } = options;
|
|
780
|
-
const
|
|
781
|
-
const getTrackedConnection = async () => {
|
|
782
|
-
const connection = await options.getConnection();
|
|
783
|
-
allConnections.add(connection);
|
|
784
|
-
return connection;
|
|
785
|
-
};
|
|
786
|
-
const guardMaxConnections = guardBoundedAccess(getTrackedConnection, {
|
|
1052
|
+
const guardMaxConnections = guardBoundedAccess(options.getConnection, {
|
|
787
1053
|
maxResources: maxConnections,
|
|
788
|
-
reuseResources: true
|
|
1054
|
+
reuseResources: true,
|
|
1055
|
+
closeResource: (connection) => connection.close()
|
|
789
1056
|
});
|
|
790
1057
|
let closed = false;
|
|
791
1058
|
const closedError = () => /* @__PURE__ */ new Error("Bounded connection pool has been closed");
|
|
792
1059
|
const ensureOpen = () => {
|
|
793
1060
|
if (closed) throw closedError();
|
|
794
1061
|
};
|
|
795
|
-
const
|
|
796
|
-
const connections = [...allConnections];
|
|
797
|
-
allConnections.clear();
|
|
798
|
-
await Promise.all(connections.map((conn) => conn.close()));
|
|
799
|
-
};
|
|
800
|
-
const executeWithPooling = async (operation) => {
|
|
1062
|
+
const executeWithPooledConnection = async (operation, operationOptions) => {
|
|
801
1063
|
ensureOpen();
|
|
802
|
-
|
|
803
|
-
try {
|
|
804
|
-
return await operation(conn);
|
|
805
|
-
} finally {
|
|
806
|
-
guardMaxConnections.release(conn);
|
|
807
|
-
}
|
|
1064
|
+
return guardMaxConnections.execute(operation, operationOptions);
|
|
808
1065
|
};
|
|
809
1066
|
return {
|
|
810
1067
|
driverType,
|
|
811
|
-
connection: async () => {
|
|
1068
|
+
connection: async (connectionOptions) => {
|
|
812
1069
|
ensureOpen();
|
|
813
|
-
const conn = await guardMaxConnections.acquire();
|
|
1070
|
+
const conn = await guardMaxConnections.acquire(connectionOptions);
|
|
814
1071
|
return wrapPooledConnection(conn, () => Promise.resolve(guardMaxConnections.release(conn)));
|
|
815
1072
|
},
|
|
816
1073
|
execute: {
|
|
817
|
-
query: (sql, opts) =>
|
|
818
|
-
batchQuery: (sqls, opts) =>
|
|
819
|
-
command: (sql, opts) =>
|
|
820
|
-
batchCommand: (sqls, opts) =>
|
|
1074
|
+
query: (sql, opts) => executeWithPooledConnection((c) => c.execute.query(sql, opts), opts),
|
|
1075
|
+
batchQuery: (sqls, opts) => executeWithPooledConnection((c) => c.execute.batchQuery(sqls, opts), opts),
|
|
1076
|
+
command: (sql, opts) => executeWithPooledConnection((c) => c.execute.command(sql, opts), opts),
|
|
1077
|
+
batchCommand: (sqls, opts) => executeWithPooledConnection((c) => c.execute.batchCommand(sqls, opts), opts)
|
|
821
1078
|
},
|
|
822
|
-
withConnection:
|
|
1079
|
+
withConnection: executeWithPooledConnection,
|
|
823
1080
|
transaction: (transactionOptions) => {
|
|
824
1081
|
ensureOpen();
|
|
825
|
-
return transactionFactoryWithAsyncAmbientConnection(driverType, guardMaxConnections.acquire
|
|
1082
|
+
return transactionFactoryWithAsyncAmbientConnection(driverType, (context) => guardMaxConnections.acquire({
|
|
1083
|
+
...transactionOptions,
|
|
1084
|
+
abort: context.abort
|
|
1085
|
+
}), guardMaxConnections.release).transaction(transactionOptions);
|
|
826
1086
|
},
|
|
827
|
-
withTransaction: (handle, transactionOptions) =>
|
|
1087
|
+
withTransaction: (handle, transactionOptions) => executeWithPooledConnection((conn, context) => {
|
|
828
1088
|
const withTx = conn.withTransaction;
|
|
829
|
-
return withTx(handle, transactionOptions);
|
|
830
|
-
}),
|
|
1089
|
+
return withTx((tx) => handle(tx, context), transactionOptions);
|
|
1090
|
+
}, transactionOptions),
|
|
831
1091
|
close: async (closeOptions) => {
|
|
832
1092
|
if (closed) return;
|
|
833
1093
|
closed = true;
|
|
834
|
-
|
|
835
|
-
await guardMaxConnections.stop({ force: true });
|
|
836
|
-
await closeAllConnections();
|
|
1094
|
+
await guardMaxConnections.stop(closeOptions);
|
|
837
1095
|
}
|
|
838
1096
|
};
|
|
839
1097
|
};
|
|
@@ -841,16 +1099,22 @@ const createAlwaysNewConnectionPool = (options) => {
|
|
|
841
1099
|
const { driverType, getConnection, connectionOptions } = options;
|
|
842
1100
|
return createConnectionPool({
|
|
843
1101
|
driverType,
|
|
844
|
-
getConnection: () => connectionOptions ? getConnection(connectionOptions) : getConnection()
|
|
1102
|
+
getConnection: (context) => connectionOptions ? getConnection(connectionOptions, context) : getConnection(context)
|
|
845
1103
|
});
|
|
846
1104
|
};
|
|
847
1105
|
const createConnectionPool = (pool) => {
|
|
848
1106
|
const { driverType, getConnection } = pool;
|
|
849
|
-
const connection = "connection" in pool ? pool.connection : () =>
|
|
1107
|
+
const connection = "connection" in pool ? pool.connection : (options) => {
|
|
1108
|
+
Abort.throwIfAborted(options);
|
|
1109
|
+
return Promise.resolve(getConnection({ abort: Abort.from(options) }));
|
|
1110
|
+
};
|
|
850
1111
|
return {
|
|
851
1112
|
driverType,
|
|
852
1113
|
connection,
|
|
853
|
-
withConnection: "withConnection" in pool ? pool.withConnection : (handle,
|
|
1114
|
+
withConnection: "withConnection" in pool ? pool.withConnection : (handle, options) => executeInNewConnection((connection) => handle(connection, { abort: Abort.from(options) }), {
|
|
1115
|
+
connection,
|
|
1116
|
+
...options
|
|
1117
|
+
}),
|
|
854
1118
|
close: "close" in pool ? pool.close : () => Promise.resolve(),
|
|
855
1119
|
execute: "execute" in pool ? pool.execute : sqlExecutorInNewConnection({
|
|
856
1120
|
driverType,
|
|
@@ -2190,9 +2454,9 @@ const sqliteSQLExecutor = (driverType, serializer, formatter, errorMapper) => ({
|
|
|
2190
2454
|
|
|
2191
2455
|
//#endregion
|
|
2192
2456
|
//#region src/storage/sqlite/core/transactions/index.ts
|
|
2193
|
-
const sqliteTransaction = (driverType, connection,
|
|
2194
|
-
|
|
2195
|
-
const
|
|
2457
|
+
const sqliteTransaction = (driverType, connection, defaultOptions, serializer, defaultTransactionMode) => (getClient, options) => {
|
|
2458
|
+
const defaultTransactionOptions = typeof defaultOptions === "boolean" ? { allowNestedTransactions: defaultOptions } : defaultOptions;
|
|
2459
|
+
const allowNestedTransactions = options?.allowNestedTransactions ?? defaultTransactionOptions.allowNestedTransactions ?? false;
|
|
2196
2460
|
const tx = databaseTransaction({
|
|
2197
2461
|
begin: async () => {
|
|
2198
2462
|
const client = await getClient;
|
|
@@ -2220,23 +2484,28 @@ const sqliteTransaction = (driverType, connection, allowNestedTransactions, seri
|
|
|
2220
2484
|
},
|
|
2221
2485
|
releaseSavepoint: async (level) => {
|
|
2222
2486
|
await (await getClient).command(SQL`RELEASE transaction${SQL.plain(level.toString())}`);
|
|
2487
|
+
},
|
|
2488
|
+
rollbackToSavepoint: async (level) => {
|
|
2489
|
+
await (await getClient).command(SQL`ROLLBACK TO transaction${SQL.plain(level.toString())}`);
|
|
2223
2490
|
}
|
|
2224
2491
|
}, {
|
|
2225
2492
|
allowNestedTransactions,
|
|
2226
|
-
useSavepoints
|
|
2493
|
+
useSavepoints: options?.useSavepoints ?? defaultTransactionOptions.useSavepoints ?? false
|
|
2227
2494
|
});
|
|
2228
|
-
|
|
2495
|
+
const transaction = {
|
|
2229
2496
|
connection: connection(),
|
|
2230
2497
|
driverType,
|
|
2231
2498
|
begin: tx.begin,
|
|
2232
2499
|
commit: tx.commit,
|
|
2233
2500
|
rollback: tx.rollback,
|
|
2234
2501
|
execute: sqlExecutor(sqliteSQLExecutor(driverType, serializer), { connect: () => getClient }),
|
|
2502
|
+
withTransaction: (handle, options) => executeInNestedTransaction(transaction, handle, options),
|
|
2235
2503
|
_transactionOptions: {
|
|
2236
2504
|
...options,
|
|
2237
2505
|
allowNestedTransactions
|
|
2238
2506
|
}
|
|
2239
2507
|
};
|
|
2508
|
+
return transaction;
|
|
2240
2509
|
};
|
|
2241
2510
|
|
|
2242
2511
|
//#endregion
|
|
@@ -2281,7 +2550,7 @@ const sqliteAmbientClientConnection = (options) => {
|
|
|
2281
2550
|
return createAmbientConnection({
|
|
2282
2551
|
driverType,
|
|
2283
2552
|
client,
|
|
2284
|
-
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, allowNestedTransactions ?? false, serializer, defaultTransactionMode)),
|
|
2553
|
+
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, { allowNestedTransactions: allowNestedTransactions ?? false }, serializer, defaultTransactionMode)),
|
|
2285
2554
|
executor: ({ serializer }) => sqliteSQLExecutor(driverType, serializer, void 0, errorMapper),
|
|
2286
2555
|
serializer
|
|
2287
2556
|
});
|
|
@@ -2306,7 +2575,7 @@ const sqliteClientConnection = (options) => {
|
|
|
2306
2575
|
if (client && "close" in client && typeof client.close === "function") await client.close();
|
|
2307
2576
|
else if (client && "release" in client && typeof client.release === "function") client.release();
|
|
2308
2577
|
},
|
|
2309
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2578
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
2310
2579
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
2311
2580
|
serializer
|
|
2312
2581
|
});
|
|
@@ -2328,7 +2597,7 @@ const sqlitePoolClientConnection = (options) => {
|
|
|
2328
2597
|
driverType: options.driverType,
|
|
2329
2598
|
connect,
|
|
2330
2599
|
close: () => client !== null ? Promise.resolve(client.release()) : Promise.resolve(),
|
|
2331
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2600
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
2332
2601
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
2333
2602
|
serializer
|
|
2334
2603
|
});
|
|
@@ -2702,43 +2971,67 @@ const sqlite3Connection = (options) => sqliteConnection({
|
|
|
2702
2971
|
//#endregion
|
|
2703
2972
|
//#region src/storage/sqlite/sqlite3/pool/singletonPool.ts
|
|
2704
2973
|
const DEFAULT_SQLITE_MAX_TASK_IDLE_TIME_MS = 3e4;
|
|
2974
|
+
const createSQLiteTransactionContext = () => {
|
|
2975
|
+
if (typeof process === "undefined") return void 0;
|
|
2976
|
+
const asyncHooks = process.getBuiltinModule?.("node:async_hooks");
|
|
2977
|
+
if (!asyncHooks) return void 0;
|
|
2978
|
+
const context = new asyncHooks.AsyncLocalStorage();
|
|
2979
|
+
return {
|
|
2980
|
+
current: () => context.getStore(),
|
|
2981
|
+
run: (active, handle) => context.run(active, handle)
|
|
2982
|
+
};
|
|
2983
|
+
};
|
|
2705
2984
|
const sqlite3SingletonPool = (options) => {
|
|
2706
|
-
const maxTaskIdleTime = options.maxTaskIdleTime ?? 3e4;
|
|
2707
2985
|
const inner = createSingletonConnectionPool({
|
|
2708
2986
|
driverType: options.driverType,
|
|
2709
2987
|
getConnection: options.getConnection,
|
|
2710
2988
|
...options.closeConnection ? { closeConnection: options.closeConnection } : {}
|
|
2711
2989
|
});
|
|
2712
|
-
const
|
|
2713
|
-
maxActiveTasks: 1,
|
|
2990
|
+
const writerGuard = guardExclusiveAccess({
|
|
2714
2991
|
maxQueueSize: options.maxQueueSize ?? 1e3,
|
|
2715
|
-
maxTaskIdleTime
|
|
2992
|
+
maxTaskIdleTime: options.maxTaskIdleTime ?? 3e4
|
|
2716
2993
|
});
|
|
2717
|
-
const
|
|
2718
|
-
const
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2994
|
+
const transactionContext = options.transactionContext ?? createSQLiteTransactionContext();
|
|
2995
|
+
const activeConnection = () => transactionContext?.current()?.connection;
|
|
2996
|
+
const runInTransactionContext = (connection, operation) => transactionContext ? transactionContext.run({ connection }, operation) : operation();
|
|
2997
|
+
const transactionAwareConnection = (connection) => transactionContext ? {
|
|
2998
|
+
...connection,
|
|
2999
|
+
withTransaction: (handle, transactionOptions) => runInTransactionContext(connection, () => connection.withTransaction(handle, transactionOptions))
|
|
3000
|
+
} : connection;
|
|
3001
|
+
const runConnectionTransaction = (connection, handle, context, transactionOptions) => {
|
|
3002
|
+
const withTransaction = connection.withTransaction;
|
|
3003
|
+
return withTransaction((transaction) => handle(transaction, context), transactionOptions);
|
|
3004
|
+
};
|
|
3005
|
+
const runOnWriterConnection = (handle, options) => {
|
|
3006
|
+
const connection = activeConnection();
|
|
3007
|
+
if (connection) return inner.withConnection((_connection, context) => handle(connection, context), options);
|
|
3008
|
+
return writerGuard.execute((context) => inner.withConnection((connection) => handle(connection, context), options), options);
|
|
2727
3009
|
};
|
|
3010
|
+
const withWriterConnection = (handle, options) => runOnWriterConnection((connection, context) => handle(transactionAwareConnection(connection), context), options);
|
|
2728
3011
|
return {
|
|
2729
3012
|
driverType: inner.driverType,
|
|
2730
3013
|
connection: inner.connection.bind(inner),
|
|
2731
3014
|
transaction: inner.transaction.bind(inner),
|
|
2732
|
-
withConnection: (handle, connectionOptions) =>
|
|
2733
|
-
withTransaction: (handle, transactionOptions) =>
|
|
3015
|
+
withConnection: (handle, connectionOptions) => activeConnection() || !connectionOptions?.readonly ? withWriterConnection(handle, connectionOptions) : inner.withConnection(handle, connectionOptions),
|
|
3016
|
+
withTransaction: (handle, transactionOptions) => {
|
|
3017
|
+
const connection = activeConnection();
|
|
3018
|
+
if (connection) return inner.withConnection((_connection, context) => runConnectionTransaction(connection, handle, context, transactionOptions), transactionOptions);
|
|
3019
|
+
return runOnWriterConnection((connection, context) => runInTransactionContext(connection, () => runConnectionTransaction(connection, handle, context, transactionOptions)), transactionOptions);
|
|
3020
|
+
},
|
|
2734
3021
|
execute: {
|
|
2735
|
-
query: (sql, queryOptions) =>
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
3022
|
+
query: (sql, queryOptions) => {
|
|
3023
|
+
const connection = activeConnection();
|
|
3024
|
+
return connection ? connection.execute.query(sql, queryOptions) : inner.execute.query(sql, queryOptions);
|
|
3025
|
+
},
|
|
3026
|
+
batchQuery: (sqls, queryOptions) => {
|
|
3027
|
+
const connection = activeConnection();
|
|
3028
|
+
return connection ? connection.execute.batchQuery(sqls, queryOptions) : inner.execute.batchQuery(sqls, queryOptions);
|
|
3029
|
+
},
|
|
3030
|
+
command: (sql, commandOptions) => runOnWriterConnection((connection) => connection.execute.command(sql, commandOptions), commandOptions),
|
|
3031
|
+
batchCommand: (sqls, commandOptions) => runOnWriterConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), commandOptions)
|
|
2739
3032
|
},
|
|
2740
3033
|
close: async (closeOptions) => {
|
|
2741
|
-
await
|
|
3034
|
+
await writerGuard.stop(closeOptions);
|
|
2742
3035
|
await inner.close(closeOptions);
|
|
2743
3036
|
}
|
|
2744
3037
|
};
|
|
@@ -2749,15 +3042,16 @@ const sqlite3SingletonPool = (options) => {
|
|
|
2749
3042
|
const sqliteDualConnectionPool = (options) => {
|
|
2750
3043
|
const { sqliteConnectionFactory, connectionOptions } = options;
|
|
2751
3044
|
const readerPoolSize = options.readerPoolSize ?? Math.max(4, (0, os.cpus)().length);
|
|
3045
|
+
const transactionContext = createSQLiteTransactionContext();
|
|
2752
3046
|
let databaseInitPromise = null;
|
|
2753
|
-
const guardSingleConnection = guardInitializedOnce(async () => {
|
|
3047
|
+
const guardSingleConnection = guardInitializedOnce(async (context) => {
|
|
2754
3048
|
if (databaseInitPromise !== null) return databaseInitPromise;
|
|
2755
3049
|
const initConnection = sqliteConnectionFactory({
|
|
2756
3050
|
...connectionOptions,
|
|
2757
3051
|
skipDatabasePragmas: false,
|
|
2758
3052
|
readonly: false
|
|
2759
3053
|
});
|
|
2760
|
-
const initPromise = initConnection.open();
|
|
3054
|
+
const initPromise = initConnection.open(context);
|
|
2761
3055
|
databaseInitPromise = initPromise;
|
|
2762
3056
|
try {
|
|
2763
3057
|
await initPromise;
|
|
@@ -2768,45 +3062,47 @@ const sqliteDualConnectionPool = (options) => {
|
|
|
2768
3062
|
throw mapSqliteError(error);
|
|
2769
3063
|
}
|
|
2770
3064
|
});
|
|
2771
|
-
const ensureDatabaseInitialized = async () => {
|
|
3065
|
+
const ensureDatabaseInitialized = async (context) => {
|
|
2772
3066
|
if (databaseInitPromise !== null) return databaseInitPromise;
|
|
2773
|
-
return guardSingleConnection.ensureInitialized();
|
|
3067
|
+
return guardSingleConnection.ensureInitialized({ abort: context.abort });
|
|
2774
3068
|
};
|
|
2775
|
-
const wrappedConnectionFactory = async (readonly, connectionOptions) => {
|
|
2776
|
-
await ensureDatabaseInitialized();
|
|
3069
|
+
const wrappedConnectionFactory = async (readonly, connectionOptions, context) => {
|
|
3070
|
+
await ensureDatabaseInitialized(context);
|
|
2777
3071
|
const connection = sqliteConnectionFactory({
|
|
2778
3072
|
...connectionOptions,
|
|
2779
3073
|
skipDatabasePragmas: true,
|
|
2780
3074
|
readonly
|
|
2781
3075
|
});
|
|
2782
|
-
await connection.open();
|
|
3076
|
+
await connection.open(context);
|
|
2783
3077
|
return connection;
|
|
2784
3078
|
};
|
|
2785
3079
|
const writerPool = sqlite3SingletonPool({
|
|
2786
3080
|
driverType: options.driverType,
|
|
2787
|
-
getConnection: () => wrappedConnectionFactory(false, connectionOptions),
|
|
3081
|
+
getConnection: (context) => wrappedConnectionFactory(false, connectionOptions, context),
|
|
3082
|
+
...transactionContext ? { transactionContext } : {},
|
|
2788
3083
|
...options.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: options.maxTaskIdleTime } : {}
|
|
2789
3084
|
});
|
|
2790
3085
|
const readerPool = createBoundedConnectionPool({
|
|
2791
3086
|
driverType: options.driverType,
|
|
2792
|
-
getConnection: () => wrappedConnectionFactory(true, connectionOptions),
|
|
3087
|
+
getConnection: (context) => wrappedConnectionFactory(true, connectionOptions, context),
|
|
2793
3088
|
maxConnections: readerPoolSize
|
|
2794
3089
|
});
|
|
3090
|
+
const hasActiveTransaction = () => !!transactionContext?.current();
|
|
2795
3091
|
return {
|
|
2796
3092
|
driverType: options.driverType,
|
|
2797
|
-
connection: (connectionOptions) => connectionOptions?.readonly ? readerPool.connection(connectionOptions) : writerPool.connection(connectionOptions),
|
|
3093
|
+
connection: (connectionOptions) => connectionOptions?.readonly && !hasActiveTransaction() ? readerPool.connection(connectionOptions) : writerPool.connection(connectionOptions),
|
|
2798
3094
|
execute: {
|
|
2799
|
-
query: (...args) => readerPool.execute.query(...args),
|
|
2800
|
-
batchQuery: (...args) => readerPool.execute.batchQuery(...args),
|
|
3095
|
+
query: (...args) => hasActiveTransaction() ? writerPool.execute.query(...args) : readerPool.execute.query(...args),
|
|
3096
|
+
batchQuery: (...args) => hasActiveTransaction() ? writerPool.execute.batchQuery(...args) : readerPool.execute.batchQuery(...args),
|
|
2801
3097
|
command: (...args) => writerPool.execute.command(...args),
|
|
2802
3098
|
batchCommand: (...args) => writerPool.execute.batchCommand(...args)
|
|
2803
3099
|
},
|
|
2804
|
-
withConnection: (handle, connectionOptions) => connectionOptions?.readonly ? readerPool.withConnection(handle, connectionOptions) : writerPool.withConnection(handle, connectionOptions),
|
|
3100
|
+
withConnection: (handle, connectionOptions) => connectionOptions?.readonly && !hasActiveTransaction() ? readerPool.withConnection(handle, connectionOptions) : writerPool.withConnection(handle, connectionOptions),
|
|
2805
3101
|
transaction: writerPool.transaction,
|
|
2806
3102
|
withTransaction: writerPool.withTransaction,
|
|
2807
|
-
close: async () => {
|
|
2808
|
-
await guardSingleConnection.stop();
|
|
2809
|
-
await Promise.all([writerPool.close(), readerPool.close()]);
|
|
3103
|
+
close: async (closeOptions) => {
|
|
3104
|
+
await guardSingleConnection.stop(closeOptions);
|
|
3105
|
+
await Promise.all([writerPool.close(closeOptions), readerPool.close(closeOptions)]);
|
|
2810
3106
|
}
|
|
2811
3107
|
};
|
|
2812
3108
|
};
|