@prisma/query-plan-executor 7.10.0-dev.8 → 7.10.0-dev.9

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.
Files changed (2) hide show
  1. package/dist/index.js +114 -55
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -93473,7 +93473,7 @@ __export(index_exports, {
93473
93473
  module.exports = __toCommonJS(index_exports);
93474
93474
 
93475
93475
  // package.json
93476
- var version = "7.10.0-dev.8";
93476
+ var version = "7.10.0-dev.9";
93477
93477
 
93478
93478
  // ../../node_modules/.pnpm/temporal-polyfill@0.3.0/node_modules/temporal-polyfill/chunks/internal.js
93479
93479
  function clampProp(e2, n2, t2, o2, r2) {
@@ -103579,6 +103579,14 @@ var InvalidTransactionIsolationLevelError = class extends TransactionManagerErro
103579
103579
  }
103580
103580
  };
103581
103581
  var MAX_CLOSED_TRANSACTIONS = 100;
103582
+ var CANCEL_ROLLBACK_GRACE_MS = 2e3;
103583
+ function trackStartingTransaction() {
103584
+ let markSettled;
103585
+ const settled = new Promise((resolve) => {
103586
+ markSettled = resolve;
103587
+ });
103588
+ return { abortController: new AbortController(), settled, markSettled };
103589
+ }
103582
103590
  var debug3 = Debug("prisma:client:transactionManager");
103583
103591
  var COMMIT_QUERY = () => ({ sql: "COMMIT", args: [], argTypes: [] });
103584
103592
  var ROLLBACK_QUERY = () => ({ sql: "ROLLBACK", args: [], argTypes: [] });
@@ -103598,6 +103606,9 @@ var TransactionManager = class {
103598
103606
  // List of last closed transactions. Max MAX_CLOSED_TRANSACTIONS entries.
103599
103607
  // Used to provide better error messages than a generic "transaction not found".
103600
103608
  closedTransactions = [];
103609
+ // Transactions that are still being started. Tracked separately so that
103610
+ // `cancelAllTransactions` can reach them: they are not in `transactions` yet.
103611
+ #startingTransactions = /* @__PURE__ */ new Set();
103601
103612
  driverAdapter;
103602
103613
  transactionOptions;
103603
103614
  tracingHelper;
@@ -103661,56 +103672,91 @@ var TransactionManager = class {
103661
103672
  return { id: existing.id };
103662
103673
  });
103663
103674
  }
103664
- const transaction = {
103665
- id: await randomUUID(),
103666
- status: "waiting",
103667
- timer: void 0,
103668
- timeout: options.timeout,
103669
- startedAt: Date.now(),
103670
- transaction: void 0,
103671
- operationQueue: Promise.resolve(),
103672
- depth: 1,
103673
- savepoints: [],
103674
- savepointCounter: 0
103675
- };
103676
- const abortController = new AbortController();
103677
- const startTimer = createTimeoutIfDefined(() => abortController.abort(), options.maxWait);
103678
- startTimer?.unref?.();
103679
- const startTransactionPromise = this.driverAdapter.startTransaction(options.isolationLevel).catch(rethrowAsUserFacing);
103680
- transaction.transaction = await Promise.race([
103681
- startTransactionPromise.finally(() => clearTimeout(startTimer)),
103682
- once(abortController.signal, "abort").then(() => void 0)
103683
- ]);
103684
- this.transactions.set(transaction.id, transaction);
103685
- switch (transaction.status) {
103686
- case "waiting":
103687
- if (abortController.signal.aborted) {
103688
- void startTransactionPromise.then(async (tx) => {
103689
- if (tx.options.usePhantomQuery) {
103690
- await tx.rollback();
103691
- } else {
103692
- try {
103693
- await tx.executeRaw(ROLLBACK_QUERY());
103694
- } finally {
103695
- await tx.rollback();
103696
- }
103697
- }
103698
- }).catch((e2) => debug3("error in discarded transaction:", e2));
103699
- await this.#closeTransaction(transaction, "timed_out");
103700
- throw new TransactionStartTimeoutError();
103701
- }
103702
- transaction.status = "running";
103703
- transaction.timer = this.#startTransactionTimeout(transaction.id, options.timeout);
103704
- return { id: transaction.id };
103705
- case "timed_out":
103706
- case "running":
103707
- case "committed":
103708
- case "rolled_back":
103709
- throw new TransactionInternalConsistencyError(
103710
- `Transaction in invalid state ${transaction.status} although it just finished startup.`
103711
- );
103712
- default:
103713
- assertNever(transaction["status"], "Unknown transaction status.");
103675
+ const starting = trackStartingTransaction();
103676
+ const { abortController } = starting;
103677
+ this.#startingTransactions.add(starting);
103678
+ let discarding;
103679
+ try {
103680
+ const transaction = {
103681
+ id: await randomUUID(),
103682
+ status: "waiting",
103683
+ timer: void 0,
103684
+ timeout: options.timeout,
103685
+ startedAt: Date.now(),
103686
+ transaction: void 0,
103687
+ operationQueue: Promise.resolve(),
103688
+ depth: 1,
103689
+ savepoints: [],
103690
+ savepointCounter: 0
103691
+ };
103692
+ if (abortController.signal.aborted) {
103693
+ throw new TransactionStartTimeoutError();
103694
+ }
103695
+ const startTimer = createTimeoutIfDefined(() => abortController.abort(), options.maxWait);
103696
+ startTimer?.unref?.();
103697
+ const startTransactionPromise = this.driverAdapter.startTransaction(options.isolationLevel).catch(rethrowAsUserFacing);
103698
+ transaction.transaction = await Promise.race([
103699
+ startTransactionPromise.finally(() => clearTimeout(startTimer)),
103700
+ once(abortController.signal, "abort").then(() => void 0)
103701
+ ]);
103702
+ this.transactions.set(transaction.id, transaction);
103703
+ switch (transaction.status) {
103704
+ case "waiting":
103705
+ if (abortController.signal.aborted) {
103706
+ transaction.transaction = void 0;
103707
+ discarding = this.#discardStartedTransaction(startTransactionPromise);
103708
+ await this.#closeTransaction(transaction, "timed_out");
103709
+ throw new TransactionStartTimeoutError();
103710
+ }
103711
+ transaction.status = "running";
103712
+ transaction.startedAt = Date.now();
103713
+ transaction.timer = this.#startTransactionTimeout(transaction.id, options.timeout);
103714
+ return { id: transaction.id };
103715
+ case "timed_out":
103716
+ case "running":
103717
+ case "committed":
103718
+ case "rolled_back":
103719
+ throw new TransactionInternalConsistencyError(
103720
+ `Transaction in invalid state ${transaction.status} although it just finished startup.`
103721
+ );
103722
+ default:
103723
+ return assertNever(transaction["status"], "Unknown transaction status.");
103724
+ }
103725
+ } finally {
103726
+ this.#startingTransactions.delete(starting);
103727
+ if (discarding) {
103728
+ void discarding.finally(starting.markSettled);
103729
+ } else {
103730
+ starting.markSettled();
103731
+ }
103732
+ }
103733
+ }
103734
+ /**
103735
+ * Rolls back a transaction whose start was abandoned, and releases its connection.
103736
+ *
103737
+ * The `startTransaction` promise may still be running in the background. If it eventually
103738
+ * succeeds, we need to roll back and release the connection to avoid leaking it and
103739
+ * exhausting the connection pool. For adapters that don't use phantom queries (e.g. pg/neon),
103740
+ * `rollback()` only releases the connection without sending SQL, so we send an explicit
103741
+ * ROLLBACK first; otherwise the connection returns to the pool mid-transaction because
103742
+ * `BEGIN` already ran on the wire during startup.
103743
+ *
103744
+ * Errors are only logged: the caller has already reported the failure that led here.
103745
+ */
103746
+ async #discardStartedTransaction(startTransactionPromise) {
103747
+ try {
103748
+ const tx = await startTransactionPromise;
103749
+ if (tx.options.usePhantomQuery) {
103750
+ await tx.rollback();
103751
+ } else {
103752
+ try {
103753
+ await tx.executeRaw(ROLLBACK_QUERY());
103754
+ } finally {
103755
+ await tx.rollback();
103756
+ }
103757
+ }
103758
+ } catch (e2) {
103759
+ debug3("error in discarded transaction:", e2);
103714
103760
  }
103715
103761
  }
103716
103762
  async commitTransaction(transactionId) {
@@ -103801,16 +103847,21 @@ var TransactionManager = class {
103801
103847
  return transaction;
103802
103848
  }
103803
103849
  async cancelAllTransactions() {
103804
- await Promise.allSettled(
103805
- [...this.transactions.values()].map(
103850
+ const starting = [...this.#startingTransactions];
103851
+ for (const { abortController } of starting) {
103852
+ abortController.abort();
103853
+ }
103854
+ await Promise.allSettled([
103855
+ ...[...this.transactions.values()].map(
103806
103856
  (tx) => this.#runSerialized(tx, async () => {
103807
103857
  const current = this.transactions.get(tx.id);
103808
103858
  if (current) {
103809
103859
  await this.#closeTransaction(current, "rolled_back");
103810
103860
  }
103811
103861
  })
103812
- )
103813
- );
103862
+ ),
103863
+ ...starting.map(({ settled }) => settleWithin(settled, CANCEL_ROLLBACK_GRACE_MS))
103864
+ ]);
103814
103865
  }
103815
103866
  #nextSavepointName(transaction) {
103816
103867
  return `prisma_sp_${transaction.savepointCounter++}`;
@@ -103963,6 +104014,14 @@ var TransactionManager = class {
103963
104014
  function createTimeoutIfDefined(cb, ms) {
103964
104015
  return ms !== void 0 ? setTimeout(cb, ms) : void 0;
103965
104016
  }
104017
+ function settleWithin(promise2, timeout) {
104018
+ let timer;
104019
+ const deadline = new Promise((resolve) => {
104020
+ timer = setTimeout(resolve, timeout);
104021
+ timer?.unref?.();
104022
+ });
104023
+ return Promise.race([promise2, deadline]).finally(() => clearTimeout(timer));
104024
+ }
103966
104025
 
103967
104026
  // ../../node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/compose.js
103968
104027
  var compose = (middleware, onError, onNotFound) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/query-plan-executor",
3
- "version": "7.10.0-dev.8",
3
+ "version": "7.10.0-dev.9",
4
4
  "description": "This package is intended for Prisma's internal use",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -19,11 +19,11 @@
19
19
  "hono": "^4.12.23",
20
20
  "temporal-polyfill": "0.3.0",
21
21
  "zod": "4.1.3",
22
- "@prisma/adapter-pg": "7.10.0-dev.8",
23
- "@prisma/adapter-mariadb": "7.10.0-dev.8",
24
- "@prisma/client-engine-runtime": "7.10.0-dev.8",
25
- "@prisma/adapter-mssql": "7.10.0-dev.8",
26
- "@prisma/driver-adapter-utils": "7.10.0-dev.8"
22
+ "@prisma/adapter-pg": "7.10.0-dev.9",
23
+ "@prisma/adapter-mssql": "7.10.0-dev.9",
24
+ "@prisma/client-engine-runtime": "7.10.0-dev.9",
25
+ "@prisma/driver-adapter-utils": "7.10.0-dev.9",
26
+ "@prisma/adapter-mariadb": "7.10.0-dev.9"
27
27
  },
28
28
  "files": [
29
29
  "dist"