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

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/sqlite3.cjs CHANGED
@@ -273,6 +273,65 @@ const executeInAmbientConnection = async (handle, options) => {
273
273
 
274
274
  //#endregion
275
275
  //#region src/core/connections/transaction.ts
276
+ const transactionNestingCounter = () => {
277
+ let transactionLevel = 0;
278
+ return {
279
+ reset: () => {
280
+ transactionLevel = 0;
281
+ },
282
+ increment: () => {
283
+ transactionLevel++;
284
+ },
285
+ decrement: () => {
286
+ transactionLevel--;
287
+ if (transactionLevel < 0) throw new Error("Transaction level is out of bounds");
288
+ },
289
+ get level() {
290
+ return transactionLevel;
291
+ }
292
+ };
293
+ };
294
+ const databaseTransaction = (backend, options) => {
295
+ const allowNestedTransactions = options?.allowNestedTransactions ?? false;
296
+ const useSavepoints = options?.useSavepoints ?? false;
297
+ const counter = transactionNestingCounter();
298
+ let hasBegun = false;
299
+ return {
300
+ begin: async () => {
301
+ if (!allowNestedTransactions && hasBegun) throw new InvalidOperationError("Cannot start a nested transaction: allowNestedTransactions is false. Set transactionOptions: { allowNestedTransactions: true } on your pool or connection.");
302
+ if (allowNestedTransactions) {
303
+ if (counter.level >= 1) {
304
+ counter.increment();
305
+ if (useSavepoints && backend.savepoint) await backend.savepoint(counter.level);
306
+ return;
307
+ }
308
+ counter.increment();
309
+ }
310
+ hasBegun = true;
311
+ await backend.begin();
312
+ },
313
+ commit: async () => {
314
+ if (allowNestedTransactions && counter.level > 1) {
315
+ if (useSavepoints && backend.releaseSavepoint) await backend.releaseSavepoint(counter.level);
316
+ counter.decrement();
317
+ return;
318
+ }
319
+ if (allowNestedTransactions) counter.reset();
320
+ hasBegun = false;
321
+ await backend.commit();
322
+ },
323
+ rollback: async (error) => {
324
+ if (allowNestedTransactions && counter.level > 1) {
325
+ if (useSavepoints && backend.rollbackToSavepoint) await backend.rollbackToSavepoint(counter.level);
326
+ counter.decrement();
327
+ return;
328
+ }
329
+ if (allowNestedTransactions) counter.reset();
330
+ hasBegun = false;
331
+ await backend.rollback(error);
332
+ }
333
+ };
334
+ };
276
335
  const toTransactionResult = (transactionResult) => transactionResult !== void 0 && transactionResult !== null && typeof transactionResult === "object" && "success" in transactionResult ? transactionResult : {
277
336
  success: true,
278
337
  result: transactionResult
@@ -666,33 +725,80 @@ const createAmbientConnectionPool = (options) => {
666
725
  const createSingletonConnectionPool = (options) => {
667
726
  const { driverType, getConnection } = options;
668
727
  let connectionPromise = null;
728
+ let closed = false;
729
+ const activeOperations = /* @__PURE__ */ new Set();
730
+ const closedError = () => /* @__PURE__ */ new Error("Singleton connection pool has been closed");
731
+ const run = async (operation) => {
732
+ if (closed) throw closedError();
733
+ const activeOperation = operation();
734
+ activeOperations.add(activeOperation);
735
+ try {
736
+ return await activeOperation;
737
+ } finally {
738
+ activeOperations.delete(activeOperation);
739
+ }
740
+ };
669
741
  const getExistingOrNewConnection = () => {
670
742
  if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection());
671
743
  return connectionPromise;
672
744
  };
745
+ const innerTransactionFactory = transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection);
673
746
  return {
674
747
  driverType,
675
- connection: () => getExistingOrNewConnection().then((conn) => wrapPooledConnection(conn, () => Promise.resolve())),
676
- execute: sqlExecutorInAmbientConnection({
677
- driverType,
678
- connection: getExistingOrNewConnection
679
- }),
680
- withConnection: (handle, _options) => executeInAmbientConnection(handle, { connection: getExistingOrNewConnection }),
681
- ...transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection),
682
- close: async () => {
748
+ connection: () => run(() => getExistingOrNewConnection().then((conn) => wrapPooledConnection(conn, () => Promise.resolve()))),
749
+ execute: (() => {
750
+ const ambientExecutor = sqlExecutorInAmbientConnection({
751
+ driverType,
752
+ connection: getExistingOrNewConnection
753
+ });
754
+ return {
755
+ query: (sql, opts) => run(() => ambientExecutor.query(sql, opts)),
756
+ batchQuery: (sqls, opts) => run(() => ambientExecutor.batchQuery(sqls, opts)),
757
+ command: (sql, opts) => run(() => ambientExecutor.command(sql, opts)),
758
+ batchCommand: (sqls, opts) => run(() => ambientExecutor.batchCommand(sqls, opts))
759
+ };
760
+ })(),
761
+ withConnection: (handle, _options) => run(() => executeInAmbientConnection(handle, { connection: getExistingOrNewConnection })),
762
+ transaction: (transactionOptions) => {
763
+ if (closed) throw closedError();
764
+ return innerTransactionFactory.transaction(transactionOptions);
765
+ },
766
+ withTransaction: (handle, transactionOptions) => run(() => innerTransactionFactory.withTransaction(handle, transactionOptions)),
767
+ close: async (closeOptions) => {
768
+ if (closed) return;
769
+ closed = true;
770
+ if (!closeOptions?.force) await Promise.allSettled([...activeOperations]);
683
771
  if (!connectionPromise) return;
684
- await (await connectionPromise).close();
772
+ const connection = await connectionPromise;
773
+ connectionPromise = null;
774
+ await connection.close();
685
775
  }
686
776
  };
687
777
  };
688
778
  const createBoundedConnectionPool = (options) => {
689
779
  const { driverType, maxConnections } = options;
690
- const guardMaxConnections = guardBoundedAccess(options.getConnection, {
780
+ const allConnections = /* @__PURE__ */ new Set();
781
+ const getTrackedConnection = async () => {
782
+ const connection = await options.getConnection();
783
+ allConnections.add(connection);
784
+ return connection;
785
+ };
786
+ const guardMaxConnections = guardBoundedAccess(getTrackedConnection, {
691
787
  maxResources: maxConnections,
692
788
  reuseResources: true
693
789
  });
694
790
  let closed = false;
791
+ const closedError = () => /* @__PURE__ */ new Error("Bounded connection pool has been closed");
792
+ const ensureOpen = () => {
793
+ if (closed) throw closedError();
794
+ };
795
+ const closeAllConnections = async () => {
796
+ const connections = [...allConnections];
797
+ allConnections.clear();
798
+ await Promise.all(connections.map((conn) => conn.close()));
799
+ };
695
800
  const executeWithPooling = async (operation) => {
801
+ ensureOpen();
696
802
  const conn = await guardMaxConnections.acquire();
697
803
  try {
698
804
  return await operation(conn);
@@ -703,6 +809,7 @@ const createBoundedConnectionPool = (options) => {
703
809
  return {
704
810
  driverType,
705
811
  connection: async () => {
812
+ ensureOpen();
706
813
  const conn = await guardMaxConnections.acquire();
707
814
  return wrapPooledConnection(conn, () => Promise.resolve(guardMaxConnections.release(conn)));
708
815
  },
@@ -713,11 +820,20 @@ const createBoundedConnectionPool = (options) => {
713
820
  batchCommand: (sqls, opts) => executeWithPooling((c) => c.execute.batchCommand(sqls, opts))
714
821
  },
715
822
  withConnection: executeWithPooling,
716
- ...transactionFactoryWithAsyncAmbientConnection(driverType, guardMaxConnections.acquire, guardMaxConnections.release),
717
- close: async () => {
823
+ transaction: (transactionOptions) => {
824
+ ensureOpen();
825
+ return transactionFactoryWithAsyncAmbientConnection(driverType, guardMaxConnections.acquire, guardMaxConnections.release).transaction(transactionOptions);
826
+ },
827
+ withTransaction: (handle, transactionOptions) => executeWithPooling((conn) => {
828
+ const withTx = conn.withTransaction;
829
+ return withTx(handle, transactionOptions);
830
+ }),
831
+ close: async (closeOptions) => {
718
832
  if (closed) return;
719
833
  closed = true;
834
+ if (!closeOptions?.force) await guardMaxConnections.waitForIdle();
720
835
  await guardMaxConnections.stop({ force: true });
836
+ await closeAllConnections();
721
837
  }
722
838
  };
723
839
  };
@@ -2075,54 +2191,46 @@ const sqliteSQLExecutor = (driverType, serializer, formatter, errorMapper) => ({
2075
2191
  //#endregion
2076
2192
  //#region src/storage/sqlite/core/transactions/index.ts
2077
2193
  const sqliteTransaction = (driverType, connection, allowNestedTransactions, serializer, defaultTransactionMode) => (getClient, options) => {
2078
- const transactionCounter = transactionNestingCounter();
2079
2194
  allowNestedTransactions = options?.allowNestedTransactions ?? allowNestedTransactions;
2080
- let hasBegun = false;
2081
- return {
2082
- connection: connection(),
2083
- driverType,
2084
- begin: async function() {
2195
+ const useSavepoints = options?.useSavepoints ?? false;
2196
+ const tx = databaseTransaction({
2197
+ begin: async () => {
2085
2198
  const client = await getClient;
2086
- if (allowNestedTransactions) {
2087
- if (transactionCounter.level >= 1) {
2088
- transactionCounter.increment();
2089
- if (options?.useSavepoints) await client.command(SQL`SAVEPOINT transaction${SQL.plain(transactionCounter.level.toString())}`);
2090
- return;
2091
- }
2092
- transactionCounter.increment();
2093
- } else if (hasBegun) throw new InvalidOperationError("Cannot start a nested transaction: allowNestedTransactions is false. Set transactionOptions: { allowNestedTransactions: true } on your pool or connection.");
2094
- hasBegun = true;
2095
2199
  const mode = options?.mode ?? defaultTransactionMode ?? "IMMEDIATE";
2096
2200
  await client.command(SQL`BEGIN ${SQL.plain(mode)} TRANSACTION`);
2097
2201
  },
2098
- commit: async function() {
2202
+ commit: async () => {
2099
2203
  const client = await getClient;
2100
- if (allowNestedTransactions && transactionCounter.level > 1) {
2101
- if (options?.useSavepoints) await client.command(SQL`RELEASE transaction${SQL.plain(transactionCounter.level.toString())}`);
2102
- transactionCounter.decrement();
2103
- return;
2104
- }
2105
2204
  try {
2106
- if (allowNestedTransactions) transactionCounter.reset();
2107
- hasBegun = false;
2108
2205
  await client.command(SQL`COMMIT`);
2109
2206
  } finally {
2110
- if (options?.close) await options?.close(client);
2207
+ if (options?.close) await options.close(client);
2111
2208
  }
2112
2209
  },
2113
- rollback: async function(error) {
2210
+ rollback: async (error) => {
2114
2211
  const client = await getClient;
2115
- if (allowNestedTransactions && transactionCounter.level > 1) {
2116
- transactionCounter.decrement();
2117
- return;
2118
- }
2119
2212
  try {
2120
- hasBegun = false;
2121
2213
  await client.command(SQL`ROLLBACK`);
2122
2214
  } finally {
2123
- if (options?.close) await options?.close(client, error);
2215
+ if (options?.close) await options.close(client, error);
2124
2216
  }
2125
2217
  },
2218
+ savepoint: async (level) => {
2219
+ await (await getClient).command(SQL`SAVEPOINT transaction${SQL.plain(level.toString())}`);
2220
+ },
2221
+ releaseSavepoint: async (level) => {
2222
+ await (await getClient).command(SQL`RELEASE transaction${SQL.plain(level.toString())}`);
2223
+ }
2224
+ }, {
2225
+ allowNestedTransactions,
2226
+ useSavepoints
2227
+ });
2228
+ return {
2229
+ connection: connection(),
2230
+ driverType,
2231
+ begin: tx.begin,
2232
+ commit: tx.commit,
2233
+ rollback: tx.rollback,
2126
2234
  execute: sqlExecutor(sqliteSQLExecutor(driverType, serializer), { connect: () => getClient }),
2127
2235
  _transactionOptions: {
2128
2236
  ...options,
@@ -2168,24 +2276,6 @@ const isSQLiteError = (error) => {
2168
2276
  if (error instanceof Error && "code" in error) return true;
2169
2277
  return false;
2170
2278
  };
2171
- const transactionNestingCounter = () => {
2172
- let transactionLevel = 0;
2173
- return {
2174
- reset: () => {
2175
- transactionLevel = 0;
2176
- },
2177
- increment: () => {
2178
- transactionLevel++;
2179
- },
2180
- decrement: () => {
2181
- transactionLevel--;
2182
- if (transactionLevel < 0) throw new Error("Transaction level is out of bounds");
2183
- },
2184
- get level() {
2185
- return transactionLevel;
2186
- }
2187
- };
2188
- };
2189
2279
  const sqliteAmbientClientConnection = (options) => {
2190
2280
  const { client, driverType, initTransaction, allowNestedTransactions, defaultTransactionMode, serializer, errorMapper } = options;
2191
2281
  return createAmbientConnection({
@@ -2611,7 +2701,9 @@ const sqlite3Connection = (options) => sqliteConnection({
2611
2701
 
2612
2702
  //#endregion
2613
2703
  //#region src/storage/sqlite/sqlite3/pool/singletonPool.ts
2704
+ const DEFAULT_SQLITE_MAX_TASK_IDLE_TIME_MS = 3e4;
2614
2705
  const sqlite3SingletonPool = (options) => {
2706
+ const maxTaskIdleTime = options.maxTaskIdleTime ?? 3e4;
2615
2707
  const inner = createSingletonConnectionPool({
2616
2708
  driverType: options.driverType,
2617
2709
  getConnection: options.getConnection,
@@ -2619,7 +2711,8 @@ const sqlite3SingletonPool = (options) => {
2619
2711
  });
2620
2712
  const taskProcessor = new TaskProcessor({
2621
2713
  maxActiveTasks: 1,
2622
- maxQueueSize: options.maxQueueSize ?? 1e3
2714
+ maxQueueSize: options.maxQueueSize ?? 1e3,
2715
+ maxTaskIdleTime
2623
2716
  });
2624
2717
  const insideWriterTask = new node_async_hooks.AsyncLocalStorage();
2625
2718
  const enqueue = (op) => {
@@ -2644,9 +2737,9 @@ const sqlite3SingletonPool = (options) => {
2644
2737
  command: (sql, commandOptions) => enqueue(() => inner.execute.command(sql, commandOptions)),
2645
2738
  batchCommand: (sqls, commandOptions) => enqueue(() => inner.execute.batchCommand(sqls, commandOptions))
2646
2739
  },
2647
- close: async () => {
2648
- await taskProcessor.stop();
2649
- await inner.close();
2740
+ close: async (closeOptions) => {
2741
+ await taskProcessor.stop({ ...closeOptions?.force ? { force: true } : {} });
2742
+ await inner.close(closeOptions);
2650
2743
  }
2651
2744
  };
2652
2745
  };
@@ -2691,7 +2784,8 @@ const sqliteDualConnectionPool = (options) => {
2691
2784
  };
2692
2785
  const writerPool = sqlite3SingletonPool({
2693
2786
  driverType: options.driverType,
2694
- getConnection: () => wrappedConnectionFactory(false, connectionOptions)
2787
+ getConnection: () => wrappedConnectionFactory(false, connectionOptions),
2788
+ ...options.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: options.maxTaskIdleTime } : {}
2695
2789
  });
2696
2790
  const readerPool = createBoundedConnectionPool({
2697
2791
  driverType: options.driverType,
@@ -2728,18 +2822,26 @@ const sqlite3Pool = (options) => {
2728
2822
  ...opts,
2729
2823
  serializer: options.serializer ?? JSONSerializer
2730
2824
  });
2731
- if (isInMemoryDatabase(options) || "client" in options && Boolean(options.client) || options.singleton === true) return sqlite3SingletonPool({
2825
+ const isSingleton = isInMemoryDatabase(options) || "client" in options && Boolean(options.client) || options.singleton === true;
2826
+ const lifecycleOptions = extractLifecycleOptions(options);
2827
+ if (isSingleton) return sqlite3SingletonPool({
2732
2828
  driverType: SQLite3DriverType,
2733
- getConnection: () => sqliteConnectionFactory(options)
2829
+ getConnection: () => sqliteConnectionFactory(options),
2830
+ ...lifecycleOptions
2734
2831
  });
2735
2832
  const readerPoolSize = options.readerPoolSize;
2736
2833
  return sqliteDualConnectionPool({
2737
2834
  driverType: SQLite3DriverType,
2738
2835
  sqliteConnectionFactory,
2739
2836
  connectionOptions: options,
2740
- ...readerPoolSize !== void 0 ? { readerPoolSize } : {}
2837
+ ...readerPoolSize !== void 0 ? { readerPoolSize } : {},
2838
+ ...lifecycleOptions
2741
2839
  });
2742
2840
  };
2841
+ const extractLifecycleOptions = (options) => {
2842
+ const lifecycle = options;
2843
+ return { ...lifecycle.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: lifecycle.maxTaskIdleTime } : {} };
2844
+ };
2743
2845
  const tryParseConnectionString = (connectionString) => {
2744
2846
  try {
2745
2847
  return SQLiteConnectionString(connectionString);
@@ -2793,6 +2895,5 @@ exports.sqliteSingletonConnectionPool = sqliteSingletonConnectionPool;
2793
2895
  exports.sqliteTransaction = sqliteTransaction;
2794
2896
  exports.tableExists = tableExists;
2795
2897
  exports.toSqlitePoolOptions = toSqlitePoolOptions;
2796
- exports.transactionNestingCounter = transactionNestingCounter;
2797
2898
  exports.useSqlite3DumboDriver = useSqlite3DumboDriver;
2798
2899
  //# sourceMappingURL=sqlite3.cjs.map