@event-driven-io/dumbo 0.13.0-beta.46 → 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.js
CHANGED
|
@@ -2,7 +2,6 @@ import { v7 } from "uuid";
|
|
|
2
2
|
import ansis from "ansis";
|
|
3
3
|
import sqlite3 from "sqlite3";
|
|
4
4
|
import { cpus } from "os";
|
|
5
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
5
|
|
|
7
6
|
//#region src/core/errors/index.ts
|
|
8
7
|
const isNumber = (val) => typeof val === "number" && val === val;
|
|
@@ -175,6 +174,420 @@ var InvalidOperationError = class InvalidOperationError extends DumboError {
|
|
|
175
174
|
}
|
|
176
175
|
};
|
|
177
176
|
|
|
177
|
+
//#endregion
|
|
178
|
+
//#region src/core/taskProcessing/abort.ts
|
|
179
|
+
const never = { signal: new AbortController().signal };
|
|
180
|
+
const from = (options) => options?.abort ?? never;
|
|
181
|
+
const getSignal = (abort) => "signal" in abort ? abort.signal : abort;
|
|
182
|
+
const reason = (abort) => {
|
|
183
|
+
const signal = getSignal(abort);
|
|
184
|
+
return signal.reason instanceof Error ? signal.reason : new DumboError(typeof signal.reason === "string" ? signal.reason : "Operation aborted");
|
|
185
|
+
};
|
|
186
|
+
const scope = (parent, onAbort) => {
|
|
187
|
+
const controller = new AbortController();
|
|
188
|
+
return {
|
|
189
|
+
abort: (reason) => controller.abort(reason),
|
|
190
|
+
dispose: onAbortSignal(parent, (reason) => {
|
|
191
|
+
controller.abort(reason);
|
|
192
|
+
onAbort?.(reason);
|
|
193
|
+
}),
|
|
194
|
+
signal: controller.signal
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
const execute = (operation, options) => {
|
|
198
|
+
const abort = options?.abort;
|
|
199
|
+
if (!abort) return operation();
|
|
200
|
+
const signal = abort.signal;
|
|
201
|
+
if (signal.aborted) return Promise.reject(reason(abort));
|
|
202
|
+
return new Promise((resolve, reject) => {
|
|
203
|
+
let finished = false;
|
|
204
|
+
const rejectOnAbort = () => {
|
|
205
|
+
if (finished) return;
|
|
206
|
+
finished = true;
|
|
207
|
+
reject(reason(abort));
|
|
208
|
+
};
|
|
209
|
+
signal.addEventListener("abort", rejectOnAbort, { once: true });
|
|
210
|
+
let operationPromise;
|
|
211
|
+
try {
|
|
212
|
+
operationPromise = operation();
|
|
213
|
+
} catch (error) {
|
|
214
|
+
finished = true;
|
|
215
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
216
|
+
reject(error);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
operationPromise.then((result) => {
|
|
220
|
+
if (finished) return;
|
|
221
|
+
finished = true;
|
|
222
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
223
|
+
resolve(result);
|
|
224
|
+
}).catch((error) => {
|
|
225
|
+
if (finished) return;
|
|
226
|
+
finished = true;
|
|
227
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
228
|
+
reject(error);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
const throwIfAborted = (options) => {
|
|
233
|
+
const abort = options?.abort;
|
|
234
|
+
if (abort?.signal.aborted) throw reason(abort);
|
|
235
|
+
};
|
|
236
|
+
const rejectIfAborted = (options) => {
|
|
237
|
+
const abort = options?.abort;
|
|
238
|
+
return abort?.signal.aborted ? Promise.reject(reason(abort)) : void 0;
|
|
239
|
+
};
|
|
240
|
+
const onAbortSignal = (abort, handle) => {
|
|
241
|
+
if (!abort) return () => {};
|
|
242
|
+
const signal = abort.signal;
|
|
243
|
+
if (signal.aborted) {
|
|
244
|
+
handle(reason(abort));
|
|
245
|
+
return () => {};
|
|
246
|
+
}
|
|
247
|
+
const abortListener = () => handle(reason(abort));
|
|
248
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
249
|
+
return () => signal.removeEventListener("abort", abortListener);
|
|
250
|
+
};
|
|
251
|
+
const Abort = {
|
|
252
|
+
execute,
|
|
253
|
+
from,
|
|
254
|
+
never,
|
|
255
|
+
onAbort: onAbortSignal,
|
|
256
|
+
reason,
|
|
257
|
+
rejectIfAborted,
|
|
258
|
+
scope,
|
|
259
|
+
throwIfAborted
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
//#endregion
|
|
263
|
+
//#region src/core/taskProcessing/taskProcessor.ts
|
|
264
|
+
var TaskProcessor = class {
|
|
265
|
+
queue = [];
|
|
266
|
+
isProcessing = false;
|
|
267
|
+
activeTasks = 0;
|
|
268
|
+
activeGroups = /* @__PURE__ */ new Set();
|
|
269
|
+
options;
|
|
270
|
+
stopped = false;
|
|
271
|
+
idleWaiters = [];
|
|
272
|
+
activeTaskAbortCallbacks = /* @__PURE__ */ new Set();
|
|
273
|
+
constructor(options) {
|
|
274
|
+
this.options = options;
|
|
275
|
+
}
|
|
276
|
+
enqueue(task, options) {
|
|
277
|
+
if (options?.abort?.signal.aborted) return Promise.reject(Abort.reason(options.abort.signal));
|
|
278
|
+
if (this.stopped) return Promise.reject(new DumboError("TaskProcessor has been stopped"));
|
|
279
|
+
if (this.queue.length >= this.options.maxQueueSize) return Promise.reject(new TransientDatabaseError("Too many pending connections. Please try again later."));
|
|
280
|
+
return this.schedule(task, options);
|
|
281
|
+
}
|
|
282
|
+
waitForEndOfProcessing() {
|
|
283
|
+
if (this.activeTasks === 0 && this.queue.length === 0) return Promise.resolve();
|
|
284
|
+
return new Promise((resolve) => {
|
|
285
|
+
this.idleWaiters.push(resolve);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
async stop(options) {
|
|
289
|
+
if (this.stopped) return;
|
|
290
|
+
this.stopped = true;
|
|
291
|
+
const stoppedError = new DumboError("TaskProcessor has been stopped");
|
|
292
|
+
for (const item of this.queue.splice(0)) {
|
|
293
|
+
item.abort(stoppedError);
|
|
294
|
+
item.reject(stoppedError);
|
|
295
|
+
}
|
|
296
|
+
this.activeGroups.clear();
|
|
297
|
+
if (options?.force) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
|
|
298
|
+
if (options?.force) return;
|
|
299
|
+
if (options?.closeDeadline === void 0) {
|
|
300
|
+
await this.waitForEndOfProcessing();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (!await waitForProcessingOrDeadline(this.waitForEndOfProcessing(), options.closeDeadline)) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
|
|
304
|
+
}
|
|
305
|
+
schedule(task, options) {
|
|
306
|
+
return new Promise((resolve, reject) => {
|
|
307
|
+
let didQueueTimeout = false;
|
|
308
|
+
let didAbortBeforeStart = false;
|
|
309
|
+
let didStart = false;
|
|
310
|
+
let queueWaitTimer = noopQueueWaitTimer;
|
|
311
|
+
const abortScope = Abort.scope(options?.abort, (reason) => {
|
|
312
|
+
queueWaitTimer.cancel();
|
|
313
|
+
didAbortBeforeStart = !didStart;
|
|
314
|
+
reject(reason);
|
|
315
|
+
});
|
|
316
|
+
queueWaitTimer = createQueueWaitTimer(this.options.maxTaskIdleTime, (reason) => {
|
|
317
|
+
didQueueTimeout = true;
|
|
318
|
+
abortScope.abort(reason);
|
|
319
|
+
abortScope.dispose();
|
|
320
|
+
reject(reason);
|
|
321
|
+
});
|
|
322
|
+
const taskWithContext = () => {
|
|
323
|
+
return new Promise((resolveTask) => {
|
|
324
|
+
if (didQueueTimeout || didAbortBeforeStart) {
|
|
325
|
+
resolveTask();
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
let taskPromise;
|
|
329
|
+
try {
|
|
330
|
+
taskPromise = task({
|
|
331
|
+
ack: resolveTask,
|
|
332
|
+
abort: abortScope
|
|
333
|
+
});
|
|
334
|
+
} catch (err) {
|
|
335
|
+
abortScope.dispose();
|
|
336
|
+
resolveTask();
|
|
337
|
+
reject(err);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
taskPromise.then((result) => {
|
|
341
|
+
abortScope.dispose();
|
|
342
|
+
resolve(result);
|
|
343
|
+
}).catch((err) => {
|
|
344
|
+
abortScope.dispose();
|
|
345
|
+
resolveTask();
|
|
346
|
+
reject(err);
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
this.queue.push({
|
|
351
|
+
task: taskWithContext,
|
|
352
|
+
options,
|
|
353
|
+
reject: (reason) => {
|
|
354
|
+
queueWaitTimer.cancel();
|
|
355
|
+
abortScope.dispose();
|
|
356
|
+
reject(reason);
|
|
357
|
+
},
|
|
358
|
+
markStarted: () => {
|
|
359
|
+
didStart = true;
|
|
360
|
+
queueWaitTimer.cancel();
|
|
361
|
+
},
|
|
362
|
+
abort: (reason) => {
|
|
363
|
+
abortScope.dispose();
|
|
364
|
+
abortScope.abort(reason);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
if (!this.isProcessing) this.ensureProcessing();
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
ensureProcessing() {
|
|
371
|
+
if (this.isProcessing) return;
|
|
372
|
+
this.isProcessing = true;
|
|
373
|
+
this.processQueue();
|
|
374
|
+
}
|
|
375
|
+
processQueue() {
|
|
376
|
+
try {
|
|
377
|
+
while (this.activeTasks < this.options.maxActiveTasks && this.queue.length > 0) {
|
|
378
|
+
const item = this.takeFirstAvailableItem();
|
|
379
|
+
if (item === null) return;
|
|
380
|
+
const groupId = item.options?.taskGroupId;
|
|
381
|
+
if (groupId) this.activeGroups.add(groupId);
|
|
382
|
+
this.activeTasks++;
|
|
383
|
+
this.executeItem(item);
|
|
384
|
+
}
|
|
385
|
+
} catch (error) {
|
|
386
|
+
console.error(error);
|
|
387
|
+
throw error;
|
|
388
|
+
} finally {
|
|
389
|
+
this.isProcessing = false;
|
|
390
|
+
if (this.hasItemsToProcess() && this.activeTasks < this.options.maxActiveTasks) this.ensureProcessing();
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async executeItem({ task, options, markStarted, abort }) {
|
|
394
|
+
markStarted();
|
|
395
|
+
this.activeTaskAbortCallbacks.add(abort);
|
|
396
|
+
try {
|
|
397
|
+
await task();
|
|
398
|
+
} finally {
|
|
399
|
+
this.activeTaskAbortCallbacks.delete(abort);
|
|
400
|
+
this.activeTasks--;
|
|
401
|
+
if (options && options.taskGroupId) this.activeGroups.delete(options.taskGroupId);
|
|
402
|
+
this.resolveIdleWaiters();
|
|
403
|
+
this.ensureProcessing();
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
takeFirstAvailableItem = () => {
|
|
407
|
+
const taskIndex = this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId));
|
|
408
|
+
if (taskIndex === -1) return null;
|
|
409
|
+
const [item] = this.queue.splice(taskIndex, 1);
|
|
410
|
+
return item ?? null;
|
|
411
|
+
};
|
|
412
|
+
hasItemsToProcess = () => this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId)) !== -1;
|
|
413
|
+
resolveIdleWaiters = () => {
|
|
414
|
+
if (this.activeTasks > 0 || this.queue.length > 0) return;
|
|
415
|
+
const waiters = this.idleWaiters.splice(0);
|
|
416
|
+
for (const resolve of waiters) resolve();
|
|
417
|
+
};
|
|
418
|
+
};
|
|
419
|
+
const noopQueueWaitTimer = { cancel: () => {} };
|
|
420
|
+
const createQueueWaitTimer = (timeoutMs, reject) => {
|
|
421
|
+
if (timeoutMs === void 0) return noopQueueWaitTimer;
|
|
422
|
+
let timeoutId = setTimeout(() => {
|
|
423
|
+
reject(/* @__PURE__ */ new Error("Task was not started within the maximum waiting time"));
|
|
424
|
+
}, timeoutMs);
|
|
425
|
+
timeoutId.unref();
|
|
426
|
+
return { cancel: () => {
|
|
427
|
+
if (!timeoutId) return;
|
|
428
|
+
clearTimeout(timeoutId);
|
|
429
|
+
timeoutId = null;
|
|
430
|
+
} };
|
|
431
|
+
};
|
|
432
|
+
const waitForProcessingOrDeadline = async (processing, closeDeadline) => {
|
|
433
|
+
let timeoutId = null;
|
|
434
|
+
try {
|
|
435
|
+
return await Promise.race([processing.then(() => true), new Promise((resolve) => {
|
|
436
|
+
timeoutId = setTimeout(() => resolve(false), closeDeadline);
|
|
437
|
+
timeoutId.unref();
|
|
438
|
+
})]);
|
|
439
|
+
} finally {
|
|
440
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
//#endregion
|
|
445
|
+
//#region src/core/taskProcessing/executionGuards.ts
|
|
446
|
+
const guardExclusiveAccess = (options) => {
|
|
447
|
+
const taskProcessor = new TaskProcessor({
|
|
448
|
+
maxActiveTasks: 1,
|
|
449
|
+
maxQueueSize: options?.maxQueueSize ?? 1e3,
|
|
450
|
+
...options?.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: options.maxTaskIdleTime } : {}
|
|
451
|
+
});
|
|
452
|
+
return {
|
|
453
|
+
execute: (operation, options) => taskProcessor.enqueue(async (context) => {
|
|
454
|
+
try {
|
|
455
|
+
return await operation(context);
|
|
456
|
+
} finally {
|
|
457
|
+
context.ack();
|
|
458
|
+
}
|
|
459
|
+
}, options),
|
|
460
|
+
waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
|
|
461
|
+
stop: (options) => taskProcessor.stop(options)
|
|
462
|
+
};
|
|
463
|
+
};
|
|
464
|
+
const guardConcurrentAccess = (options) => {
|
|
465
|
+
const taskProcessor = new TaskProcessor({
|
|
466
|
+
maxActiveTasks: options?.maxActiveTasks ?? Number.MAX_SAFE_INTEGER,
|
|
467
|
+
maxQueueSize: options?.maxQueueSize ?? Number.MAX_SAFE_INTEGER,
|
|
468
|
+
...options?.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: options.maxTaskIdleTime } : {}
|
|
469
|
+
});
|
|
470
|
+
return {
|
|
471
|
+
execute: (operation, options) => taskProcessor.enqueue(async (context) => {
|
|
472
|
+
try {
|
|
473
|
+
return await operation({ abort: context.abort });
|
|
474
|
+
} finally {
|
|
475
|
+
context.ack();
|
|
476
|
+
}
|
|
477
|
+
}, options),
|
|
478
|
+
waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
|
|
479
|
+
stop: (options) => taskProcessor.stop(options)
|
|
480
|
+
};
|
|
481
|
+
};
|
|
482
|
+
const guardBoundedAccess = (getResource, options) => {
|
|
483
|
+
let isStopped = false;
|
|
484
|
+
const taskProcessor = new TaskProcessor({
|
|
485
|
+
maxActiveTasks: options.maxResources,
|
|
486
|
+
maxQueueSize: options.maxQueueSize ?? 1e3
|
|
487
|
+
});
|
|
488
|
+
const resourcePool = [];
|
|
489
|
+
const allResources = /* @__PURE__ */ new Set();
|
|
490
|
+
const activeResourceContexts = /* @__PURE__ */ new Map();
|
|
491
|
+
const acquireResource = async (taskContext) => {
|
|
492
|
+
try {
|
|
493
|
+
let resource;
|
|
494
|
+
if (options.reuseResources) resource = resourcePool.pop();
|
|
495
|
+
if (!resource) {
|
|
496
|
+
resource = await getResource({ abort: taskContext.abort });
|
|
497
|
+
allResources.add(resource);
|
|
498
|
+
}
|
|
499
|
+
activeResourceContexts.set(resource, {
|
|
500
|
+
ack: taskContext.ack,
|
|
501
|
+
taskContext
|
|
502
|
+
});
|
|
503
|
+
return resource;
|
|
504
|
+
} catch (e) {
|
|
505
|
+
taskContext.ack();
|
|
506
|
+
throw e;
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
const acquire = async (operationOptions) => taskProcessor.enqueue((taskContext) => acquireResource(taskContext), operationOptions);
|
|
510
|
+
const getActiveResourceContext = (resource) => {
|
|
511
|
+
const activeResourceContext = activeResourceContexts.get(resource);
|
|
512
|
+
if (!activeResourceContext) throw new Error("Acquired resource is not active");
|
|
513
|
+
return activeResourceContext;
|
|
514
|
+
};
|
|
515
|
+
const release = (resource) => {
|
|
516
|
+
const activeResourceContext = activeResourceContexts.get(resource);
|
|
517
|
+
if (activeResourceContext) {
|
|
518
|
+
activeResourceContexts.delete(resource);
|
|
519
|
+
if (options.reuseResources) resourcePool.push(resource);
|
|
520
|
+
activeResourceContext.ack();
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
const execute = async (operation, operationOptions) => {
|
|
524
|
+
return taskProcessor.enqueue(async (taskContext) => {
|
|
525
|
+
const resource = await acquireResource(taskContext);
|
|
526
|
+
const activeResourceContext = getActiveResourceContext(resource);
|
|
527
|
+
try {
|
|
528
|
+
return await operation(resource, { abort: activeResourceContext.taskContext.abort });
|
|
529
|
+
} finally {
|
|
530
|
+
release(resource);
|
|
531
|
+
}
|
|
532
|
+
}, operationOptions);
|
|
533
|
+
};
|
|
534
|
+
return {
|
|
535
|
+
acquire,
|
|
536
|
+
release,
|
|
537
|
+
execute,
|
|
538
|
+
waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
|
|
539
|
+
stop: async (stopOptions) => {
|
|
540
|
+
if (isStopped) return;
|
|
541
|
+
isStopped = true;
|
|
542
|
+
await taskProcessor.stop(stopOptions);
|
|
543
|
+
if (options?.closeResource) {
|
|
544
|
+
const resources = [...allResources];
|
|
545
|
+
allResources.clear();
|
|
546
|
+
resourcePool.length = 0;
|
|
547
|
+
await Promise.all(resources.map(async (resource) => await options.closeResource(resource)));
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
};
|
|
552
|
+
const guardInitializedOnce = (initialize, options) => {
|
|
553
|
+
let initPromise = null;
|
|
554
|
+
const taskProcessor = new TaskProcessor({
|
|
555
|
+
maxActiveTasks: 1,
|
|
556
|
+
maxQueueSize: options?.maxQueueSize ?? 1e3
|
|
557
|
+
});
|
|
558
|
+
const ensureInitialized = async (operationOptions, retryCount = 0) => {
|
|
559
|
+
if (initPromise !== null) return initPromise;
|
|
560
|
+
return taskProcessor.enqueue(async ({ abort, ack }) => {
|
|
561
|
+
if (initPromise !== null) {
|
|
562
|
+
ack();
|
|
563
|
+
return initPromise;
|
|
564
|
+
}
|
|
565
|
+
try {
|
|
566
|
+
const promise = initialize({ abort });
|
|
567
|
+
initPromise = promise;
|
|
568
|
+
const result = await promise;
|
|
569
|
+
ack();
|
|
570
|
+
return result;
|
|
571
|
+
} catch (error) {
|
|
572
|
+
initPromise = null;
|
|
573
|
+
ack();
|
|
574
|
+
if (retryCount < (options?.maxRetries ?? 3)) return ensureInitialized(operationOptions, retryCount + 1);
|
|
575
|
+
throw error;
|
|
576
|
+
}
|
|
577
|
+
}, {
|
|
578
|
+
...operationOptions,
|
|
579
|
+
taskGroupId: v7()
|
|
580
|
+
});
|
|
581
|
+
};
|
|
582
|
+
return {
|
|
583
|
+
ensureInitialized,
|
|
584
|
+
reset: () => {
|
|
585
|
+
initPromise = null;
|
|
586
|
+
},
|
|
587
|
+
stop: (options) => taskProcessor.stop(options)
|
|
588
|
+
};
|
|
589
|
+
};
|
|
590
|
+
|
|
178
591
|
//#endregion
|
|
179
592
|
//#region src/core/execute/execute.ts
|
|
180
593
|
const mapSQLQueryResult = (result, mapping) => {
|
|
@@ -199,26 +612,63 @@ var BatchCommandNoChangesError = class BatchCommandNoChangesError extends DumboE
|
|
|
199
612
|
}
|
|
200
613
|
};
|
|
201
614
|
const sqlExecutor = (sqlExecutor, options) => ({
|
|
202
|
-
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions),
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
615
|
+
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions), {
|
|
616
|
+
...options,
|
|
617
|
+
...queryOptions
|
|
618
|
+
}),
|
|
619
|
+
batchQuery: (sqls, queryOptions) => executeInNewDbClient((client) => sqlExecutor.batchQuery(client, sqls, queryOptions), {
|
|
620
|
+
...options,
|
|
621
|
+
...queryOptions
|
|
622
|
+
}),
|
|
623
|
+
command: (sql, commandOptions) => executeInNewDbClient((client) => sqlExecutor.command(client, sql, commandOptions), {
|
|
624
|
+
...options,
|
|
625
|
+
...commandOptions
|
|
626
|
+
}),
|
|
627
|
+
batchCommand: (sqls, commandOptions) => executeInNewDbClient((client) => sqlExecutor.batchCommand(client, sqls, commandOptions), {
|
|
628
|
+
...options,
|
|
629
|
+
...commandOptions
|
|
630
|
+
})
|
|
206
631
|
});
|
|
207
632
|
const sqlExecutorInNewConnection = (options) => ({
|
|
208
|
-
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
633
|
+
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
634
|
+
...options,
|
|
635
|
+
...queryOptions
|
|
636
|
+
}),
|
|
637
|
+
batchQuery: (sqls, queryOptions) => executeInNewConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
638
|
+
...options,
|
|
639
|
+
...queryOptions
|
|
640
|
+
}),
|
|
641
|
+
command: (sql, commandOptions) => executeInNewConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
642
|
+
...options,
|
|
643
|
+
...commandOptions
|
|
644
|
+
}),
|
|
645
|
+
batchCommand: (sqls, commandOptions) => executeInNewConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
646
|
+
...options,
|
|
647
|
+
...commandOptions
|
|
648
|
+
})
|
|
212
649
|
});
|
|
213
650
|
const sqlExecutorInAmbientConnection = (options) => ({
|
|
214
|
-
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
651
|
+
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
652
|
+
...options,
|
|
653
|
+
...queryOptions
|
|
654
|
+
}),
|
|
655
|
+
batchQuery: (sqls, queryOptions) => executeInAmbientConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
656
|
+
...options,
|
|
657
|
+
...queryOptions
|
|
658
|
+
}),
|
|
659
|
+
command: (sql, commandOptions) => executeInAmbientConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
660
|
+
...options,
|
|
661
|
+
...commandOptions
|
|
662
|
+
}),
|
|
663
|
+
batchCommand: (sqls, commandOptions) => executeInAmbientConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
664
|
+
...options,
|
|
665
|
+
...commandOptions
|
|
666
|
+
})
|
|
218
667
|
});
|
|
219
668
|
const executeInNewDbClient = async (handle, options) => {
|
|
669
|
+
Abort.throwIfAborted(options);
|
|
220
670
|
const { connect, close } = options;
|
|
221
|
-
const client = await connect();
|
|
671
|
+
const client = await connect({ abort: Abort.from(options) });
|
|
222
672
|
try {
|
|
223
673
|
return await handle(client);
|
|
224
674
|
} catch (error) {
|
|
@@ -227,7 +677,8 @@ const executeInNewDbClient = async (handle, options) => {
|
|
|
227
677
|
}
|
|
228
678
|
};
|
|
229
679
|
const executeInNewConnection = async (handle, options) => {
|
|
230
|
-
|
|
680
|
+
Abort.throwIfAborted(options);
|
|
681
|
+
const connection = await options.connection({ abort: Abort.from(options) });
|
|
231
682
|
try {
|
|
232
683
|
return await handle(connection);
|
|
233
684
|
} finally {
|
|
@@ -235,10 +686,8 @@ const executeInNewConnection = async (handle, options) => {
|
|
|
235
686
|
}
|
|
236
687
|
};
|
|
237
688
|
const executeInAmbientConnection = async (handle, options) => {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return await handle(connection);
|
|
241
|
-
} finally {}
|
|
689
|
+
Abort.throwIfAborted(options);
|
|
690
|
+
return handle(await options.connection({ abort: Abort.from(options) }));
|
|
242
691
|
};
|
|
243
692
|
|
|
244
693
|
//#endregion
|
|
@@ -306,10 +755,10 @@ const toTransactionResult = (transactionResult) => transactionResult !== void 0
|
|
|
306
755
|
success: true,
|
|
307
756
|
result: transactionResult
|
|
308
757
|
};
|
|
309
|
-
const executeInTransaction = async (transaction, handle) => {
|
|
758
|
+
const executeInTransaction = async (transaction, handle, context = { abort: Abort.never }) => {
|
|
310
759
|
await transaction.begin();
|
|
311
760
|
try {
|
|
312
|
-
const { success, result } = toTransactionResult(await handle(transaction));
|
|
761
|
+
const { success, result } = toTransactionResult(await handle(transaction, context));
|
|
313
762
|
if (success) await transaction.commit();
|
|
314
763
|
else await transaction.rollback();
|
|
315
764
|
return result;
|
|
@@ -318,18 +767,32 @@ const executeInTransaction = async (transaction, handle) => {
|
|
|
318
767
|
throw e;
|
|
319
768
|
}
|
|
320
769
|
};
|
|
770
|
+
const executeInNestedTransaction = async (transaction, handle, options, context) => {
|
|
771
|
+
Abort.throwIfAborted(options);
|
|
772
|
+
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.");
|
|
773
|
+
return executeInTransaction(transaction, handle, context);
|
|
774
|
+
};
|
|
321
775
|
const transactionFactoryWithDbClient = (connect, initTransaction) => {
|
|
322
776
|
let currentTransaction = void 0;
|
|
323
|
-
const getOrInitCurrentTransaction = (options) =>
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
777
|
+
const getOrInitCurrentTransaction = (options) => {
|
|
778
|
+
Abort.throwIfAborted(options);
|
|
779
|
+
if (currentTransaction) return currentTransaction;
|
|
780
|
+
currentTransaction = initTransaction(connect({ abort: Abort.from(options) }), {
|
|
781
|
+
close: () => {
|
|
782
|
+
currentTransaction = void 0;
|
|
783
|
+
return Promise.resolve();
|
|
784
|
+
},
|
|
785
|
+
...options ?? {}
|
|
786
|
+
});
|
|
787
|
+
return currentTransaction;
|
|
788
|
+
};
|
|
330
789
|
return {
|
|
331
790
|
transaction: getOrInitCurrentTransaction,
|
|
332
|
-
withTransaction: (handle, options) =>
|
|
791
|
+
withTransaction: (handle, options) => {
|
|
792
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
793
|
+
if (abortRejection) return abortRejection;
|
|
794
|
+
return executeInTransaction(getOrInitCurrentTransaction(options), handle, { abort: Abort.from(options) });
|
|
795
|
+
}
|
|
333
796
|
};
|
|
334
797
|
};
|
|
335
798
|
const wrapInConnectionClosure = async (connection, handle) => {
|
|
@@ -341,7 +804,8 @@ const wrapInConnectionClosure = async (connection, handle) => {
|
|
|
341
804
|
};
|
|
342
805
|
const transactionFactoryWithNewConnection = (connect) => ({
|
|
343
806
|
transaction: (options) => {
|
|
344
|
-
|
|
807
|
+
Abort.throwIfAborted(options);
|
|
808
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
345
809
|
const transaction = connection.transaction(options);
|
|
346
810
|
return {
|
|
347
811
|
...transaction,
|
|
@@ -350,7 +814,9 @@ const transactionFactoryWithNewConnection = (connect) => ({
|
|
|
350
814
|
};
|
|
351
815
|
},
|
|
352
816
|
withTransaction: (handle, options) => {
|
|
353
|
-
const
|
|
817
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
818
|
+
if (abortRejection) return abortRejection;
|
|
819
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
354
820
|
const withTx = connection.withTransaction;
|
|
355
821
|
return wrapInConnectionClosure(connection, () => withTx(handle, options));
|
|
356
822
|
}
|
|
@@ -359,19 +825,21 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
359
825
|
close ??= () => Promise.resolve();
|
|
360
826
|
return {
|
|
361
827
|
transaction: (options) => {
|
|
828
|
+
Abort.throwIfAborted(options);
|
|
362
829
|
let conn = null;
|
|
363
830
|
let innerTx = null;
|
|
364
831
|
let connectingPromise = null;
|
|
365
832
|
const ensureConnection = async () => {
|
|
366
833
|
if (conn) return innerTx;
|
|
367
834
|
if (!connectingPromise) connectingPromise = (async () => {
|
|
368
|
-
|
|
835
|
+
Abort.throwIfAborted(options);
|
|
836
|
+
conn = await connect({ abort: Abort.from(options) });
|
|
369
837
|
innerTx = conn.transaction(options);
|
|
370
838
|
})();
|
|
371
839
|
await connectingPromise;
|
|
372
840
|
return innerTx;
|
|
373
841
|
};
|
|
374
|
-
|
|
842
|
+
const tx = {
|
|
375
843
|
driverType,
|
|
376
844
|
get connection() {
|
|
377
845
|
if (!conn) throw new Error("Transaction not started - call begin() first");
|
|
@@ -413,11 +881,14 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
413
881
|
if (conn) await close(conn);
|
|
414
882
|
}
|
|
415
883
|
},
|
|
416
|
-
|
|
884
|
+
withTransaction: (handle, options) => executeInNestedTransaction(tx, handle, options, { abort: Abort.from(options) }),
|
|
885
|
+
_transactionOptions: options ?? {}
|
|
417
886
|
};
|
|
887
|
+
return tx;
|
|
418
888
|
},
|
|
419
889
|
withTransaction: async (handle, options) => {
|
|
420
|
-
|
|
890
|
+
Abort.throwIfAborted(options);
|
|
891
|
+
const conn = await connect({ abort: Abort.from(options) });
|
|
421
892
|
try {
|
|
422
893
|
const withTx = conn.withTransaction;
|
|
423
894
|
return await withTx(handle, options);
|
|
@@ -434,7 +905,10 @@ const createAmbientConnection = (options) => {
|
|
|
434
905
|
const { driverType, client, executor, initTransaction, serializer } = options;
|
|
435
906
|
const clientPromise = Promise.resolve(client);
|
|
436
907
|
const closePromise = Promise.resolve();
|
|
437
|
-
const open = () =>
|
|
908
|
+
const open = (context) => {
|
|
909
|
+
Abort.throwIfAborted(context);
|
|
910
|
+
return clientPromise;
|
|
911
|
+
};
|
|
438
912
|
const close = () => closePromise;
|
|
439
913
|
const typedConnection = {
|
|
440
914
|
driverType,
|
|
@@ -450,9 +924,10 @@ const createConnection = (options) => {
|
|
|
450
924
|
const { driverType, connect, close, initTransaction, executor, serializer } = options;
|
|
451
925
|
let client = null;
|
|
452
926
|
let connectPromise = null;
|
|
453
|
-
const getClient = async () => {
|
|
927
|
+
const getClient = async (context) => {
|
|
928
|
+
Abort.throwIfAborted(context);
|
|
454
929
|
if (client) return client;
|
|
455
|
-
if (!connectPromise) connectPromise = connect().then((c) => {
|
|
930
|
+
if (!connectPromise) connectPromise = connect(context ?? { abort: Abort.never }).then((c) => {
|
|
456
931
|
client = c;
|
|
457
932
|
return c;
|
|
458
933
|
});
|
|
@@ -469,209 +944,6 @@ const createConnection = (options) => {
|
|
|
469
944
|
return typedConnection;
|
|
470
945
|
};
|
|
471
946
|
|
|
472
|
-
//#endregion
|
|
473
|
-
//#region src/core/taskProcessing/taskProcessor.ts
|
|
474
|
-
var TaskProcessor = class {
|
|
475
|
-
queue = [];
|
|
476
|
-
isProcessing = false;
|
|
477
|
-
activeTasks = 0;
|
|
478
|
-
activeGroups = /* @__PURE__ */ new Set();
|
|
479
|
-
options;
|
|
480
|
-
stopped = false;
|
|
481
|
-
constructor(options) {
|
|
482
|
-
this.options = options;
|
|
483
|
-
}
|
|
484
|
-
enqueue(task, options) {
|
|
485
|
-
if (this.stopped) return Promise.reject(new DumboError("TaskProcessor has been stopped"));
|
|
486
|
-
if (this.queue.length >= this.options.maxQueueSize) return Promise.reject(new TransientDatabaseError("Too many pending connections. Please try again later."));
|
|
487
|
-
return this.schedule(task, options);
|
|
488
|
-
}
|
|
489
|
-
waitForEndOfProcessing() {
|
|
490
|
-
return this.schedule(({ ack }) => Promise.resolve(ack()));
|
|
491
|
-
}
|
|
492
|
-
async stop(options) {
|
|
493
|
-
if (this.stopped) return;
|
|
494
|
-
this.stopped = true;
|
|
495
|
-
this.queue.length = 0;
|
|
496
|
-
this.activeGroups.clear();
|
|
497
|
-
if (!options?.force) await this.waitForEndOfProcessing();
|
|
498
|
-
}
|
|
499
|
-
schedule(task, options) {
|
|
500
|
-
return promiseWithDeadline((resolve, reject) => {
|
|
501
|
-
const taskWithContext = () => {
|
|
502
|
-
return new Promise((resolveTask, failTask) => {
|
|
503
|
-
task({ ack: resolveTask }).then(resolve).catch((err) => {
|
|
504
|
-
failTask(err);
|
|
505
|
-
reject(err);
|
|
506
|
-
});
|
|
507
|
-
});
|
|
508
|
-
};
|
|
509
|
-
this.queue.push({
|
|
510
|
-
task: taskWithContext,
|
|
511
|
-
options
|
|
512
|
-
});
|
|
513
|
-
if (!this.isProcessing) this.ensureProcessing();
|
|
514
|
-
}, { deadline: this.options.maxTaskIdleTime });
|
|
515
|
-
}
|
|
516
|
-
ensureProcessing() {
|
|
517
|
-
if (this.isProcessing) return;
|
|
518
|
-
this.isProcessing = true;
|
|
519
|
-
this.processQueue();
|
|
520
|
-
}
|
|
521
|
-
processQueue() {
|
|
522
|
-
try {
|
|
523
|
-
while (this.activeTasks < this.options.maxActiveTasks && this.queue.length > 0) {
|
|
524
|
-
const item = this.takeFirstAvailableItem();
|
|
525
|
-
if (item === null) return;
|
|
526
|
-
const groupId = item.options?.taskGroupId;
|
|
527
|
-
if (groupId) this.activeGroups.add(groupId);
|
|
528
|
-
this.activeTasks++;
|
|
529
|
-
this.executeItem(item);
|
|
530
|
-
}
|
|
531
|
-
} catch (error) {
|
|
532
|
-
console.error(error);
|
|
533
|
-
throw error;
|
|
534
|
-
} finally {
|
|
535
|
-
this.isProcessing = false;
|
|
536
|
-
if (this.hasItemsToProcess() && this.activeTasks < this.options.maxActiveTasks) this.ensureProcessing();
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
async executeItem({ task, options }) {
|
|
540
|
-
try {
|
|
541
|
-
await task();
|
|
542
|
-
} finally {
|
|
543
|
-
this.activeTasks--;
|
|
544
|
-
if (options && options.taskGroupId) this.activeGroups.delete(options.taskGroupId);
|
|
545
|
-
this.ensureProcessing();
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
takeFirstAvailableItem = () => {
|
|
549
|
-
const taskIndex = this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId));
|
|
550
|
-
if (taskIndex === -1) return null;
|
|
551
|
-
const [item] = this.queue.splice(taskIndex, 1);
|
|
552
|
-
return item ?? null;
|
|
553
|
-
};
|
|
554
|
-
hasItemsToProcess = () => this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId)) !== -1;
|
|
555
|
-
};
|
|
556
|
-
const DEFAULT_PROMISE_DEADLINE = 2147483647;
|
|
557
|
-
const promiseWithDeadline = (executor, options) => {
|
|
558
|
-
return new Promise((resolve, reject) => {
|
|
559
|
-
let taskStarted = false;
|
|
560
|
-
let timeoutId = null;
|
|
561
|
-
const deadline = options.deadline ?? DEFAULT_PROMISE_DEADLINE;
|
|
562
|
-
timeoutId = setTimeout(() => {
|
|
563
|
-
if (!taskStarted) reject(/* @__PURE__ */ new Error("Task was not started within the maximum waiting time"));
|
|
564
|
-
}, deadline);
|
|
565
|
-
timeoutId.unref();
|
|
566
|
-
executor((value) => {
|
|
567
|
-
taskStarted = true;
|
|
568
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
569
|
-
timeoutId = null;
|
|
570
|
-
resolve(value);
|
|
571
|
-
}, (reason) => {
|
|
572
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
573
|
-
timeoutId = null;
|
|
574
|
-
reject(reason);
|
|
575
|
-
});
|
|
576
|
-
});
|
|
577
|
-
};
|
|
578
|
-
|
|
579
|
-
//#endregion
|
|
580
|
-
//#region src/core/taskProcessing/executionGuards.ts
|
|
581
|
-
const guardBoundedAccess = (getResource, options) => {
|
|
582
|
-
let isStopped = false;
|
|
583
|
-
const taskProcessor = new TaskProcessor({
|
|
584
|
-
maxActiveTasks: options.maxResources,
|
|
585
|
-
maxQueueSize: options.maxQueueSize ?? 1e3
|
|
586
|
-
});
|
|
587
|
-
const resourcePool = [];
|
|
588
|
-
const allResources = /* @__PURE__ */ new Set();
|
|
589
|
-
const ackCallbacks = /* @__PURE__ */ new Map();
|
|
590
|
-
const acquire = async () => taskProcessor.enqueue(async ({ ack }) => {
|
|
591
|
-
try {
|
|
592
|
-
let resource;
|
|
593
|
-
if (options.reuseResources) resource = resourcePool.pop();
|
|
594
|
-
if (!resource) {
|
|
595
|
-
resource = await getResource();
|
|
596
|
-
allResources.add(resource);
|
|
597
|
-
}
|
|
598
|
-
ackCallbacks.set(resource, ack);
|
|
599
|
-
return resource;
|
|
600
|
-
} catch (e) {
|
|
601
|
-
ack();
|
|
602
|
-
throw e;
|
|
603
|
-
}
|
|
604
|
-
});
|
|
605
|
-
const release = (resource) => {
|
|
606
|
-
const ack = ackCallbacks.get(resource);
|
|
607
|
-
if (ack) {
|
|
608
|
-
ackCallbacks.delete(resource);
|
|
609
|
-
if (options.reuseResources) resourcePool.push(resource);
|
|
610
|
-
ack();
|
|
611
|
-
}
|
|
612
|
-
};
|
|
613
|
-
const execute = async (operation) => {
|
|
614
|
-
const resource = await acquire();
|
|
615
|
-
try {
|
|
616
|
-
return await operation(resource);
|
|
617
|
-
} finally {
|
|
618
|
-
release(resource);
|
|
619
|
-
}
|
|
620
|
-
};
|
|
621
|
-
return {
|
|
622
|
-
acquire,
|
|
623
|
-
release,
|
|
624
|
-
execute,
|
|
625
|
-
waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
|
|
626
|
-
stop: async (stopOptions) => {
|
|
627
|
-
if (isStopped) return;
|
|
628
|
-
isStopped = true;
|
|
629
|
-
if (options?.closeResource) {
|
|
630
|
-
const resources = [...allResources];
|
|
631
|
-
allResources.clear();
|
|
632
|
-
resourcePool.length = 0;
|
|
633
|
-
await Promise.all(resources.map(async (resource) => await options.closeResource(resource)));
|
|
634
|
-
}
|
|
635
|
-
await taskProcessor.stop(stopOptions);
|
|
636
|
-
}
|
|
637
|
-
};
|
|
638
|
-
};
|
|
639
|
-
const guardInitializedOnce = (initialize, options) => {
|
|
640
|
-
let initPromise = null;
|
|
641
|
-
const taskProcessor = new TaskProcessor({
|
|
642
|
-
maxActiveTasks: 1,
|
|
643
|
-
maxQueueSize: options?.maxQueueSize ?? 1e3
|
|
644
|
-
});
|
|
645
|
-
const ensureInitialized = async (retryCount = 0) => {
|
|
646
|
-
if (initPromise !== null) return initPromise;
|
|
647
|
-
return taskProcessor.enqueue(async ({ ack }) => {
|
|
648
|
-
if (initPromise !== null) {
|
|
649
|
-
ack();
|
|
650
|
-
return initPromise;
|
|
651
|
-
}
|
|
652
|
-
try {
|
|
653
|
-
const promise = initialize();
|
|
654
|
-
initPromise = promise;
|
|
655
|
-
const result = await promise;
|
|
656
|
-
ack();
|
|
657
|
-
return result;
|
|
658
|
-
} catch (error) {
|
|
659
|
-
initPromise = null;
|
|
660
|
-
ack();
|
|
661
|
-
if (retryCount < (options?.maxRetries ?? 3)) return ensureInitialized(retryCount + 1);
|
|
662
|
-
throw error;
|
|
663
|
-
}
|
|
664
|
-
}, { taskGroupId: v7() });
|
|
665
|
-
};
|
|
666
|
-
return {
|
|
667
|
-
ensureInitialized,
|
|
668
|
-
reset: () => {
|
|
669
|
-
initPromise = null;
|
|
670
|
-
},
|
|
671
|
-
stop: (options) => taskProcessor.stop(options)
|
|
672
|
-
};
|
|
673
|
-
};
|
|
674
|
-
|
|
675
947
|
//#endregion
|
|
676
948
|
//#region src/core/connections/pool.ts
|
|
677
949
|
const wrapPooledConnection = (conn, onClose) => ({
|
|
@@ -685,7 +957,10 @@ const createAmbientConnectionPool = (options) => {
|
|
|
685
957
|
getConnection: () => connection,
|
|
686
958
|
execute: connection.execute,
|
|
687
959
|
transaction: (options) => connection.transaction(options),
|
|
688
|
-
withConnection: (handle,
|
|
960
|
+
withConnection: (handle, options) => {
|
|
961
|
+
Abort.throwIfAborted(options);
|
|
962
|
+
return handle(connection, { abort: Abort.from(options) });
|
|
963
|
+
},
|
|
689
964
|
withTransaction: (handle, options) => {
|
|
690
965
|
const withTx = connection.withTransaction;
|
|
691
966
|
return withTx(handle, options);
|
|
@@ -696,48 +971,45 @@ const createSingletonConnectionPool = (options) => {
|
|
|
696
971
|
const { driverType, getConnection } = options;
|
|
697
972
|
let connectionPromise = null;
|
|
698
973
|
let closed = false;
|
|
699
|
-
const
|
|
974
|
+
const operationGuard = guardConcurrentAccess();
|
|
700
975
|
const closedError = () => /* @__PURE__ */ new Error("Singleton connection pool has been closed");
|
|
701
|
-
const
|
|
976
|
+
const executeIfOpen = async (operation, operationOptions) => {
|
|
702
977
|
if (closed) throw closedError();
|
|
703
|
-
|
|
704
|
-
activeOperations.add(activeOperation);
|
|
705
|
-
try {
|
|
706
|
-
return await activeOperation;
|
|
707
|
-
} finally {
|
|
708
|
-
activeOperations.delete(activeOperation);
|
|
709
|
-
}
|
|
978
|
+
return operationGuard.execute(operation, operationOptions);
|
|
710
979
|
};
|
|
711
|
-
const getExistingOrNewConnection = () => {
|
|
712
|
-
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection());
|
|
980
|
+
const getExistingOrNewConnection = (context) => {
|
|
981
|
+
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection(context));
|
|
713
982
|
return connectionPromise;
|
|
714
983
|
};
|
|
715
984
|
const innerTransactionFactory = transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection);
|
|
716
985
|
return {
|
|
717
986
|
driverType,
|
|
718
|
-
connection: () =>
|
|
987
|
+
connection: (connectionOptions) => executeIfOpen((context) => getExistingOrNewConnection(context).then((conn) => wrapPooledConnection(conn, () => Promise.resolve())), connectionOptions),
|
|
719
988
|
execute: (() => {
|
|
720
989
|
const ambientExecutor = sqlExecutorInAmbientConnection({
|
|
721
990
|
driverType,
|
|
722
991
|
connection: getExistingOrNewConnection
|
|
723
992
|
});
|
|
724
993
|
return {
|
|
725
|
-
query: (sql, opts) =>
|
|
726
|
-
batchQuery: (sqls, opts) =>
|
|
727
|
-
command: (sql, opts) =>
|
|
728
|
-
batchCommand: (sqls, opts) =>
|
|
994
|
+
query: (sql, opts) => executeIfOpen(() => ambientExecutor.query(sql, opts), opts),
|
|
995
|
+
batchQuery: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchQuery(sqls, opts), opts),
|
|
996
|
+
command: (sql, opts) => executeIfOpen(() => ambientExecutor.command(sql, opts), opts),
|
|
997
|
+
batchCommand: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchCommand(sqls, opts), opts)
|
|
729
998
|
};
|
|
730
999
|
})(),
|
|
731
|
-
withConnection: (handle,
|
|
1000
|
+
withConnection: (handle, options) => executeIfOpen((context) => executeInAmbientConnection((connection) => handle(connection, context), {
|
|
1001
|
+
connection: getExistingOrNewConnection,
|
|
1002
|
+
...options
|
|
1003
|
+
}), options),
|
|
732
1004
|
transaction: (transactionOptions) => {
|
|
733
1005
|
if (closed) throw closedError();
|
|
734
1006
|
return innerTransactionFactory.transaction(transactionOptions);
|
|
735
1007
|
},
|
|
736
|
-
withTransaction: (handle, transactionOptions) =>
|
|
1008
|
+
withTransaction: (handle, transactionOptions) => executeIfOpen((context) => innerTransactionFactory.withTransaction((tx) => handle(tx, context), transactionOptions), transactionOptions),
|
|
737
1009
|
close: async (closeOptions) => {
|
|
738
1010
|
if (closed) return;
|
|
739
1011
|
closed = true;
|
|
740
|
-
|
|
1012
|
+
await operationGuard.stop(closeOptions);
|
|
741
1013
|
if (!connectionPromise) return;
|
|
742
1014
|
const connection = await connectionPromise;
|
|
743
1015
|
connectionPromise = null;
|
|
@@ -747,63 +1019,49 @@ const createSingletonConnectionPool = (options) => {
|
|
|
747
1019
|
};
|
|
748
1020
|
const createBoundedConnectionPool = (options) => {
|
|
749
1021
|
const { driverType, maxConnections } = options;
|
|
750
|
-
const
|
|
751
|
-
const getTrackedConnection = async () => {
|
|
752
|
-
const connection = await options.getConnection();
|
|
753
|
-
allConnections.add(connection);
|
|
754
|
-
return connection;
|
|
755
|
-
};
|
|
756
|
-
const guardMaxConnections = guardBoundedAccess(getTrackedConnection, {
|
|
1022
|
+
const guardMaxConnections = guardBoundedAccess(options.getConnection, {
|
|
757
1023
|
maxResources: maxConnections,
|
|
758
|
-
reuseResources: true
|
|
1024
|
+
reuseResources: true,
|
|
1025
|
+
closeResource: (connection) => connection.close()
|
|
759
1026
|
});
|
|
760
1027
|
let closed = false;
|
|
761
1028
|
const closedError = () => /* @__PURE__ */ new Error("Bounded connection pool has been closed");
|
|
762
1029
|
const ensureOpen = () => {
|
|
763
1030
|
if (closed) throw closedError();
|
|
764
1031
|
};
|
|
765
|
-
const
|
|
766
|
-
const connections = [...allConnections];
|
|
767
|
-
allConnections.clear();
|
|
768
|
-
await Promise.all(connections.map((conn) => conn.close()));
|
|
769
|
-
};
|
|
770
|
-
const executeWithPooling = async (operation) => {
|
|
1032
|
+
const executeWithPooledConnection = async (operation, operationOptions) => {
|
|
771
1033
|
ensureOpen();
|
|
772
|
-
|
|
773
|
-
try {
|
|
774
|
-
return await operation(conn);
|
|
775
|
-
} finally {
|
|
776
|
-
guardMaxConnections.release(conn);
|
|
777
|
-
}
|
|
1034
|
+
return guardMaxConnections.execute(operation, operationOptions);
|
|
778
1035
|
};
|
|
779
1036
|
return {
|
|
780
1037
|
driverType,
|
|
781
|
-
connection: async () => {
|
|
1038
|
+
connection: async (connectionOptions) => {
|
|
782
1039
|
ensureOpen();
|
|
783
|
-
const conn = await guardMaxConnections.acquire();
|
|
1040
|
+
const conn = await guardMaxConnections.acquire(connectionOptions);
|
|
784
1041
|
return wrapPooledConnection(conn, () => Promise.resolve(guardMaxConnections.release(conn)));
|
|
785
1042
|
},
|
|
786
1043
|
execute: {
|
|
787
|
-
query: (sql, opts) =>
|
|
788
|
-
batchQuery: (sqls, opts) =>
|
|
789
|
-
command: (sql, opts) =>
|
|
790
|
-
batchCommand: (sqls, opts) =>
|
|
1044
|
+
query: (sql, opts) => executeWithPooledConnection((c) => c.execute.query(sql, opts), opts),
|
|
1045
|
+
batchQuery: (sqls, opts) => executeWithPooledConnection((c) => c.execute.batchQuery(sqls, opts), opts),
|
|
1046
|
+
command: (sql, opts) => executeWithPooledConnection((c) => c.execute.command(sql, opts), opts),
|
|
1047
|
+
batchCommand: (sqls, opts) => executeWithPooledConnection((c) => c.execute.batchCommand(sqls, opts), opts)
|
|
791
1048
|
},
|
|
792
|
-
withConnection:
|
|
1049
|
+
withConnection: executeWithPooledConnection,
|
|
793
1050
|
transaction: (transactionOptions) => {
|
|
794
1051
|
ensureOpen();
|
|
795
|
-
return transactionFactoryWithAsyncAmbientConnection(driverType, guardMaxConnections.acquire
|
|
1052
|
+
return transactionFactoryWithAsyncAmbientConnection(driverType, (context) => guardMaxConnections.acquire({
|
|
1053
|
+
...transactionOptions,
|
|
1054
|
+
abort: context.abort
|
|
1055
|
+
}), guardMaxConnections.release).transaction(transactionOptions);
|
|
796
1056
|
},
|
|
797
|
-
withTransaction: (handle, transactionOptions) =>
|
|
1057
|
+
withTransaction: (handle, transactionOptions) => executeWithPooledConnection((conn, context) => {
|
|
798
1058
|
const withTx = conn.withTransaction;
|
|
799
|
-
return withTx(handle, transactionOptions);
|
|
800
|
-
}),
|
|
1059
|
+
return withTx((tx) => handle(tx, context), transactionOptions);
|
|
1060
|
+
}, transactionOptions),
|
|
801
1061
|
close: async (closeOptions) => {
|
|
802
1062
|
if (closed) return;
|
|
803
1063
|
closed = true;
|
|
804
|
-
|
|
805
|
-
await guardMaxConnections.stop({ force: true });
|
|
806
|
-
await closeAllConnections();
|
|
1064
|
+
await guardMaxConnections.stop(closeOptions);
|
|
807
1065
|
}
|
|
808
1066
|
};
|
|
809
1067
|
};
|
|
@@ -811,16 +1069,22 @@ const createAlwaysNewConnectionPool = (options) => {
|
|
|
811
1069
|
const { driverType, getConnection, connectionOptions } = options;
|
|
812
1070
|
return createConnectionPool({
|
|
813
1071
|
driverType,
|
|
814
|
-
getConnection: () => connectionOptions ? getConnection(connectionOptions) : getConnection()
|
|
1072
|
+
getConnection: (context) => connectionOptions ? getConnection(connectionOptions, context) : getConnection(context)
|
|
815
1073
|
});
|
|
816
1074
|
};
|
|
817
1075
|
const createConnectionPool = (pool) => {
|
|
818
1076
|
const { driverType, getConnection } = pool;
|
|
819
|
-
const connection = "connection" in pool ? pool.connection : () =>
|
|
1077
|
+
const connection = "connection" in pool ? pool.connection : (options) => {
|
|
1078
|
+
Abort.throwIfAborted(options);
|
|
1079
|
+
return Promise.resolve(getConnection({ abort: Abort.from(options) }));
|
|
1080
|
+
};
|
|
820
1081
|
return {
|
|
821
1082
|
driverType,
|
|
822
1083
|
connection,
|
|
823
|
-
withConnection: "withConnection" in pool ? pool.withConnection : (handle,
|
|
1084
|
+
withConnection: "withConnection" in pool ? pool.withConnection : (handle, options) => executeInNewConnection((connection) => handle(connection, { abort: Abort.from(options) }), {
|
|
1085
|
+
connection,
|
|
1086
|
+
...options
|
|
1087
|
+
}),
|
|
824
1088
|
close: "close" in pool ? pool.close : () => Promise.resolve(),
|
|
825
1089
|
execute: "execute" in pool ? pool.execute : sqlExecutorInNewConnection({
|
|
826
1090
|
driverType,
|
|
@@ -2160,9 +2424,9 @@ const sqliteSQLExecutor = (driverType, serializer, formatter, errorMapper) => ({
|
|
|
2160
2424
|
|
|
2161
2425
|
//#endregion
|
|
2162
2426
|
//#region src/storage/sqlite/core/transactions/index.ts
|
|
2163
|
-
const sqliteTransaction = (driverType, connection,
|
|
2164
|
-
|
|
2165
|
-
const
|
|
2427
|
+
const sqliteTransaction = (driverType, connection, defaultOptions, serializer, defaultTransactionMode) => (getClient, options) => {
|
|
2428
|
+
const defaultTransactionOptions = typeof defaultOptions === "boolean" ? { allowNestedTransactions: defaultOptions } : defaultOptions;
|
|
2429
|
+
const allowNestedTransactions = options?.allowNestedTransactions ?? defaultTransactionOptions.allowNestedTransactions ?? false;
|
|
2166
2430
|
const tx = databaseTransaction({
|
|
2167
2431
|
begin: async () => {
|
|
2168
2432
|
const client = await getClient;
|
|
@@ -2190,23 +2454,28 @@ const sqliteTransaction = (driverType, connection, allowNestedTransactions, seri
|
|
|
2190
2454
|
},
|
|
2191
2455
|
releaseSavepoint: async (level) => {
|
|
2192
2456
|
await (await getClient).command(SQL`RELEASE transaction${SQL.plain(level.toString())}`);
|
|
2457
|
+
},
|
|
2458
|
+
rollbackToSavepoint: async (level) => {
|
|
2459
|
+
await (await getClient).command(SQL`ROLLBACK TO transaction${SQL.plain(level.toString())}`);
|
|
2193
2460
|
}
|
|
2194
2461
|
}, {
|
|
2195
2462
|
allowNestedTransactions,
|
|
2196
|
-
useSavepoints
|
|
2463
|
+
useSavepoints: options?.useSavepoints ?? defaultTransactionOptions.useSavepoints ?? false
|
|
2197
2464
|
});
|
|
2198
|
-
|
|
2465
|
+
const transaction = {
|
|
2199
2466
|
connection: connection(),
|
|
2200
2467
|
driverType,
|
|
2201
2468
|
begin: tx.begin,
|
|
2202
2469
|
commit: tx.commit,
|
|
2203
2470
|
rollback: tx.rollback,
|
|
2204
2471
|
execute: sqlExecutor(sqliteSQLExecutor(driverType, serializer), { connect: () => getClient }),
|
|
2472
|
+
withTransaction: (handle, options) => executeInNestedTransaction(transaction, handle, options),
|
|
2205
2473
|
_transactionOptions: {
|
|
2206
2474
|
...options,
|
|
2207
2475
|
allowNestedTransactions
|
|
2208
2476
|
}
|
|
2209
2477
|
};
|
|
2478
|
+
return transaction;
|
|
2210
2479
|
};
|
|
2211
2480
|
|
|
2212
2481
|
//#endregion
|
|
@@ -2251,7 +2520,7 @@ const sqliteAmbientClientConnection = (options) => {
|
|
|
2251
2520
|
return createAmbientConnection({
|
|
2252
2521
|
driverType,
|
|
2253
2522
|
client,
|
|
2254
|
-
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, allowNestedTransactions ?? false, serializer, defaultTransactionMode)),
|
|
2523
|
+
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, { allowNestedTransactions: allowNestedTransactions ?? false }, serializer, defaultTransactionMode)),
|
|
2255
2524
|
executor: ({ serializer }) => sqliteSQLExecutor(driverType, serializer, void 0, errorMapper),
|
|
2256
2525
|
serializer
|
|
2257
2526
|
});
|
|
@@ -2276,7 +2545,7 @@ const sqliteClientConnection = (options) => {
|
|
|
2276
2545
|
if (client && "close" in client && typeof client.close === "function") await client.close();
|
|
2277
2546
|
else if (client && "release" in client && typeof client.release === "function") client.release();
|
|
2278
2547
|
},
|
|
2279
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2548
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
2280
2549
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
2281
2550
|
serializer
|
|
2282
2551
|
});
|
|
@@ -2298,7 +2567,7 @@ const sqlitePoolClientConnection = (options) => {
|
|
|
2298
2567
|
driverType: options.driverType,
|
|
2299
2568
|
connect,
|
|
2300
2569
|
close: () => client !== null ? Promise.resolve(client.release()) : Promise.resolve(),
|
|
2301
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2570
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
2302
2571
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
2303
2572
|
serializer
|
|
2304
2573
|
});
|
|
@@ -2672,43 +2941,67 @@ const sqlite3Connection = (options) => sqliteConnection({
|
|
|
2672
2941
|
//#endregion
|
|
2673
2942
|
//#region src/storage/sqlite/sqlite3/pool/singletonPool.ts
|
|
2674
2943
|
const DEFAULT_SQLITE_MAX_TASK_IDLE_TIME_MS = 3e4;
|
|
2944
|
+
const createSQLiteTransactionContext = () => {
|
|
2945
|
+
if (typeof process === "undefined") return void 0;
|
|
2946
|
+
const asyncHooks = process.getBuiltinModule?.("node:async_hooks");
|
|
2947
|
+
if (!asyncHooks) return void 0;
|
|
2948
|
+
const context = new asyncHooks.AsyncLocalStorage();
|
|
2949
|
+
return {
|
|
2950
|
+
current: () => context.getStore(),
|
|
2951
|
+
run: (active, handle) => context.run(active, handle)
|
|
2952
|
+
};
|
|
2953
|
+
};
|
|
2675
2954
|
const sqlite3SingletonPool = (options) => {
|
|
2676
|
-
const maxTaskIdleTime = options.maxTaskIdleTime ?? 3e4;
|
|
2677
2955
|
const inner = createSingletonConnectionPool({
|
|
2678
2956
|
driverType: options.driverType,
|
|
2679
2957
|
getConnection: options.getConnection,
|
|
2680
2958
|
...options.closeConnection ? { closeConnection: options.closeConnection } : {}
|
|
2681
2959
|
});
|
|
2682
|
-
const
|
|
2683
|
-
maxActiveTasks: 1,
|
|
2960
|
+
const writerGuard = guardExclusiveAccess({
|
|
2684
2961
|
maxQueueSize: options.maxQueueSize ?? 1e3,
|
|
2685
|
-
maxTaskIdleTime
|
|
2962
|
+
maxTaskIdleTime: options.maxTaskIdleTime ?? 3e4
|
|
2686
2963
|
});
|
|
2687
|
-
const
|
|
2688
|
-
const
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2964
|
+
const transactionContext = options.transactionContext ?? createSQLiteTransactionContext();
|
|
2965
|
+
const activeConnection = () => transactionContext?.current()?.connection;
|
|
2966
|
+
const runInTransactionContext = (connection, operation) => transactionContext ? transactionContext.run({ connection }, operation) : operation();
|
|
2967
|
+
const transactionAwareConnection = (connection) => transactionContext ? {
|
|
2968
|
+
...connection,
|
|
2969
|
+
withTransaction: (handle, transactionOptions) => runInTransactionContext(connection, () => connection.withTransaction(handle, transactionOptions))
|
|
2970
|
+
} : connection;
|
|
2971
|
+
const runConnectionTransaction = (connection, handle, context, transactionOptions) => {
|
|
2972
|
+
const withTransaction = connection.withTransaction;
|
|
2973
|
+
return withTransaction((transaction) => handle(transaction, context), transactionOptions);
|
|
2974
|
+
};
|
|
2975
|
+
const runOnWriterConnection = (handle, options) => {
|
|
2976
|
+
const connection = activeConnection();
|
|
2977
|
+
if (connection) return inner.withConnection((_connection, context) => handle(connection, context), options);
|
|
2978
|
+
return writerGuard.execute((context) => inner.withConnection((connection) => handle(connection, context), options), options);
|
|
2697
2979
|
};
|
|
2980
|
+
const withWriterConnection = (handle, options) => runOnWriterConnection((connection, context) => handle(transactionAwareConnection(connection), context), options);
|
|
2698
2981
|
return {
|
|
2699
2982
|
driverType: inner.driverType,
|
|
2700
2983
|
connection: inner.connection.bind(inner),
|
|
2701
2984
|
transaction: inner.transaction.bind(inner),
|
|
2702
|
-
withConnection: (handle, connectionOptions) =>
|
|
2703
|
-
withTransaction: (handle, transactionOptions) =>
|
|
2985
|
+
withConnection: (handle, connectionOptions) => activeConnection() || !connectionOptions?.readonly ? withWriterConnection(handle, connectionOptions) : inner.withConnection(handle, connectionOptions),
|
|
2986
|
+
withTransaction: (handle, transactionOptions) => {
|
|
2987
|
+
const connection = activeConnection();
|
|
2988
|
+
if (connection) return inner.withConnection((_connection, context) => runConnectionTransaction(connection, handle, context, transactionOptions), transactionOptions);
|
|
2989
|
+
return runOnWriterConnection((connection, context) => runInTransactionContext(connection, () => runConnectionTransaction(connection, handle, context, transactionOptions)), transactionOptions);
|
|
2990
|
+
},
|
|
2704
2991
|
execute: {
|
|
2705
|
-
query: (sql, queryOptions) =>
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2992
|
+
query: (sql, queryOptions) => {
|
|
2993
|
+
const connection = activeConnection();
|
|
2994
|
+
return connection ? connection.execute.query(sql, queryOptions) : inner.execute.query(sql, queryOptions);
|
|
2995
|
+
},
|
|
2996
|
+
batchQuery: (sqls, queryOptions) => {
|
|
2997
|
+
const connection = activeConnection();
|
|
2998
|
+
return connection ? connection.execute.batchQuery(sqls, queryOptions) : inner.execute.batchQuery(sqls, queryOptions);
|
|
2999
|
+
},
|
|
3000
|
+
command: (sql, commandOptions) => runOnWriterConnection((connection) => connection.execute.command(sql, commandOptions), commandOptions),
|
|
3001
|
+
batchCommand: (sqls, commandOptions) => runOnWriterConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), commandOptions)
|
|
2709
3002
|
},
|
|
2710
3003
|
close: async (closeOptions) => {
|
|
2711
|
-
await
|
|
3004
|
+
await writerGuard.stop(closeOptions);
|
|
2712
3005
|
await inner.close(closeOptions);
|
|
2713
3006
|
}
|
|
2714
3007
|
};
|
|
@@ -2719,15 +3012,16 @@ const sqlite3SingletonPool = (options) => {
|
|
|
2719
3012
|
const sqliteDualConnectionPool = (options) => {
|
|
2720
3013
|
const { sqliteConnectionFactory, connectionOptions } = options;
|
|
2721
3014
|
const readerPoolSize = options.readerPoolSize ?? Math.max(4, cpus().length);
|
|
3015
|
+
const transactionContext = createSQLiteTransactionContext();
|
|
2722
3016
|
let databaseInitPromise = null;
|
|
2723
|
-
const guardSingleConnection = guardInitializedOnce(async () => {
|
|
3017
|
+
const guardSingleConnection = guardInitializedOnce(async (context) => {
|
|
2724
3018
|
if (databaseInitPromise !== null) return databaseInitPromise;
|
|
2725
3019
|
const initConnection = sqliteConnectionFactory({
|
|
2726
3020
|
...connectionOptions,
|
|
2727
3021
|
skipDatabasePragmas: false,
|
|
2728
3022
|
readonly: false
|
|
2729
3023
|
});
|
|
2730
|
-
const initPromise = initConnection.open();
|
|
3024
|
+
const initPromise = initConnection.open(context);
|
|
2731
3025
|
databaseInitPromise = initPromise;
|
|
2732
3026
|
try {
|
|
2733
3027
|
await initPromise;
|
|
@@ -2738,45 +3032,47 @@ const sqliteDualConnectionPool = (options) => {
|
|
|
2738
3032
|
throw mapSqliteError(error);
|
|
2739
3033
|
}
|
|
2740
3034
|
});
|
|
2741
|
-
const ensureDatabaseInitialized = async () => {
|
|
3035
|
+
const ensureDatabaseInitialized = async (context) => {
|
|
2742
3036
|
if (databaseInitPromise !== null) return databaseInitPromise;
|
|
2743
|
-
return guardSingleConnection.ensureInitialized();
|
|
3037
|
+
return guardSingleConnection.ensureInitialized({ abort: context.abort });
|
|
2744
3038
|
};
|
|
2745
|
-
const wrappedConnectionFactory = async (readonly, connectionOptions) => {
|
|
2746
|
-
await ensureDatabaseInitialized();
|
|
3039
|
+
const wrappedConnectionFactory = async (readonly, connectionOptions, context) => {
|
|
3040
|
+
await ensureDatabaseInitialized(context);
|
|
2747
3041
|
const connection = sqliteConnectionFactory({
|
|
2748
3042
|
...connectionOptions,
|
|
2749
3043
|
skipDatabasePragmas: true,
|
|
2750
3044
|
readonly
|
|
2751
3045
|
});
|
|
2752
|
-
await connection.open();
|
|
3046
|
+
await connection.open(context);
|
|
2753
3047
|
return connection;
|
|
2754
3048
|
};
|
|
2755
3049
|
const writerPool = sqlite3SingletonPool({
|
|
2756
3050
|
driverType: options.driverType,
|
|
2757
|
-
getConnection: () => wrappedConnectionFactory(false, connectionOptions),
|
|
3051
|
+
getConnection: (context) => wrappedConnectionFactory(false, connectionOptions, context),
|
|
3052
|
+
...transactionContext ? { transactionContext } : {},
|
|
2758
3053
|
...options.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: options.maxTaskIdleTime } : {}
|
|
2759
3054
|
});
|
|
2760
3055
|
const readerPool = createBoundedConnectionPool({
|
|
2761
3056
|
driverType: options.driverType,
|
|
2762
|
-
getConnection: () => wrappedConnectionFactory(true, connectionOptions),
|
|
3057
|
+
getConnection: (context) => wrappedConnectionFactory(true, connectionOptions, context),
|
|
2763
3058
|
maxConnections: readerPoolSize
|
|
2764
3059
|
});
|
|
3060
|
+
const hasActiveTransaction = () => !!transactionContext?.current();
|
|
2765
3061
|
return {
|
|
2766
3062
|
driverType: options.driverType,
|
|
2767
|
-
connection: (connectionOptions) => connectionOptions?.readonly ? readerPool.connection(connectionOptions) : writerPool.connection(connectionOptions),
|
|
3063
|
+
connection: (connectionOptions) => connectionOptions?.readonly && !hasActiveTransaction() ? readerPool.connection(connectionOptions) : writerPool.connection(connectionOptions),
|
|
2768
3064
|
execute: {
|
|
2769
|
-
query: (...args) => readerPool.execute.query(...args),
|
|
2770
|
-
batchQuery: (...args) => readerPool.execute.batchQuery(...args),
|
|
3065
|
+
query: (...args) => hasActiveTransaction() ? writerPool.execute.query(...args) : readerPool.execute.query(...args),
|
|
3066
|
+
batchQuery: (...args) => hasActiveTransaction() ? writerPool.execute.batchQuery(...args) : readerPool.execute.batchQuery(...args),
|
|
2771
3067
|
command: (...args) => writerPool.execute.command(...args),
|
|
2772
3068
|
batchCommand: (...args) => writerPool.execute.batchCommand(...args)
|
|
2773
3069
|
},
|
|
2774
|
-
withConnection: (handle, connectionOptions) => connectionOptions?.readonly ? readerPool.withConnection(handle, connectionOptions) : writerPool.withConnection(handle, connectionOptions),
|
|
3070
|
+
withConnection: (handle, connectionOptions) => connectionOptions?.readonly && !hasActiveTransaction() ? readerPool.withConnection(handle, connectionOptions) : writerPool.withConnection(handle, connectionOptions),
|
|
2775
3071
|
transaction: writerPool.transaction,
|
|
2776
3072
|
withTransaction: writerPool.withTransaction,
|
|
2777
|
-
close: async () => {
|
|
2778
|
-
await guardSingleConnection.stop();
|
|
2779
|
-
await Promise.all([writerPool.close(), readerPool.close()]);
|
|
3073
|
+
close: async (closeOptions) => {
|
|
3074
|
+
await guardSingleConnection.stop(closeOptions);
|
|
3075
|
+
await Promise.all([writerPool.close(closeOptions), readerPool.close(closeOptions)]);
|
|
2780
3076
|
}
|
|
2781
3077
|
};
|
|
2782
3078
|
};
|