@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/sqlite.js
CHANGED
|
@@ -172,6 +172,294 @@ var InvalidOperationError = class InvalidOperationError extends DumboError {
|
|
|
172
172
|
}
|
|
173
173
|
};
|
|
174
174
|
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/core/taskProcessing/abort.ts
|
|
177
|
+
const never = { signal: new AbortController().signal };
|
|
178
|
+
const from = (options) => options?.abort ?? never;
|
|
179
|
+
const getSignal = (abort) => "signal" in abort ? abort.signal : abort;
|
|
180
|
+
const reason = (abort) => {
|
|
181
|
+
const signal = getSignal(abort);
|
|
182
|
+
return signal.reason instanceof Error ? signal.reason : new DumboError(typeof signal.reason === "string" ? signal.reason : "Operation aborted");
|
|
183
|
+
};
|
|
184
|
+
const scope = (parent, onAbort) => {
|
|
185
|
+
const controller = new AbortController();
|
|
186
|
+
return {
|
|
187
|
+
abort: (reason) => controller.abort(reason),
|
|
188
|
+
dispose: onAbortSignal(parent, (reason) => {
|
|
189
|
+
controller.abort(reason);
|
|
190
|
+
onAbort?.(reason);
|
|
191
|
+
}),
|
|
192
|
+
signal: controller.signal
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
const execute = (operation, options) => {
|
|
196
|
+
const abort = options?.abort;
|
|
197
|
+
if (!abort) return operation();
|
|
198
|
+
const signal = abort.signal;
|
|
199
|
+
if (signal.aborted) return Promise.reject(reason(abort));
|
|
200
|
+
return new Promise((resolve, reject) => {
|
|
201
|
+
let finished = false;
|
|
202
|
+
const rejectOnAbort = () => {
|
|
203
|
+
if (finished) return;
|
|
204
|
+
finished = true;
|
|
205
|
+
reject(reason(abort));
|
|
206
|
+
};
|
|
207
|
+
signal.addEventListener("abort", rejectOnAbort, { once: true });
|
|
208
|
+
let operationPromise;
|
|
209
|
+
try {
|
|
210
|
+
operationPromise = operation();
|
|
211
|
+
} catch (error) {
|
|
212
|
+
finished = true;
|
|
213
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
214
|
+
reject(error);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
operationPromise.then((result) => {
|
|
218
|
+
if (finished) return;
|
|
219
|
+
finished = true;
|
|
220
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
221
|
+
resolve(result);
|
|
222
|
+
}).catch((error) => {
|
|
223
|
+
if (finished) return;
|
|
224
|
+
finished = true;
|
|
225
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
226
|
+
reject(error);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
const throwIfAborted = (options) => {
|
|
231
|
+
const abort = options?.abort;
|
|
232
|
+
if (abort?.signal.aborted) throw reason(abort);
|
|
233
|
+
};
|
|
234
|
+
const rejectIfAborted = (options) => {
|
|
235
|
+
const abort = options?.abort;
|
|
236
|
+
return abort?.signal.aborted ? Promise.reject(reason(abort)) : void 0;
|
|
237
|
+
};
|
|
238
|
+
const onAbortSignal = (abort, handle) => {
|
|
239
|
+
if (!abort) return () => {};
|
|
240
|
+
const signal = abort.signal;
|
|
241
|
+
if (signal.aborted) {
|
|
242
|
+
handle(reason(abort));
|
|
243
|
+
return () => {};
|
|
244
|
+
}
|
|
245
|
+
const abortListener = () => handle(reason(abort));
|
|
246
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
247
|
+
return () => signal.removeEventListener("abort", abortListener);
|
|
248
|
+
};
|
|
249
|
+
const Abort = {
|
|
250
|
+
execute,
|
|
251
|
+
from,
|
|
252
|
+
never,
|
|
253
|
+
onAbort: onAbortSignal,
|
|
254
|
+
reason,
|
|
255
|
+
rejectIfAborted,
|
|
256
|
+
scope,
|
|
257
|
+
throwIfAborted
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region src/core/taskProcessing/taskProcessor.ts
|
|
262
|
+
var TaskProcessor = class {
|
|
263
|
+
queue = [];
|
|
264
|
+
isProcessing = false;
|
|
265
|
+
activeTasks = 0;
|
|
266
|
+
activeGroups = /* @__PURE__ */ new Set();
|
|
267
|
+
options;
|
|
268
|
+
stopped = false;
|
|
269
|
+
idleWaiters = [];
|
|
270
|
+
activeTaskAbortCallbacks = /* @__PURE__ */ new Set();
|
|
271
|
+
constructor(options) {
|
|
272
|
+
this.options = options;
|
|
273
|
+
}
|
|
274
|
+
enqueue(task, options) {
|
|
275
|
+
if (options?.abort?.signal.aborted) return Promise.reject(Abort.reason(options.abort.signal));
|
|
276
|
+
if (this.stopped) return Promise.reject(new DumboError("TaskProcessor has been stopped"));
|
|
277
|
+
if (this.queue.length >= this.options.maxQueueSize) return Promise.reject(new TransientDatabaseError("Too many pending connections. Please try again later."));
|
|
278
|
+
return this.schedule(task, options);
|
|
279
|
+
}
|
|
280
|
+
waitForEndOfProcessing() {
|
|
281
|
+
if (this.activeTasks === 0 && this.queue.length === 0) return Promise.resolve();
|
|
282
|
+
return new Promise((resolve) => {
|
|
283
|
+
this.idleWaiters.push(resolve);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
async stop(options) {
|
|
287
|
+
if (this.stopped) return;
|
|
288
|
+
this.stopped = true;
|
|
289
|
+
const stoppedError = new DumboError("TaskProcessor has been stopped");
|
|
290
|
+
for (const item of this.queue.splice(0)) {
|
|
291
|
+
item.abort(stoppedError);
|
|
292
|
+
item.reject(stoppedError);
|
|
293
|
+
}
|
|
294
|
+
this.activeGroups.clear();
|
|
295
|
+
if (options?.force) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
|
|
296
|
+
if (options?.force) return;
|
|
297
|
+
if (options?.closeDeadline === void 0) {
|
|
298
|
+
await this.waitForEndOfProcessing();
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (!await waitForProcessingOrDeadline(this.waitForEndOfProcessing(), options.closeDeadline)) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
|
|
302
|
+
}
|
|
303
|
+
schedule(task, options) {
|
|
304
|
+
return new Promise((resolve, reject) => {
|
|
305
|
+
let didQueueTimeout = false;
|
|
306
|
+
let didAbortBeforeStart = false;
|
|
307
|
+
let didStart = false;
|
|
308
|
+
let queueWaitTimer = noopQueueWaitTimer;
|
|
309
|
+
const abortScope = Abort.scope(options?.abort, (reason) => {
|
|
310
|
+
queueWaitTimer.cancel();
|
|
311
|
+
didAbortBeforeStart = !didStart;
|
|
312
|
+
reject(reason);
|
|
313
|
+
});
|
|
314
|
+
queueWaitTimer = createQueueWaitTimer(this.options.maxTaskIdleTime, (reason) => {
|
|
315
|
+
didQueueTimeout = true;
|
|
316
|
+
abortScope.abort(reason);
|
|
317
|
+
abortScope.dispose();
|
|
318
|
+
reject(reason);
|
|
319
|
+
});
|
|
320
|
+
const taskWithContext = () => {
|
|
321
|
+
return new Promise((resolveTask) => {
|
|
322
|
+
if (didQueueTimeout || didAbortBeforeStart) {
|
|
323
|
+
resolveTask();
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
let taskPromise;
|
|
327
|
+
try {
|
|
328
|
+
taskPromise = task({
|
|
329
|
+
ack: resolveTask,
|
|
330
|
+
abort: abortScope
|
|
331
|
+
});
|
|
332
|
+
} catch (err) {
|
|
333
|
+
abortScope.dispose();
|
|
334
|
+
resolveTask();
|
|
335
|
+
reject(err);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
taskPromise.then((result) => {
|
|
339
|
+
abortScope.dispose();
|
|
340
|
+
resolve(result);
|
|
341
|
+
}).catch((err) => {
|
|
342
|
+
abortScope.dispose();
|
|
343
|
+
resolveTask();
|
|
344
|
+
reject(err);
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
};
|
|
348
|
+
this.queue.push({
|
|
349
|
+
task: taskWithContext,
|
|
350
|
+
options,
|
|
351
|
+
reject: (reason) => {
|
|
352
|
+
queueWaitTimer.cancel();
|
|
353
|
+
abortScope.dispose();
|
|
354
|
+
reject(reason);
|
|
355
|
+
},
|
|
356
|
+
markStarted: () => {
|
|
357
|
+
didStart = true;
|
|
358
|
+
queueWaitTimer.cancel();
|
|
359
|
+
},
|
|
360
|
+
abort: (reason) => {
|
|
361
|
+
abortScope.dispose();
|
|
362
|
+
abortScope.abort(reason);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
if (!this.isProcessing) this.ensureProcessing();
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
ensureProcessing() {
|
|
369
|
+
if (this.isProcessing) return;
|
|
370
|
+
this.isProcessing = true;
|
|
371
|
+
this.processQueue();
|
|
372
|
+
}
|
|
373
|
+
processQueue() {
|
|
374
|
+
try {
|
|
375
|
+
while (this.activeTasks < this.options.maxActiveTasks && this.queue.length > 0) {
|
|
376
|
+
const item = this.takeFirstAvailableItem();
|
|
377
|
+
if (item === null) return;
|
|
378
|
+
const groupId = item.options?.taskGroupId;
|
|
379
|
+
if (groupId) this.activeGroups.add(groupId);
|
|
380
|
+
this.activeTasks++;
|
|
381
|
+
this.executeItem(item);
|
|
382
|
+
}
|
|
383
|
+
} catch (error) {
|
|
384
|
+
console.error(error);
|
|
385
|
+
throw error;
|
|
386
|
+
} finally {
|
|
387
|
+
this.isProcessing = false;
|
|
388
|
+
if (this.hasItemsToProcess() && this.activeTasks < this.options.maxActiveTasks) this.ensureProcessing();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
async executeItem({ task, options, markStarted, abort }) {
|
|
392
|
+
markStarted();
|
|
393
|
+
this.activeTaskAbortCallbacks.add(abort);
|
|
394
|
+
try {
|
|
395
|
+
await task();
|
|
396
|
+
} finally {
|
|
397
|
+
this.activeTaskAbortCallbacks.delete(abort);
|
|
398
|
+
this.activeTasks--;
|
|
399
|
+
if (options && options.taskGroupId) this.activeGroups.delete(options.taskGroupId);
|
|
400
|
+
this.resolveIdleWaiters();
|
|
401
|
+
this.ensureProcessing();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
takeFirstAvailableItem = () => {
|
|
405
|
+
const taskIndex = this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId));
|
|
406
|
+
if (taskIndex === -1) return null;
|
|
407
|
+
const [item] = this.queue.splice(taskIndex, 1);
|
|
408
|
+
return item ?? null;
|
|
409
|
+
};
|
|
410
|
+
hasItemsToProcess = () => this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId)) !== -1;
|
|
411
|
+
resolveIdleWaiters = () => {
|
|
412
|
+
if (this.activeTasks > 0 || this.queue.length > 0) return;
|
|
413
|
+
const waiters = this.idleWaiters.splice(0);
|
|
414
|
+
for (const resolve of waiters) resolve();
|
|
415
|
+
};
|
|
416
|
+
};
|
|
417
|
+
const noopQueueWaitTimer = { cancel: () => {} };
|
|
418
|
+
const createQueueWaitTimer = (timeoutMs, reject) => {
|
|
419
|
+
if (timeoutMs === void 0) return noopQueueWaitTimer;
|
|
420
|
+
let timeoutId = setTimeout(() => {
|
|
421
|
+
reject(/* @__PURE__ */ new Error("Task was not started within the maximum waiting time"));
|
|
422
|
+
}, timeoutMs);
|
|
423
|
+
timeoutId.unref();
|
|
424
|
+
return { cancel: () => {
|
|
425
|
+
if (!timeoutId) return;
|
|
426
|
+
clearTimeout(timeoutId);
|
|
427
|
+
timeoutId = null;
|
|
428
|
+
} };
|
|
429
|
+
};
|
|
430
|
+
const waitForProcessingOrDeadline = async (processing, closeDeadline) => {
|
|
431
|
+
let timeoutId = null;
|
|
432
|
+
try {
|
|
433
|
+
return await Promise.race([processing.then(() => true), new Promise((resolve) => {
|
|
434
|
+
timeoutId = setTimeout(() => resolve(false), closeDeadline);
|
|
435
|
+
timeoutId.unref();
|
|
436
|
+
})]);
|
|
437
|
+
} finally {
|
|
438
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
//#endregion
|
|
443
|
+
//#region src/core/taskProcessing/executionGuards.ts
|
|
444
|
+
const guardConcurrentAccess = (options) => {
|
|
445
|
+
const taskProcessor = new TaskProcessor({
|
|
446
|
+
maxActiveTasks: options?.maxActiveTasks ?? Number.MAX_SAFE_INTEGER,
|
|
447
|
+
maxQueueSize: options?.maxQueueSize ?? Number.MAX_SAFE_INTEGER,
|
|
448
|
+
...options?.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: options.maxTaskIdleTime } : {}
|
|
449
|
+
});
|
|
450
|
+
return {
|
|
451
|
+
execute: (operation, options) => taskProcessor.enqueue(async (context) => {
|
|
452
|
+
try {
|
|
453
|
+
return await operation({ abort: context.abort });
|
|
454
|
+
} finally {
|
|
455
|
+
context.ack();
|
|
456
|
+
}
|
|
457
|
+
}, options),
|
|
458
|
+
waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
|
|
459
|
+
stop: (options) => taskProcessor.stop(options)
|
|
460
|
+
};
|
|
461
|
+
};
|
|
462
|
+
|
|
175
463
|
//#endregion
|
|
176
464
|
//#region src/core/execute/execute.ts
|
|
177
465
|
const mapSQLQueryResult = (result, mapping) => {
|
|
@@ -181,26 +469,63 @@ const mapSQLQueryResult = (result, mapping) => {
|
|
|
181
469
|
return mappedResult;
|
|
182
470
|
};
|
|
183
471
|
const sqlExecutor = (sqlExecutor, options) => ({
|
|
184
|
-
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions),
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
472
|
+
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions), {
|
|
473
|
+
...options,
|
|
474
|
+
...queryOptions
|
|
475
|
+
}),
|
|
476
|
+
batchQuery: (sqls, queryOptions) => executeInNewDbClient((client) => sqlExecutor.batchQuery(client, sqls, queryOptions), {
|
|
477
|
+
...options,
|
|
478
|
+
...queryOptions
|
|
479
|
+
}),
|
|
480
|
+
command: (sql, commandOptions) => executeInNewDbClient((client) => sqlExecutor.command(client, sql, commandOptions), {
|
|
481
|
+
...options,
|
|
482
|
+
...commandOptions
|
|
483
|
+
}),
|
|
484
|
+
batchCommand: (sqls, commandOptions) => executeInNewDbClient((client) => sqlExecutor.batchCommand(client, sqls, commandOptions), {
|
|
485
|
+
...options,
|
|
486
|
+
...commandOptions
|
|
487
|
+
})
|
|
188
488
|
});
|
|
189
489
|
const sqlExecutorInNewConnection = (options) => ({
|
|
190
|
-
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
490
|
+
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
491
|
+
...options,
|
|
492
|
+
...queryOptions
|
|
493
|
+
}),
|
|
494
|
+
batchQuery: (sqls, queryOptions) => executeInNewConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
495
|
+
...options,
|
|
496
|
+
...queryOptions
|
|
497
|
+
}),
|
|
498
|
+
command: (sql, commandOptions) => executeInNewConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
499
|
+
...options,
|
|
500
|
+
...commandOptions
|
|
501
|
+
}),
|
|
502
|
+
batchCommand: (sqls, commandOptions) => executeInNewConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
503
|
+
...options,
|
|
504
|
+
...commandOptions
|
|
505
|
+
})
|
|
194
506
|
});
|
|
195
507
|
const sqlExecutorInAmbientConnection = (options) => ({
|
|
196
|
-
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
508
|
+
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
509
|
+
...options,
|
|
510
|
+
...queryOptions
|
|
511
|
+
}),
|
|
512
|
+
batchQuery: (sqls, queryOptions) => executeInAmbientConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
513
|
+
...options,
|
|
514
|
+
...queryOptions
|
|
515
|
+
}),
|
|
516
|
+
command: (sql, commandOptions) => executeInAmbientConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
517
|
+
...options,
|
|
518
|
+
...commandOptions
|
|
519
|
+
}),
|
|
520
|
+
batchCommand: (sqls, commandOptions) => executeInAmbientConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
521
|
+
...options,
|
|
522
|
+
...commandOptions
|
|
523
|
+
})
|
|
200
524
|
});
|
|
201
525
|
const executeInNewDbClient = async (handle, options) => {
|
|
526
|
+
Abort.throwIfAborted(options);
|
|
202
527
|
const { connect, close } = options;
|
|
203
|
-
const client = await connect();
|
|
528
|
+
const client = await connect({ abort: Abort.from(options) });
|
|
204
529
|
try {
|
|
205
530
|
return await handle(client);
|
|
206
531
|
} catch (error) {
|
|
@@ -209,7 +534,8 @@ const executeInNewDbClient = async (handle, options) => {
|
|
|
209
534
|
}
|
|
210
535
|
};
|
|
211
536
|
const executeInNewConnection = async (handle, options) => {
|
|
212
|
-
|
|
537
|
+
Abort.throwIfAborted(options);
|
|
538
|
+
const connection = await options.connection({ abort: Abort.from(options) });
|
|
213
539
|
try {
|
|
214
540
|
return await handle(connection);
|
|
215
541
|
} finally {
|
|
@@ -217,10 +543,8 @@ const executeInNewConnection = async (handle, options) => {
|
|
|
217
543
|
}
|
|
218
544
|
};
|
|
219
545
|
const executeInAmbientConnection = async (handle, options) => {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return await handle(connection);
|
|
223
|
-
} finally {}
|
|
546
|
+
Abort.throwIfAborted(options);
|
|
547
|
+
return handle(await options.connection({ abort: Abort.from(options) }));
|
|
224
548
|
};
|
|
225
549
|
|
|
226
550
|
//#endregion
|
|
@@ -288,10 +612,10 @@ const toTransactionResult = (transactionResult) => transactionResult !== void 0
|
|
|
288
612
|
success: true,
|
|
289
613
|
result: transactionResult
|
|
290
614
|
};
|
|
291
|
-
const executeInTransaction = async (transaction, handle) => {
|
|
615
|
+
const executeInTransaction = async (transaction, handle, context = { abort: Abort.never }) => {
|
|
292
616
|
await transaction.begin();
|
|
293
617
|
try {
|
|
294
|
-
const { success, result } = toTransactionResult(await handle(transaction));
|
|
618
|
+
const { success, result } = toTransactionResult(await handle(transaction, context));
|
|
295
619
|
if (success) await transaction.commit();
|
|
296
620
|
else await transaction.rollback();
|
|
297
621
|
return result;
|
|
@@ -300,18 +624,32 @@ const executeInTransaction = async (transaction, handle) => {
|
|
|
300
624
|
throw e;
|
|
301
625
|
}
|
|
302
626
|
};
|
|
627
|
+
const executeInNestedTransaction = async (transaction, handle, options, context) => {
|
|
628
|
+
Abort.throwIfAborted(options);
|
|
629
|
+
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.");
|
|
630
|
+
return executeInTransaction(transaction, handle, context);
|
|
631
|
+
};
|
|
303
632
|
const transactionFactoryWithDbClient = (connect, initTransaction) => {
|
|
304
633
|
let currentTransaction = void 0;
|
|
305
|
-
const getOrInitCurrentTransaction = (options) =>
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
634
|
+
const getOrInitCurrentTransaction = (options) => {
|
|
635
|
+
Abort.throwIfAborted(options);
|
|
636
|
+
if (currentTransaction) return currentTransaction;
|
|
637
|
+
currentTransaction = initTransaction(connect({ abort: Abort.from(options) }), {
|
|
638
|
+
close: () => {
|
|
639
|
+
currentTransaction = void 0;
|
|
640
|
+
return Promise.resolve();
|
|
641
|
+
},
|
|
642
|
+
...options ?? {}
|
|
643
|
+
});
|
|
644
|
+
return currentTransaction;
|
|
645
|
+
};
|
|
312
646
|
return {
|
|
313
647
|
transaction: getOrInitCurrentTransaction,
|
|
314
|
-
withTransaction: (handle, options) =>
|
|
648
|
+
withTransaction: (handle, options) => {
|
|
649
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
650
|
+
if (abortRejection) return abortRejection;
|
|
651
|
+
return executeInTransaction(getOrInitCurrentTransaction(options), handle, { abort: Abort.from(options) });
|
|
652
|
+
}
|
|
315
653
|
};
|
|
316
654
|
};
|
|
317
655
|
const wrapInConnectionClosure = async (connection, handle) => {
|
|
@@ -323,7 +661,8 @@ const wrapInConnectionClosure = async (connection, handle) => {
|
|
|
323
661
|
};
|
|
324
662
|
const transactionFactoryWithNewConnection = (connect) => ({
|
|
325
663
|
transaction: (options) => {
|
|
326
|
-
|
|
664
|
+
Abort.throwIfAborted(options);
|
|
665
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
327
666
|
const transaction = connection.transaction(options);
|
|
328
667
|
return {
|
|
329
668
|
...transaction,
|
|
@@ -332,7 +671,9 @@ const transactionFactoryWithNewConnection = (connect) => ({
|
|
|
332
671
|
};
|
|
333
672
|
},
|
|
334
673
|
withTransaction: (handle, options) => {
|
|
335
|
-
const
|
|
674
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
675
|
+
if (abortRejection) return abortRejection;
|
|
676
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
336
677
|
const withTx = connection.withTransaction;
|
|
337
678
|
return wrapInConnectionClosure(connection, () => withTx(handle, options));
|
|
338
679
|
}
|
|
@@ -341,19 +682,21 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
341
682
|
close ??= () => Promise.resolve();
|
|
342
683
|
return {
|
|
343
684
|
transaction: (options) => {
|
|
685
|
+
Abort.throwIfAborted(options);
|
|
344
686
|
let conn = null;
|
|
345
687
|
let innerTx = null;
|
|
346
688
|
let connectingPromise = null;
|
|
347
689
|
const ensureConnection = async () => {
|
|
348
690
|
if (conn) return innerTx;
|
|
349
691
|
if (!connectingPromise) connectingPromise = (async () => {
|
|
350
|
-
|
|
692
|
+
Abort.throwIfAborted(options);
|
|
693
|
+
conn = await connect({ abort: Abort.from(options) });
|
|
351
694
|
innerTx = conn.transaction(options);
|
|
352
695
|
})();
|
|
353
696
|
await connectingPromise;
|
|
354
697
|
return innerTx;
|
|
355
698
|
};
|
|
356
|
-
|
|
699
|
+
const tx = {
|
|
357
700
|
driverType,
|
|
358
701
|
get connection() {
|
|
359
702
|
if (!conn) throw new Error("Transaction not started - call begin() first");
|
|
@@ -395,11 +738,14 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
395
738
|
if (conn) await close(conn);
|
|
396
739
|
}
|
|
397
740
|
},
|
|
398
|
-
|
|
741
|
+
withTransaction: (handle, options) => executeInNestedTransaction(tx, handle, options, { abort: Abort.from(options) }),
|
|
742
|
+
_transactionOptions: options ?? {}
|
|
399
743
|
};
|
|
744
|
+
return tx;
|
|
400
745
|
},
|
|
401
746
|
withTransaction: async (handle, options) => {
|
|
402
|
-
|
|
747
|
+
Abort.throwIfAborted(options);
|
|
748
|
+
const conn = await connect({ abort: Abort.from(options) });
|
|
403
749
|
try {
|
|
404
750
|
const withTx = conn.withTransaction;
|
|
405
751
|
return await withTx(handle, options);
|
|
@@ -416,7 +762,10 @@ const createAmbientConnection = (options) => {
|
|
|
416
762
|
const { driverType, client, executor, initTransaction, serializer } = options;
|
|
417
763
|
const clientPromise = Promise.resolve(client);
|
|
418
764
|
const closePromise = Promise.resolve();
|
|
419
|
-
const open = () =>
|
|
765
|
+
const open = (context) => {
|
|
766
|
+
Abort.throwIfAborted(context);
|
|
767
|
+
return clientPromise;
|
|
768
|
+
};
|
|
420
769
|
const close = () => closePromise;
|
|
421
770
|
const typedConnection = {
|
|
422
771
|
driverType,
|
|
@@ -432,9 +781,10 @@ const createConnection = (options) => {
|
|
|
432
781
|
const { driverType, connect, close, initTransaction, executor, serializer } = options;
|
|
433
782
|
let client = null;
|
|
434
783
|
let connectPromise = null;
|
|
435
|
-
const getClient = async () => {
|
|
784
|
+
const getClient = async (context) => {
|
|
785
|
+
Abort.throwIfAborted(context);
|
|
436
786
|
if (client) return client;
|
|
437
|
-
if (!connectPromise) connectPromise = connect().then((c) => {
|
|
787
|
+
if (!connectPromise) connectPromise = connect(context ?? { abort: Abort.never }).then((c) => {
|
|
438
788
|
client = c;
|
|
439
789
|
return c;
|
|
440
790
|
});
|
|
@@ -464,7 +814,10 @@ const createAmbientConnectionPool = (options) => {
|
|
|
464
814
|
getConnection: () => connection,
|
|
465
815
|
execute: connection.execute,
|
|
466
816
|
transaction: (options) => connection.transaction(options),
|
|
467
|
-
withConnection: (handle,
|
|
817
|
+
withConnection: (handle, options) => {
|
|
818
|
+
Abort.throwIfAborted(options);
|
|
819
|
+
return handle(connection, { abort: Abort.from(options) });
|
|
820
|
+
},
|
|
468
821
|
withTransaction: (handle, options) => {
|
|
469
822
|
const withTx = connection.withTransaction;
|
|
470
823
|
return withTx(handle, options);
|
|
@@ -475,48 +828,45 @@ const createSingletonConnectionPool = (options) => {
|
|
|
475
828
|
const { driverType, getConnection } = options;
|
|
476
829
|
let connectionPromise = null;
|
|
477
830
|
let closed = false;
|
|
478
|
-
const
|
|
831
|
+
const operationGuard = guardConcurrentAccess();
|
|
479
832
|
const closedError = () => /* @__PURE__ */ new Error("Singleton connection pool has been closed");
|
|
480
|
-
const
|
|
833
|
+
const executeIfOpen = async (operation, operationOptions) => {
|
|
481
834
|
if (closed) throw closedError();
|
|
482
|
-
|
|
483
|
-
activeOperations.add(activeOperation);
|
|
484
|
-
try {
|
|
485
|
-
return await activeOperation;
|
|
486
|
-
} finally {
|
|
487
|
-
activeOperations.delete(activeOperation);
|
|
488
|
-
}
|
|
835
|
+
return operationGuard.execute(operation, operationOptions);
|
|
489
836
|
};
|
|
490
|
-
const getExistingOrNewConnection = () => {
|
|
491
|
-
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection());
|
|
837
|
+
const getExistingOrNewConnection = (context) => {
|
|
838
|
+
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection(context));
|
|
492
839
|
return connectionPromise;
|
|
493
840
|
};
|
|
494
841
|
const innerTransactionFactory = transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection);
|
|
495
842
|
return {
|
|
496
843
|
driverType,
|
|
497
|
-
connection: () =>
|
|
844
|
+
connection: (connectionOptions) => executeIfOpen((context) => getExistingOrNewConnection(context).then((conn) => wrapPooledConnection(conn, () => Promise.resolve())), connectionOptions),
|
|
498
845
|
execute: (() => {
|
|
499
846
|
const ambientExecutor = sqlExecutorInAmbientConnection({
|
|
500
847
|
driverType,
|
|
501
848
|
connection: getExistingOrNewConnection
|
|
502
849
|
});
|
|
503
850
|
return {
|
|
504
|
-
query: (sql, opts) =>
|
|
505
|
-
batchQuery: (sqls, opts) =>
|
|
506
|
-
command: (sql, opts) =>
|
|
507
|
-
batchCommand: (sqls, opts) =>
|
|
851
|
+
query: (sql, opts) => executeIfOpen(() => ambientExecutor.query(sql, opts), opts),
|
|
852
|
+
batchQuery: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchQuery(sqls, opts), opts),
|
|
853
|
+
command: (sql, opts) => executeIfOpen(() => ambientExecutor.command(sql, opts), opts),
|
|
854
|
+
batchCommand: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchCommand(sqls, opts), opts)
|
|
508
855
|
};
|
|
509
856
|
})(),
|
|
510
|
-
withConnection: (handle,
|
|
857
|
+
withConnection: (handle, options) => executeIfOpen((context) => executeInAmbientConnection((connection) => handle(connection, context), {
|
|
858
|
+
connection: getExistingOrNewConnection,
|
|
859
|
+
...options
|
|
860
|
+
}), options),
|
|
511
861
|
transaction: (transactionOptions) => {
|
|
512
862
|
if (closed) throw closedError();
|
|
513
863
|
return innerTransactionFactory.transaction(transactionOptions);
|
|
514
864
|
},
|
|
515
|
-
withTransaction: (handle, transactionOptions) =>
|
|
865
|
+
withTransaction: (handle, transactionOptions) => executeIfOpen((context) => innerTransactionFactory.withTransaction((tx) => handle(tx, context), transactionOptions), transactionOptions),
|
|
516
866
|
close: async (closeOptions) => {
|
|
517
867
|
if (closed) return;
|
|
518
868
|
closed = true;
|
|
519
|
-
|
|
869
|
+
await operationGuard.stop(closeOptions);
|
|
520
870
|
if (!connectionPromise) return;
|
|
521
871
|
const connection = await connectionPromise;
|
|
522
872
|
connectionPromise = null;
|
|
@@ -528,16 +878,22 @@ const createAlwaysNewConnectionPool = (options) => {
|
|
|
528
878
|
const { driverType, getConnection, connectionOptions } = options;
|
|
529
879
|
return createConnectionPool({
|
|
530
880
|
driverType,
|
|
531
|
-
getConnection: () => connectionOptions ? getConnection(connectionOptions) : getConnection()
|
|
881
|
+
getConnection: (context) => connectionOptions ? getConnection(connectionOptions, context) : getConnection(context)
|
|
532
882
|
});
|
|
533
883
|
};
|
|
534
884
|
const createConnectionPool = (pool) => {
|
|
535
885
|
const { driverType, getConnection } = pool;
|
|
536
|
-
const connection = "connection" in pool ? pool.connection : () =>
|
|
886
|
+
const connection = "connection" in pool ? pool.connection : (options) => {
|
|
887
|
+
Abort.throwIfAborted(options);
|
|
888
|
+
return Promise.resolve(getConnection({ abort: Abort.from(options) }));
|
|
889
|
+
};
|
|
537
890
|
return {
|
|
538
891
|
driverType,
|
|
539
892
|
connection,
|
|
540
|
-
withConnection: "withConnection" in pool ? pool.withConnection : (handle,
|
|
893
|
+
withConnection: "withConnection" in pool ? pool.withConnection : (handle, options) => executeInNewConnection((connection) => handle(connection, { abort: Abort.from(options) }), {
|
|
894
|
+
connection,
|
|
895
|
+
...options
|
|
896
|
+
}),
|
|
541
897
|
close: "close" in pool ? pool.close : () => Promise.resolve(),
|
|
542
898
|
execute: "execute" in pool ? pool.execute : sqlExecutorInNewConnection({
|
|
543
899
|
driverType,
|
|
@@ -1872,9 +2228,9 @@ const sqliteSQLExecutor = (driverType, serializer, formatter, errorMapper) => ({
|
|
|
1872
2228
|
|
|
1873
2229
|
//#endregion
|
|
1874
2230
|
//#region src/storage/sqlite/core/transactions/index.ts
|
|
1875
|
-
const sqliteTransaction = (driverType, connection,
|
|
1876
|
-
|
|
1877
|
-
const
|
|
2231
|
+
const sqliteTransaction = (driverType, connection, defaultOptions, serializer, defaultTransactionMode) => (getClient, options) => {
|
|
2232
|
+
const defaultTransactionOptions = typeof defaultOptions === "boolean" ? { allowNestedTransactions: defaultOptions } : defaultOptions;
|
|
2233
|
+
const allowNestedTransactions = options?.allowNestedTransactions ?? defaultTransactionOptions.allowNestedTransactions ?? false;
|
|
1878
2234
|
const tx = databaseTransaction({
|
|
1879
2235
|
begin: async () => {
|
|
1880
2236
|
const client = await getClient;
|
|
@@ -1902,23 +2258,28 @@ const sqliteTransaction = (driverType, connection, allowNestedTransactions, seri
|
|
|
1902
2258
|
},
|
|
1903
2259
|
releaseSavepoint: async (level) => {
|
|
1904
2260
|
await (await getClient).command(SQL`RELEASE transaction${SQL.plain(level.toString())}`);
|
|
2261
|
+
},
|
|
2262
|
+
rollbackToSavepoint: async (level) => {
|
|
2263
|
+
await (await getClient).command(SQL`ROLLBACK TO transaction${SQL.plain(level.toString())}`);
|
|
1905
2264
|
}
|
|
1906
2265
|
}, {
|
|
1907
2266
|
allowNestedTransactions,
|
|
1908
|
-
useSavepoints
|
|
2267
|
+
useSavepoints: options?.useSavepoints ?? defaultTransactionOptions.useSavepoints ?? false
|
|
1909
2268
|
});
|
|
1910
|
-
|
|
2269
|
+
const transaction = {
|
|
1911
2270
|
connection: connection(),
|
|
1912
2271
|
driverType,
|
|
1913
2272
|
begin: tx.begin,
|
|
1914
2273
|
commit: tx.commit,
|
|
1915
2274
|
rollback: tx.rollback,
|
|
1916
2275
|
execute: sqlExecutor(sqliteSQLExecutor(driverType, serializer), { connect: () => getClient }),
|
|
2276
|
+
withTransaction: (handle, options) => executeInNestedTransaction(transaction, handle, options),
|
|
1917
2277
|
_transactionOptions: {
|
|
1918
2278
|
...options,
|
|
1919
2279
|
allowNestedTransactions
|
|
1920
2280
|
}
|
|
1921
2281
|
};
|
|
2282
|
+
return transaction;
|
|
1922
2283
|
};
|
|
1923
2284
|
|
|
1924
2285
|
//#endregion
|
|
@@ -1963,7 +2324,7 @@ const sqliteAmbientClientConnection = (options) => {
|
|
|
1963
2324
|
return createAmbientConnection({
|
|
1964
2325
|
driverType,
|
|
1965
2326
|
client,
|
|
1966
|
-
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, allowNestedTransactions ?? false, serializer, defaultTransactionMode)),
|
|
2327
|
+
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, { allowNestedTransactions: allowNestedTransactions ?? false }, serializer, defaultTransactionMode)),
|
|
1967
2328
|
executor: ({ serializer }) => sqliteSQLExecutor(driverType, serializer, void 0, errorMapper),
|
|
1968
2329
|
serializer
|
|
1969
2330
|
});
|
|
@@ -1988,7 +2349,7 @@ const sqliteClientConnection = (options) => {
|
|
|
1988
2349
|
if (client && "close" in client && typeof client.close === "function") await client.close();
|
|
1989
2350
|
else if (client && "release" in client && typeof client.release === "function") client.release();
|
|
1990
2351
|
},
|
|
1991
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2352
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
1992
2353
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
1993
2354
|
serializer
|
|
1994
2355
|
});
|
|
@@ -2010,7 +2371,7 @@ const sqlitePoolClientConnection = (options) => {
|
|
|
2010
2371
|
driverType: options.driverType,
|
|
2011
2372
|
connect,
|
|
2012
2373
|
close: () => client !== null ? Promise.resolve(client.release()) : Promise.resolve(),
|
|
2013
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2374
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
2014
2375
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
2015
2376
|
serializer
|
|
2016
2377
|
});
|