@event-driven-io/dumbo 0.13.0-beta.43 → 0.13.0-beta.44

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/index.cjs CHANGED
@@ -323,6 +323,65 @@ const executeInAmbientConnection = async (handle, options) => {
323
323
 
324
324
  //#endregion
325
325
  //#region src/core/connections/transaction.ts
326
+ const transactionNestingCounter = () => {
327
+ let transactionLevel = 0;
328
+ return {
329
+ reset: () => {
330
+ transactionLevel = 0;
331
+ },
332
+ increment: () => {
333
+ transactionLevel++;
334
+ },
335
+ decrement: () => {
336
+ transactionLevel--;
337
+ if (transactionLevel < 0) throw new Error("Transaction level is out of bounds");
338
+ },
339
+ get level() {
340
+ return transactionLevel;
341
+ }
342
+ };
343
+ };
344
+ const databaseTransaction = (backend, options) => {
345
+ const allowNestedTransactions = options?.allowNestedTransactions ?? false;
346
+ const useSavepoints = options?.useSavepoints ?? false;
347
+ const counter = transactionNestingCounter();
348
+ let hasBegun = false;
349
+ return {
350
+ begin: async () => {
351
+ if (!allowNestedTransactions && hasBegun) throw new InvalidOperationError("Cannot start a nested transaction: allowNestedTransactions is false. Set transactionOptions: { allowNestedTransactions: true } on your pool or connection.");
352
+ if (allowNestedTransactions) {
353
+ if (counter.level >= 1) {
354
+ counter.increment();
355
+ if (useSavepoints && backend.savepoint) await backend.savepoint(counter.level);
356
+ return;
357
+ }
358
+ counter.increment();
359
+ }
360
+ hasBegun = true;
361
+ await backend.begin();
362
+ },
363
+ commit: async () => {
364
+ if (allowNestedTransactions && counter.level > 1) {
365
+ if (useSavepoints && backend.releaseSavepoint) await backend.releaseSavepoint(counter.level);
366
+ counter.decrement();
367
+ return;
368
+ }
369
+ if (allowNestedTransactions) counter.reset();
370
+ hasBegun = false;
371
+ await backend.commit();
372
+ },
373
+ rollback: async (error) => {
374
+ if (allowNestedTransactions && counter.level > 1) {
375
+ if (useSavepoints && backend.rollbackToSavepoint) await backend.rollbackToSavepoint(counter.level);
376
+ counter.decrement();
377
+ return;
378
+ }
379
+ if (allowNestedTransactions) counter.reset();
380
+ hasBegun = false;
381
+ await backend.rollback(error);
382
+ }
383
+ };
384
+ };
326
385
  const toTransactionResult = (transactionResult) => transactionResult !== void 0 && transactionResult !== null && typeof transactionResult === "object" && "success" in transactionResult ? transactionResult : {
327
386
  success: true,
328
387
  result: transactionResult
@@ -729,33 +788,80 @@ const createAmbientConnectionPool = (options) => {
729
788
  const createSingletonConnectionPool = (options) => {
730
789
  const { driverType, getConnection } = options;
731
790
  let connectionPromise = null;
791
+ let closed = false;
792
+ const activeOperations = /* @__PURE__ */ new Set();
793
+ const closedError = () => /* @__PURE__ */ new Error("Singleton connection pool has been closed");
794
+ const run = async (operation) => {
795
+ if (closed) throw closedError();
796
+ const activeOperation = operation();
797
+ activeOperations.add(activeOperation);
798
+ try {
799
+ return await activeOperation;
800
+ } finally {
801
+ activeOperations.delete(activeOperation);
802
+ }
803
+ };
732
804
  const getExistingOrNewConnection = () => {
733
805
  if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection());
734
806
  return connectionPromise;
735
807
  };
808
+ const innerTransactionFactory = transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection);
736
809
  return {
737
810
  driverType,
738
- connection: () => getExistingOrNewConnection().then((conn) => wrapPooledConnection(conn, () => Promise.resolve())),
739
- execute: sqlExecutorInAmbientConnection({
740
- driverType,
741
- connection: getExistingOrNewConnection
742
- }),
743
- withConnection: (handle, _options) => executeInAmbientConnection(handle, { connection: getExistingOrNewConnection }),
744
- ...transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection),
745
- close: async () => {
811
+ connection: () => run(() => getExistingOrNewConnection().then((conn) => wrapPooledConnection(conn, () => Promise.resolve()))),
812
+ execute: (() => {
813
+ const ambientExecutor = sqlExecutorInAmbientConnection({
814
+ driverType,
815
+ connection: getExistingOrNewConnection
816
+ });
817
+ return {
818
+ query: (sql, opts) => run(() => ambientExecutor.query(sql, opts)),
819
+ batchQuery: (sqls, opts) => run(() => ambientExecutor.batchQuery(sqls, opts)),
820
+ command: (sql, opts) => run(() => ambientExecutor.command(sql, opts)),
821
+ batchCommand: (sqls, opts) => run(() => ambientExecutor.batchCommand(sqls, opts))
822
+ };
823
+ })(),
824
+ withConnection: (handle, _options) => run(() => executeInAmbientConnection(handle, { connection: getExistingOrNewConnection })),
825
+ transaction: (transactionOptions) => {
826
+ if (closed) throw closedError();
827
+ return innerTransactionFactory.transaction(transactionOptions);
828
+ },
829
+ withTransaction: (handle, transactionOptions) => run(() => innerTransactionFactory.withTransaction(handle, transactionOptions)),
830
+ close: async (closeOptions) => {
831
+ if (closed) return;
832
+ closed = true;
833
+ if (!closeOptions?.force) await Promise.allSettled([...activeOperations]);
746
834
  if (!connectionPromise) return;
747
- await (await connectionPromise).close();
835
+ const connection = await connectionPromise;
836
+ connectionPromise = null;
837
+ await connection.close();
748
838
  }
749
839
  };
750
840
  };
751
841
  const createBoundedConnectionPool = (options) => {
752
842
  const { driverType, maxConnections } = options;
753
- const guardMaxConnections = guardBoundedAccess(options.getConnection, {
843
+ const allConnections = /* @__PURE__ */ new Set();
844
+ const getTrackedConnection = async () => {
845
+ const connection = await options.getConnection();
846
+ allConnections.add(connection);
847
+ return connection;
848
+ };
849
+ const guardMaxConnections = guardBoundedAccess(getTrackedConnection, {
754
850
  maxResources: maxConnections,
755
851
  reuseResources: true
756
852
  });
757
853
  let closed = false;
854
+ const closedError = () => /* @__PURE__ */ new Error("Bounded connection pool has been closed");
855
+ const ensureOpen = () => {
856
+ if (closed) throw closedError();
857
+ };
858
+ const closeAllConnections = async () => {
859
+ const connections = [...allConnections];
860
+ allConnections.clear();
861
+ await Promise.all(connections.map((conn) => conn.close()));
862
+ };
758
863
  const executeWithPooling = async (operation) => {
864
+ ensureOpen();
759
865
  const conn = await guardMaxConnections.acquire();
760
866
  try {
761
867
  return await operation(conn);
@@ -766,6 +872,7 @@ const createBoundedConnectionPool = (options) => {
766
872
  return {
767
873
  driverType,
768
874
  connection: async () => {
875
+ ensureOpen();
769
876
  const conn = await guardMaxConnections.acquire();
770
877
  return wrapPooledConnection(conn, () => Promise.resolve(guardMaxConnections.release(conn)));
771
878
  },
@@ -776,11 +883,20 @@ const createBoundedConnectionPool = (options) => {
776
883
  batchCommand: (sqls, opts) => executeWithPooling((c) => c.execute.batchCommand(sqls, opts))
777
884
  },
778
885
  withConnection: executeWithPooling,
779
- ...transactionFactoryWithAsyncAmbientConnection(driverType, guardMaxConnections.acquire, guardMaxConnections.release),
780
- close: async () => {
886
+ transaction: (transactionOptions) => {
887
+ ensureOpen();
888
+ return transactionFactoryWithAsyncAmbientConnection(driverType, guardMaxConnections.acquire, guardMaxConnections.release).transaction(transactionOptions);
889
+ },
890
+ withTransaction: (handle, transactionOptions) => executeWithPooling((conn) => {
891
+ const withTx = conn.withTransaction;
892
+ return withTx(handle, transactionOptions);
893
+ }),
894
+ close: async (closeOptions) => {
781
895
  if (closed) return;
782
896
  closed = true;
897
+ if (!closeOptions?.force) await guardMaxConnections.waitForIdle();
783
898
  await guardMaxConnections.stop({ force: true });
899
+ await closeAllConnections();
784
900
  }
785
901
  };
786
902
  };
@@ -2509,6 +2625,7 @@ exports.createSingletonConnectionPool = createSingletonConnectionPool;
2509
2625
  exports.createTransientConnection = createTransientConnection;
2510
2626
  exports.databaseSchemaComponent = databaseSchemaComponent;
2511
2627
  exports.databaseSchemaSchemaComponent = databaseSchemaSchemaComponent;
2628
+ exports.databaseTransaction = databaseTransaction;
2512
2629
  exports.defaultDatabaseLockOptions = defaultDatabaseLockOptions;
2513
2630
  exports.defaultProcessorsRegistry = defaultProcessorsRegistry;
2514
2631
  exports.describeSQL = describeSQL;
@@ -2574,4 +2691,5 @@ exports.transactionFactoryWithAmbientConnection = transactionFactoryWithAmbientC
2574
2691
  exports.transactionFactoryWithAsyncAmbientConnection = transactionFactoryWithAsyncAmbientConnection;
2575
2692
  exports.transactionFactoryWithDbClient = transactionFactoryWithDbClient;
2576
2693
  exports.transactionFactoryWithNewConnection = transactionFactoryWithNewConnection;
2694
+ exports.transactionNestingCounter = transactionNestingCounter;
2577
2695
  //# sourceMappingURL=index.cjs.map