@event-driven-io/dumbo 0.13.0-beta.45 → 0.13.0-beta.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cloudflare.cjs +432 -69
- package/dist/cloudflare.cjs.map +1 -1
- package/dist/cloudflare.d.cts +2 -2
- package/dist/cloudflare.d.ts +2 -2
- package/dist/cloudflare.js +432 -69
- package/dist/cloudflare.js.map +1 -1
- package/dist/{index-B5krjjrh.d.ts → index-4hcCG4SA.d.ts} +66 -30
- package/dist/{index-BaLXbc2l.d.ts → index-B7366zWR.d.ts} +1 -1
- package/dist/{index-_dj3upBo.d.cts → index-BJ-dm7dG.d.cts} +66 -30
- package/dist/{index-kYttgYkC.d.cts → index-BkJ1pipM.d.cts} +1 -1
- package/dist/{index-C9B46a1u.d.ts → index-CQokD3za.d.cts} +2 -2
- package/dist/{index-BoLWBnxd.d.cts → index-DIJv-L6M.d.ts} +2 -2
- package/dist/index.cjs +523 -269
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +522 -270
- package/dist/index.js.map +1 -1
- package/dist/pg.cjs +168 -28
- package/dist/pg.cjs.map +1 -1
- package/dist/pg.d.cts +2 -2
- package/dist/pg.d.ts +2 -2
- package/dist/pg.js +168 -28
- package/dist/pg.js.map +1 -1
- package/dist/postgresql.cjs +4 -0
- package/dist/postgresql.cjs.map +1 -1
- package/dist/postgresql.d.cts +2 -2
- package/dist/postgresql.d.ts +2 -2
- package/dist/postgresql.js +4 -0
- package/dist/postgresql.js.map +1 -1
- package/dist/sqlite.cjs +429 -68
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +2 -2
- package/dist/sqlite.d.ts +2 -2
- package/dist/sqlite.js +429 -68
- package/dist/sqlite.js.map +1 -1
- package/dist/sqlite3.cjs +639 -343
- package/dist/sqlite3.cjs.map +1 -1
- package/dist/sqlite3.d.cts +10 -10
- package/dist/sqlite3.d.ts +10 -10
- package/dist/sqlite3.js +639 -343
- package/dist/sqlite3.js.map +1 -1
- package/package.json +1 -1
package/dist/cloudflare.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) => {
|
|
@@ -196,26 +484,63 @@ var BatchCommandNoChangesError = class BatchCommandNoChangesError extends DumboE
|
|
|
196
484
|
}
|
|
197
485
|
};
|
|
198
486
|
const sqlExecutor = (sqlExecutor, options) => ({
|
|
199
|
-
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions),
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
487
|
+
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions), {
|
|
488
|
+
...options,
|
|
489
|
+
...queryOptions
|
|
490
|
+
}),
|
|
491
|
+
batchQuery: (sqls, queryOptions) => executeInNewDbClient((client) => sqlExecutor.batchQuery(client, sqls, queryOptions), {
|
|
492
|
+
...options,
|
|
493
|
+
...queryOptions
|
|
494
|
+
}),
|
|
495
|
+
command: (sql, commandOptions) => executeInNewDbClient((client) => sqlExecutor.command(client, sql, commandOptions), {
|
|
496
|
+
...options,
|
|
497
|
+
...commandOptions
|
|
498
|
+
}),
|
|
499
|
+
batchCommand: (sqls, commandOptions) => executeInNewDbClient((client) => sqlExecutor.batchCommand(client, sqls, commandOptions), {
|
|
500
|
+
...options,
|
|
501
|
+
...commandOptions
|
|
502
|
+
})
|
|
203
503
|
});
|
|
204
504
|
const sqlExecutorInNewConnection = (options) => ({
|
|
205
|
-
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
505
|
+
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
506
|
+
...options,
|
|
507
|
+
...queryOptions
|
|
508
|
+
}),
|
|
509
|
+
batchQuery: (sqls, queryOptions) => executeInNewConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
510
|
+
...options,
|
|
511
|
+
...queryOptions
|
|
512
|
+
}),
|
|
513
|
+
command: (sql, commandOptions) => executeInNewConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
514
|
+
...options,
|
|
515
|
+
...commandOptions
|
|
516
|
+
}),
|
|
517
|
+
batchCommand: (sqls, commandOptions) => executeInNewConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
518
|
+
...options,
|
|
519
|
+
...commandOptions
|
|
520
|
+
})
|
|
209
521
|
});
|
|
210
522
|
const sqlExecutorInAmbientConnection = (options) => ({
|
|
211
|
-
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
523
|
+
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
524
|
+
...options,
|
|
525
|
+
...queryOptions
|
|
526
|
+
}),
|
|
527
|
+
batchQuery: (sqls, queryOptions) => executeInAmbientConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
528
|
+
...options,
|
|
529
|
+
...queryOptions
|
|
530
|
+
}),
|
|
531
|
+
command: (sql, commandOptions) => executeInAmbientConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
532
|
+
...options,
|
|
533
|
+
...commandOptions
|
|
534
|
+
}),
|
|
535
|
+
batchCommand: (sqls, commandOptions) => executeInAmbientConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
536
|
+
...options,
|
|
537
|
+
...commandOptions
|
|
538
|
+
})
|
|
215
539
|
});
|
|
216
540
|
const executeInNewDbClient = async (handle, options) => {
|
|
541
|
+
Abort.throwIfAborted(options);
|
|
217
542
|
const { connect, close } = options;
|
|
218
|
-
const client = await connect();
|
|
543
|
+
const client = await connect({ abort: Abort.from(options) });
|
|
219
544
|
try {
|
|
220
545
|
return await handle(client);
|
|
221
546
|
} catch (error) {
|
|
@@ -224,7 +549,8 @@ const executeInNewDbClient = async (handle, options) => {
|
|
|
224
549
|
}
|
|
225
550
|
};
|
|
226
551
|
const executeInNewConnection = async (handle, options) => {
|
|
227
|
-
|
|
552
|
+
Abort.throwIfAborted(options);
|
|
553
|
+
const connection = await options.connection({ abort: Abort.from(options) });
|
|
228
554
|
try {
|
|
229
555
|
return await handle(connection);
|
|
230
556
|
} finally {
|
|
@@ -232,10 +558,8 @@ const executeInNewConnection = async (handle, options) => {
|
|
|
232
558
|
}
|
|
233
559
|
};
|
|
234
560
|
const executeInAmbientConnection = async (handle, options) => {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return await handle(connection);
|
|
238
|
-
} finally {}
|
|
561
|
+
Abort.throwIfAborted(options);
|
|
562
|
+
return handle(await options.connection({ abort: Abort.from(options) }));
|
|
239
563
|
};
|
|
240
564
|
|
|
241
565
|
//#endregion
|
|
@@ -303,10 +627,10 @@ const toTransactionResult = (transactionResult) => transactionResult !== void 0
|
|
|
303
627
|
success: true,
|
|
304
628
|
result: transactionResult
|
|
305
629
|
};
|
|
306
|
-
const executeInTransaction = async (transaction, handle) => {
|
|
630
|
+
const executeInTransaction = async (transaction, handle, context = { abort: Abort.never }) => {
|
|
307
631
|
await transaction.begin();
|
|
308
632
|
try {
|
|
309
|
-
const { success, result } = toTransactionResult(await handle(transaction));
|
|
633
|
+
const { success, result } = toTransactionResult(await handle(transaction, context));
|
|
310
634
|
if (success) await transaction.commit();
|
|
311
635
|
else await transaction.rollback();
|
|
312
636
|
return result;
|
|
@@ -315,18 +639,32 @@ const executeInTransaction = async (transaction, handle) => {
|
|
|
315
639
|
throw e;
|
|
316
640
|
}
|
|
317
641
|
};
|
|
642
|
+
const executeInNestedTransaction = async (transaction, handle, options, context) => {
|
|
643
|
+
Abort.throwIfAborted(options);
|
|
644
|
+
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.");
|
|
645
|
+
return executeInTransaction(transaction, handle, context);
|
|
646
|
+
};
|
|
318
647
|
const transactionFactoryWithDbClient = (connect, initTransaction) => {
|
|
319
648
|
let currentTransaction = void 0;
|
|
320
|
-
const getOrInitCurrentTransaction = (options) =>
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
649
|
+
const getOrInitCurrentTransaction = (options) => {
|
|
650
|
+
Abort.throwIfAborted(options);
|
|
651
|
+
if (currentTransaction) return currentTransaction;
|
|
652
|
+
currentTransaction = initTransaction(connect({ abort: Abort.from(options) }), {
|
|
653
|
+
close: () => {
|
|
654
|
+
currentTransaction = void 0;
|
|
655
|
+
return Promise.resolve();
|
|
656
|
+
},
|
|
657
|
+
...options ?? {}
|
|
658
|
+
});
|
|
659
|
+
return currentTransaction;
|
|
660
|
+
};
|
|
327
661
|
return {
|
|
328
662
|
transaction: getOrInitCurrentTransaction,
|
|
329
|
-
withTransaction: (handle, options) =>
|
|
663
|
+
withTransaction: (handle, options) => {
|
|
664
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
665
|
+
if (abortRejection) return abortRejection;
|
|
666
|
+
return executeInTransaction(getOrInitCurrentTransaction(options), handle, { abort: Abort.from(options) });
|
|
667
|
+
}
|
|
330
668
|
};
|
|
331
669
|
};
|
|
332
670
|
const wrapInConnectionClosure = async (connection, handle) => {
|
|
@@ -338,7 +676,8 @@ const wrapInConnectionClosure = async (connection, handle) => {
|
|
|
338
676
|
};
|
|
339
677
|
const transactionFactoryWithNewConnection = (connect) => ({
|
|
340
678
|
transaction: (options) => {
|
|
341
|
-
|
|
679
|
+
Abort.throwIfAborted(options);
|
|
680
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
342
681
|
const transaction = connection.transaction(options);
|
|
343
682
|
return {
|
|
344
683
|
...transaction,
|
|
@@ -347,7 +686,9 @@ const transactionFactoryWithNewConnection = (connect) => ({
|
|
|
347
686
|
};
|
|
348
687
|
},
|
|
349
688
|
withTransaction: (handle, options) => {
|
|
350
|
-
const
|
|
689
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
690
|
+
if (abortRejection) return abortRejection;
|
|
691
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
351
692
|
const withTx = connection.withTransaction;
|
|
352
693
|
return wrapInConnectionClosure(connection, () => withTx(handle, options));
|
|
353
694
|
}
|
|
@@ -356,19 +697,21 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
356
697
|
close ??= () => Promise.resolve();
|
|
357
698
|
return {
|
|
358
699
|
transaction: (options) => {
|
|
700
|
+
Abort.throwIfAborted(options);
|
|
359
701
|
let conn = null;
|
|
360
702
|
let innerTx = null;
|
|
361
703
|
let connectingPromise = null;
|
|
362
704
|
const ensureConnection = async () => {
|
|
363
705
|
if (conn) return innerTx;
|
|
364
706
|
if (!connectingPromise) connectingPromise = (async () => {
|
|
365
|
-
|
|
707
|
+
Abort.throwIfAborted(options);
|
|
708
|
+
conn = await connect({ abort: Abort.from(options) });
|
|
366
709
|
innerTx = conn.transaction(options);
|
|
367
710
|
})();
|
|
368
711
|
await connectingPromise;
|
|
369
712
|
return innerTx;
|
|
370
713
|
};
|
|
371
|
-
|
|
714
|
+
const tx = {
|
|
372
715
|
driverType,
|
|
373
716
|
get connection() {
|
|
374
717
|
if (!conn) throw new Error("Transaction not started - call begin() first");
|
|
@@ -410,11 +753,14 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
410
753
|
if (conn) await close(conn);
|
|
411
754
|
}
|
|
412
755
|
},
|
|
413
|
-
|
|
756
|
+
withTransaction: (handle, options) => executeInNestedTransaction(tx, handle, options, { abort: Abort.from(options) }),
|
|
757
|
+
_transactionOptions: options ?? {}
|
|
414
758
|
};
|
|
759
|
+
return tx;
|
|
415
760
|
},
|
|
416
761
|
withTransaction: async (handle, options) => {
|
|
417
|
-
|
|
762
|
+
Abort.throwIfAborted(options);
|
|
763
|
+
const conn = await connect({ abort: Abort.from(options) });
|
|
418
764
|
try {
|
|
419
765
|
const withTx = conn.withTransaction;
|
|
420
766
|
return await withTx(handle, options);
|
|
@@ -431,7 +777,10 @@ const createAmbientConnection = (options) => {
|
|
|
431
777
|
const { driverType, client, executor, initTransaction, serializer } = options;
|
|
432
778
|
const clientPromise = Promise.resolve(client);
|
|
433
779
|
const closePromise = Promise.resolve();
|
|
434
|
-
const open = () =>
|
|
780
|
+
const open = (context) => {
|
|
781
|
+
Abort.throwIfAborted(context);
|
|
782
|
+
return clientPromise;
|
|
783
|
+
};
|
|
435
784
|
const close = () => closePromise;
|
|
436
785
|
const typedConnection = {
|
|
437
786
|
driverType,
|
|
@@ -447,9 +796,10 @@ const createConnection = (options) => {
|
|
|
447
796
|
const { driverType, connect, close, initTransaction, executor, serializer } = options;
|
|
448
797
|
let client = null;
|
|
449
798
|
let connectPromise = null;
|
|
450
|
-
const getClient = async () => {
|
|
799
|
+
const getClient = async (context) => {
|
|
800
|
+
Abort.throwIfAborted(context);
|
|
451
801
|
if (client) return client;
|
|
452
|
-
if (!connectPromise) connectPromise = connect().then((c) => {
|
|
802
|
+
if (!connectPromise) connectPromise = connect(context ?? { abort: Abort.never }).then((c) => {
|
|
453
803
|
client = c;
|
|
454
804
|
return c;
|
|
455
805
|
});
|
|
@@ -479,7 +829,10 @@ const createAmbientConnectionPool = (options) => {
|
|
|
479
829
|
getConnection: () => connection,
|
|
480
830
|
execute: connection.execute,
|
|
481
831
|
transaction: (options) => connection.transaction(options),
|
|
482
|
-
withConnection: (handle,
|
|
832
|
+
withConnection: (handle, options) => {
|
|
833
|
+
Abort.throwIfAborted(options);
|
|
834
|
+
return handle(connection, { abort: Abort.from(options) });
|
|
835
|
+
},
|
|
483
836
|
withTransaction: (handle, options) => {
|
|
484
837
|
const withTx = connection.withTransaction;
|
|
485
838
|
return withTx(handle, options);
|
|
@@ -490,48 +843,45 @@ const createSingletonConnectionPool = (options) => {
|
|
|
490
843
|
const { driverType, getConnection } = options;
|
|
491
844
|
let connectionPromise = null;
|
|
492
845
|
let closed = false;
|
|
493
|
-
const
|
|
846
|
+
const operationGuard = guardConcurrentAccess();
|
|
494
847
|
const closedError = () => /* @__PURE__ */ new Error("Singleton connection pool has been closed");
|
|
495
|
-
const
|
|
848
|
+
const executeIfOpen = async (operation, operationOptions) => {
|
|
496
849
|
if (closed) throw closedError();
|
|
497
|
-
|
|
498
|
-
activeOperations.add(activeOperation);
|
|
499
|
-
try {
|
|
500
|
-
return await activeOperation;
|
|
501
|
-
} finally {
|
|
502
|
-
activeOperations.delete(activeOperation);
|
|
503
|
-
}
|
|
850
|
+
return operationGuard.execute(operation, operationOptions);
|
|
504
851
|
};
|
|
505
|
-
const getExistingOrNewConnection = () => {
|
|
506
|
-
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection());
|
|
852
|
+
const getExistingOrNewConnection = (context) => {
|
|
853
|
+
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection(context));
|
|
507
854
|
return connectionPromise;
|
|
508
855
|
};
|
|
509
856
|
const innerTransactionFactory = transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection);
|
|
510
857
|
return {
|
|
511
858
|
driverType,
|
|
512
|
-
connection: () =>
|
|
859
|
+
connection: (connectionOptions) => executeIfOpen((context) => getExistingOrNewConnection(context).then((conn) => wrapPooledConnection(conn, () => Promise.resolve())), connectionOptions),
|
|
513
860
|
execute: (() => {
|
|
514
861
|
const ambientExecutor = sqlExecutorInAmbientConnection({
|
|
515
862
|
driverType,
|
|
516
863
|
connection: getExistingOrNewConnection
|
|
517
864
|
});
|
|
518
865
|
return {
|
|
519
|
-
query: (sql, opts) =>
|
|
520
|
-
batchQuery: (sqls, opts) =>
|
|
521
|
-
command: (sql, opts) =>
|
|
522
|
-
batchCommand: (sqls, opts) =>
|
|
866
|
+
query: (sql, opts) => executeIfOpen(() => ambientExecutor.query(sql, opts), opts),
|
|
867
|
+
batchQuery: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchQuery(sqls, opts), opts),
|
|
868
|
+
command: (sql, opts) => executeIfOpen(() => ambientExecutor.command(sql, opts), opts),
|
|
869
|
+
batchCommand: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchCommand(sqls, opts), opts)
|
|
523
870
|
};
|
|
524
871
|
})(),
|
|
525
|
-
withConnection: (handle,
|
|
872
|
+
withConnection: (handle, options) => executeIfOpen((context) => executeInAmbientConnection((connection) => handle(connection, context), {
|
|
873
|
+
connection: getExistingOrNewConnection,
|
|
874
|
+
...options
|
|
875
|
+
}), options),
|
|
526
876
|
transaction: (transactionOptions) => {
|
|
527
877
|
if (closed) throw closedError();
|
|
528
878
|
return innerTransactionFactory.transaction(transactionOptions);
|
|
529
879
|
},
|
|
530
|
-
withTransaction: (handle, transactionOptions) =>
|
|
880
|
+
withTransaction: (handle, transactionOptions) => executeIfOpen((context) => innerTransactionFactory.withTransaction((tx) => handle(tx, context), transactionOptions), transactionOptions),
|
|
531
881
|
close: async (closeOptions) => {
|
|
532
882
|
if (closed) return;
|
|
533
883
|
closed = true;
|
|
534
|
-
|
|
884
|
+
await operationGuard.stop(closeOptions);
|
|
535
885
|
if (!connectionPromise) return;
|
|
536
886
|
const connection = await connectionPromise;
|
|
537
887
|
connectionPromise = null;
|
|
@@ -543,16 +893,22 @@ const createAlwaysNewConnectionPool = (options) => {
|
|
|
543
893
|
const { driverType, getConnection, connectionOptions } = options;
|
|
544
894
|
return createConnectionPool({
|
|
545
895
|
driverType,
|
|
546
|
-
getConnection: () => connectionOptions ? getConnection(connectionOptions) : getConnection()
|
|
896
|
+
getConnection: (context) => connectionOptions ? getConnection(connectionOptions, context) : getConnection(context)
|
|
547
897
|
});
|
|
548
898
|
};
|
|
549
899
|
const createConnectionPool = (pool) => {
|
|
550
900
|
const { driverType, getConnection } = pool;
|
|
551
|
-
const connection = "connection" in pool ? pool.connection : () =>
|
|
901
|
+
const connection = "connection" in pool ? pool.connection : (options) => {
|
|
902
|
+
Abort.throwIfAborted(options);
|
|
903
|
+
return Promise.resolve(getConnection({ abort: Abort.from(options) }));
|
|
904
|
+
};
|
|
552
905
|
return {
|
|
553
906
|
driverType,
|
|
554
907
|
connection,
|
|
555
|
-
withConnection: "withConnection" in pool ? pool.withConnection : (handle,
|
|
908
|
+
withConnection: "withConnection" in pool ? pool.withConnection : (handle, options) => executeInNewConnection((connection) => handle(connection, { abort: Abort.from(options) }), {
|
|
909
|
+
connection,
|
|
910
|
+
...options
|
|
911
|
+
}),
|
|
556
912
|
close: "close" in pool ? pool.close : () => Promise.resolve(),
|
|
557
913
|
execute: "execute" in pool ? pool.execute : sqlExecutorInNewConnection({
|
|
558
914
|
driverType,
|
|
@@ -1887,9 +2243,9 @@ const sqliteSQLExecutor = (driverType, serializer, formatter, errorMapper) => ({
|
|
|
1887
2243
|
|
|
1888
2244
|
//#endregion
|
|
1889
2245
|
//#region src/storage/sqlite/core/transactions/index.ts
|
|
1890
|
-
const sqliteTransaction = (driverType, connection,
|
|
1891
|
-
|
|
1892
|
-
const
|
|
2246
|
+
const sqliteTransaction = (driverType, connection, defaultOptions, serializer, defaultTransactionMode) => (getClient, options) => {
|
|
2247
|
+
const defaultTransactionOptions = typeof defaultOptions === "boolean" ? { allowNestedTransactions: defaultOptions } : defaultOptions;
|
|
2248
|
+
const allowNestedTransactions = options?.allowNestedTransactions ?? defaultTransactionOptions.allowNestedTransactions ?? false;
|
|
1893
2249
|
const tx = databaseTransaction({
|
|
1894
2250
|
begin: async () => {
|
|
1895
2251
|
const client = await getClient;
|
|
@@ -1917,23 +2273,28 @@ const sqliteTransaction = (driverType, connection, allowNestedTransactions, seri
|
|
|
1917
2273
|
},
|
|
1918
2274
|
releaseSavepoint: async (level) => {
|
|
1919
2275
|
await (await getClient).command(SQL`RELEASE transaction${SQL.plain(level.toString())}`);
|
|
2276
|
+
},
|
|
2277
|
+
rollbackToSavepoint: async (level) => {
|
|
2278
|
+
await (await getClient).command(SQL`ROLLBACK TO transaction${SQL.plain(level.toString())}`);
|
|
1920
2279
|
}
|
|
1921
2280
|
}, {
|
|
1922
2281
|
allowNestedTransactions,
|
|
1923
|
-
useSavepoints
|
|
2282
|
+
useSavepoints: options?.useSavepoints ?? defaultTransactionOptions.useSavepoints ?? false
|
|
1924
2283
|
});
|
|
1925
|
-
|
|
2284
|
+
const transaction = {
|
|
1926
2285
|
connection: connection(),
|
|
1927
2286
|
driverType,
|
|
1928
2287
|
begin: tx.begin,
|
|
1929
2288
|
commit: tx.commit,
|
|
1930
2289
|
rollback: tx.rollback,
|
|
1931
2290
|
execute: sqlExecutor(sqliteSQLExecutor(driverType, serializer), { connect: () => getClient }),
|
|
2291
|
+
withTransaction: (handle, options) => executeInNestedTransaction(transaction, handle, options),
|
|
1932
2292
|
_transactionOptions: {
|
|
1933
2293
|
...options,
|
|
1934
2294
|
allowNestedTransactions
|
|
1935
2295
|
}
|
|
1936
2296
|
};
|
|
2297
|
+
return transaction;
|
|
1937
2298
|
};
|
|
1938
2299
|
|
|
1939
2300
|
//#endregion
|
|
@@ -1978,7 +2339,7 @@ const sqliteAmbientClientConnection = (options) => {
|
|
|
1978
2339
|
return createAmbientConnection({
|
|
1979
2340
|
driverType,
|
|
1980
2341
|
client,
|
|
1981
|
-
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, allowNestedTransactions ?? false, serializer, defaultTransactionMode)),
|
|
2342
|
+
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, { allowNestedTransactions: allowNestedTransactions ?? false }, serializer, defaultTransactionMode)),
|
|
1982
2343
|
executor: ({ serializer }) => sqliteSQLExecutor(driverType, serializer, void 0, errorMapper),
|
|
1983
2344
|
serializer
|
|
1984
2345
|
});
|
|
@@ -2003,7 +2364,7 @@ const sqliteClientConnection = (options) => {
|
|
|
2003
2364
|
if (client && "close" in client && typeof client.close === "function") await client.close();
|
|
2004
2365
|
else if (client && "release" in client && typeof client.release === "function") client.release();
|
|
2005
2366
|
},
|
|
2006
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2367
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
2007
2368
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
2008
2369
|
serializer
|
|
2009
2370
|
});
|
|
@@ -2025,7 +2386,7 @@ const sqlitePoolClientConnection = (options) => {
|
|
|
2025
2386
|
driverType: options.driverType,
|
|
2026
2387
|
connect,
|
|
2027
2388
|
close: () => client !== null ? Promise.resolve(client.release()) : Promise.resolve(),
|
|
2028
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2389
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
2029
2390
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
2030
2391
|
serializer
|
|
2031
2392
|
});
|
|
@@ -2395,7 +2756,7 @@ const d1Transaction = (connection, serializer, defaultOptions) => (getClient, op
|
|
|
2395
2756
|
client = await getClient;
|
|
2396
2757
|
return client;
|
|
2397
2758
|
};
|
|
2398
|
-
|
|
2759
|
+
const transaction = {
|
|
2399
2760
|
connection: connection(),
|
|
2400
2761
|
driverType: D1DriverType,
|
|
2401
2762
|
begin: async function() {
|
|
@@ -2439,8 +2800,10 @@ const d1Transaction = (connection, serializer, defaultOptions) => (getClient, op
|
|
|
2439
2800
|
if (!sessionClient) throw new Error("Transaction has not been started. Call begin() first.");
|
|
2440
2801
|
return Promise.resolve(sessionClient);
|
|
2441
2802
|
} }),
|
|
2803
|
+
withTransaction: (handle, options) => executeInNestedTransaction(transaction, handle, options),
|
|
2442
2804
|
_transactionOptions: options ?? {}
|
|
2443
2805
|
};
|
|
2806
|
+
return transaction;
|
|
2444
2807
|
};
|
|
2445
2808
|
|
|
2446
2809
|
//#endregion
|