@lokalise/prisma-utils 7.0.1 → 7.1.0

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/README.md CHANGED
@@ -143,6 +143,38 @@ errors, providing a robust and efficient way to ensure your transactions succeed
143
143
  By automatically retrying failed transactions with an intelligent backoff strategy, you can significantly improve the
144
144
  reliability and performance of your application when using distributed databases.
145
145
 
146
+ ### Retry On Serialization Failure
147
+
148
+ Retries a single statement while it fails with a serialization failure, without opening a transaction around it.
149
+
150
+ ```typescript
151
+ import { retryOnSerializationFailure } from '@lokalise/prisma-utils'
152
+
153
+ const rows = await retryOnSerializationFailure(() =>
154
+ prisma.$queryRaw<Row[]>`SELECT ... FROM "segment" WHERE "project_id" = ${projectId}`,
155
+ )
156
+ ```
157
+
158
+ Retries when the error is Prisma's `P2034` or a CockroachDB transaction retry error (SQLSTATE `40001`), in both the query-engine and the driver-adapter shape. Anything else is rethrown immediately, and the last error is rethrown once the retries are exhausted.
159
+
160
+ #### Options
161
+
162
+ ```typescript
163
+ const rows = await retryOnSerializationFailure(query, {
164
+ retriesAllowed: 2, // Default: 2 (total 3 attempts)
165
+ baseRetryDelayMs: 100, // Default: 100ms
166
+ maxRetryDelayMs: 30000, // Default: 30s
167
+ })
168
+ ```
169
+
170
+ The backoff is the same as `prismaTransaction`'s: `2^(retry-1) × baseRetryDelayMs`, capped at `maxRetryDelayMs`.
171
+
172
+ #### When To Use It Instead Of `prismaTransaction`
173
+
174
+ For a single read, wrapping the statement in an explicit transaction costs a BEGIN/COMMIT round trip and takes away CockroachDB's own ability to refresh and retry the implicit single-statement transaction server-side, which it can no longer do once the client has opened the transaction.
175
+
176
+ It is only safe around a **single read-only statement**. A write, or a statement that runs inside an interactive transaction, has to be retried at the transaction boundary (`prismaTransaction`) instead — replaying it alone would either duplicate its effects or re-run it against an aborted transaction.
177
+
146
178
  ### Prisma Bulk Update
147
179
 
148
180
  Updates many rows in a single atomic SQL statement (`UPDATE ... FROM (VALUES ...)`), instead of issuing one `UPDATE` per row. Because it is a single statement, it either fully applies or fully rolls back, so no surrounding transaction is required. Works on both CockroachDB and PostgreSQL.
@@ -2,6 +2,11 @@ import type { PrismaClientKnownRequestError } from '@prisma/client/runtime/clien
2
2
  /**
3
3
  * Check if the error is a CockroachDB transaction retry error
4
4
  *
5
+ * Two shapes carry the SQLSTATE, depending on how the query reached the database:
6
+ * - the Rust query engine reports it flat, as `meta.code`
7
+ * - a driver adapter (e.g. `@prisma/adapter-pg`) reports a `P2010` whose SQLSTATE is nested
8
+ * under `meta.driverAdapterError.cause.originalCode`, with no `meta.code` at all
9
+ *
5
10
  * @param error
6
11
  */
7
12
  export declare const isCockroachDBRetryTransaction: (error: PrismaClientKnownRequestError) => boolean;
@@ -4,15 +4,29 @@
4
4
  * All transaction retry errors use the SQLSTATE error code 40001
5
5
  */
6
6
  const COCKROACHDB_RETRY_TRANSACTION_CODE = '40001';
7
+ const isRecord = (value) => typeof value === 'object' && value !== null;
7
8
  /**
8
9
  * Check if the error is a CockroachDB transaction retry error
9
10
  *
11
+ * Two shapes carry the SQLSTATE, depending on how the query reached the database:
12
+ * - the Rust query engine reports it flat, as `meta.code`
13
+ * - a driver adapter (e.g. `@prisma/adapter-pg`) reports a `P2010` whose SQLSTATE is nested
14
+ * under `meta.driverAdapterError.cause.originalCode`, with no `meta.code` at all
15
+ *
10
16
  * @param error
11
17
  */
12
18
  export const isCockroachDBRetryTransaction = (error) => {
13
19
  const meta = error.meta;
14
- if (!meta)
20
+ if (!isRecord(meta))
21
+ return false;
22
+ if (meta.code === COCKROACHDB_RETRY_TRANSACTION_CODE)
23
+ return true;
24
+ const driverAdapterError = meta.driverAdapterError;
25
+ if (!isRecord(driverAdapterError))
26
+ return false;
27
+ const cause = driverAdapterError.cause;
28
+ if (!isRecord(cause))
15
29
  return false;
16
- return meta.code === COCKROACHDB_RETRY_TRANSACTION_CODE;
30
+ return cause.originalCode === COCKROACHDB_RETRY_TRANSACTION_CODE;
17
31
  };
18
32
  //# sourceMappingURL=cockroachdbError.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cockroachdbError.js","sourceRoot":"","sources":["../../src/errors/cockroachdbError.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,kCAAkC,GAAG,OAAO,CAAA;AAElD;;;;GAIG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,KAAoC,EAAW,EAAE;IAC7F,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IACvB,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IAEvB,OAAO,IAAI,CAAC,IAAI,KAAK,kCAAkC,CAAA;AACzD,CAAC,CAAA"}
1
+ {"version":3,"file":"cockroachdbError.js","sourceRoot":"","sources":["../../src/errors/cockroachdbError.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,kCAAkC,GAAG,OAAO,CAAA;AAElD,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAA;AAE7C;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,KAAoC,EAAW,EAAE;IAC7F,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IACvB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAEjC,IAAI,IAAI,CAAC,IAAI,KAAK,kCAAkC;QAAE,OAAO,IAAI,CAAA;IAEjE,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,CAAA;IAClD,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC;QAAE,OAAO,KAAK,CAAA;IAE/C,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAA;IACtC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAElC,OAAO,KAAK,CAAC,YAAY,KAAK,kCAAkC,CAAA;AAClE,CAAC,CAAA"}
@@ -1 +1,2 @@
1
+ export * from './cockroachdbError.ts';
1
2
  export * from './prismaError.ts';
@@ -1,2 +1,3 @@
1
+ export * from './cockroachdbError.js';
1
2
  export * from './prismaError.js';
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA"}
@@ -8,6 +8,13 @@ import type { PrismaClientKnownRequestError } from '@prisma/client/runtime/clien
8
8
  */
9
9
  export declare const isPrismaClientKnownRequestError: (error: unknown) => error is PrismaClientKnownRequestError;
10
10
  export declare const isPrismaTransactionClosedError: (error: PrismaClientKnownRequestError) => boolean;
11
+ /**
12
+ * Check if the error is a serialization failure, and therefore safe to retry.
13
+ *
14
+ * Covers both Prisma's own `P2034` and the SQLSTATE 40001 reported by CockroachDB, whose shape
15
+ * depends on whether the query went through the Rust query engine or a driver adapter.
16
+ */
17
+ export declare const isSerializationFailure: (error: unknown) => boolean;
11
18
  /**
12
19
  * Prisma error code P2025 indicates that the operation failed because it depends on one or more
13
20
  * records that were required but not found
@@ -1,4 +1,5 @@
1
1
  import { isError } from '@lokalise/node-core';
2
+ import { isCockroachDBRetryTransaction } from './cockroachdbError.js';
2
3
  /**
3
4
  * What is checked?
4
5
  * 1. error is defined and not null
@@ -13,6 +14,14 @@ export const isPrismaClientKnownRequestError = (error) => !!error &&
13
14
  error.code.startsWith('P');
14
15
  export const isPrismaTransactionClosedError = (error) => error.code === PRISMA_TRANSACTION_ERROR &&
15
16
  error.message.toLowerCase().includes('transaction already closed');
17
+ /**
18
+ * Check if the error is a serialization failure, and therefore safe to retry.
19
+ *
20
+ * Covers both Prisma's own `P2034` and the SQLSTATE 40001 reported by CockroachDB, whose shape
21
+ * depends on whether the query went through the Rust query engine or a driver adapter.
22
+ */
23
+ export const isSerializationFailure = (error) => isPrismaClientKnownRequestError(error) &&
24
+ (error.code === PRISMA_SERIALIZATION_ERROR || isCockroachDBRetryTransaction(error));
16
25
  /**
17
26
  * Prisma error code P2025 indicates that the operation failed because it depends on one or more
18
27
  * records that were required but not found
@@ -1 +1 @@
1
- {"version":3,"file":"prismaError.js","sourceRoot":"","sources":["../../src/errors/prismaError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAG7C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAC7C,KAAc,EAC0B,EAAE,CAC1C,CAAC,CAAC,KAAK;IACP,OAAO,CAAC,KAAK,CAAC;IACd,MAAM,IAAI,KAAK;IACf,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;IAC9B,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;AAE5B,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,KAAoC,EAAW,EAAE,CAC9F,KAAK,CAAC,IAAI,KAAK,wBAAwB;IACvC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAA;AAEpE;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAA;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,OAAO,CAAA;AAEjD;;;GAGG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,OAAO,CAAA;AAErD;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAA;AAE/C;;GAEG;AACH,MAAM,CAAC,MAAM,qCAAqC,GAAG,OAAO,CAAA"}
1
+ {"version":3,"file":"prismaError.js","sourceRoot":"","sources":["../../src/errors/prismaError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,OAAO,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAA;AAErE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAC7C,KAAc,EAC0B,EAAE,CAC1C,CAAC,CAAC,KAAK;IACP,OAAO,CAAC,KAAK,CAAC;IACd,MAAM,IAAI,KAAK;IACf,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;IAC9B,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;AAE5B,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,KAAoC,EAAW,EAAE,CAC9F,KAAK,CAAC,IAAI,KAAK,wBAAwB;IACvC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAA;AAEpE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,KAAc,EAAW,EAAE,CAChE,+BAA+B,CAAC,KAAK,CAAC;IACtC,CAAC,KAAK,CAAC,IAAI,KAAK,0BAA0B,IAAI,6BAA6B,CAAC,KAAK,CAAC,CAAC,CAAA;AAErF;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAA;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,OAAO,CAAA;AAEjD;;;GAGG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,OAAO,CAAA;AAErD;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAA;AAE/C;;GAEG;AACH,MAAM,CAAC,MAAM,qCAAqC,GAAG,OAAO,CAAA"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './bulk-update/index.ts';
2
2
  export * from './errors/index.ts';
3
3
  export * from './factory/index.ts';
4
+ export * from './retry/index.ts';
4
5
  export * from './transaction/index.ts';
5
6
  export type * from './types.ts';
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './bulk-update/index.js';
2
2
  export * from './errors/index.js';
3
3
  export * from './factory/index.js';
4
+ export * from './retry/index.js';
4
5
  export * from './transaction/index.js';
5
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAA;AACtC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,wBAAwB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAA;AACtC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,wBAAwB,CAAA"}
@@ -0,0 +1 @@
1
+ export declare const calculateRetryDelay: (retries: number, baseRetryDelayMs: number, maxDelayMs: number) => number;
@@ -0,0 +1,6 @@
1
+ export const calculateRetryDelay = (retries, baseRetryDelayMs, maxDelayMs) => {
2
+ // exponential backoff -> 2^(retry-1) * baseRetryDelayMs
3
+ const expDelay = Math.pow(2, retries - 1) * baseRetryDelayMs;
4
+ return Math.min(expDelay, maxDelayMs);
5
+ };
6
+ //# sourceMappingURL=calculateRetryDelay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calculateRetryDelay.js","sourceRoot":"","sources":["../../src/retry/calculateRetryDelay.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,OAAe,EACf,gBAAwB,EACxB,UAAkB,EACV,EAAE;IACV,wDAAwD;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,GAAG,gBAAgB,CAAA;IAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;AACvC,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { retryOnSerializationFailure } from './retryOnSerializationFailure.ts';
2
+ export type * from './types.ts';
@@ -0,0 +1,2 @@
1
+ export { retryOnSerializationFailure } from './retryOnSerializationFailure.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/retry/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAA"}
@@ -0,0 +1,20 @@
1
+ import type { RetryOnSerializationFailureOptions } from './types.ts';
2
+ /**
3
+ * Run a single statement, retrying it with exponential backoff while it fails with a
4
+ * serialization failure. Any other error is rethrown immediately, and the last serialization
5
+ * failure is rethrown once the retries are exhausted.
6
+ *
7
+ * Use this instead of {@link prismaTransaction} when the statement is a single read: wrapping one
8
+ * statement in an explicit transaction costs a BEGIN/COMMIT round trip and takes away
9
+ * CockroachDB's own ability to refresh and retry the implicit single-statement transaction
10
+ * server-side, which it can no longer do once the client has opened the transaction.
11
+ *
12
+ * Only safe around a single read-only statement. A write, or a statement that runs inside an
13
+ * interactive transaction, has to be retried at the transaction boundary instead - replaying it
14
+ * alone would either duplicate its effects or re-run it against an aborted transaction.
15
+ *
16
+ * @param {() => Promise<T>} query function issuing the statement, called once per attempt
17
+ * @param {RetryOnSerializationFailureOptions} options retry configuration
18
+ * @return {Promise<T>}
19
+ */
20
+ export declare const retryOnSerializationFailure: <T>(query: () => Promise<T>, options?: RetryOnSerializationFailureOptions) => Promise<T>;
@@ -0,0 +1,43 @@
1
+ import { setTimeout } from 'node:timers/promises';
2
+ import { isSerializationFailure } from '../errors/index.js';
3
+ import { calculateRetryDelay } from './calculateRetryDelay.js';
4
+ const DEFAULT_OPTIONS = {
5
+ retriesAllowed: 2, // first try + 2 retries = 3 tries
6
+ baseRetryDelayMs: 100,
7
+ maxRetryDelayMs: 30000, // 30s
8
+ };
9
+ /**
10
+ * Run a single statement, retrying it with exponential backoff while it fails with a
11
+ * serialization failure. Any other error is rethrown immediately, and the last serialization
12
+ * failure is rethrown once the retries are exhausted.
13
+ *
14
+ * Use this instead of {@link prismaTransaction} when the statement is a single read: wrapping one
15
+ * statement in an explicit transaction costs a BEGIN/COMMIT round trip and takes away
16
+ * CockroachDB's own ability to refresh and retry the implicit single-statement transaction
17
+ * server-side, which it can no longer do once the client has opened the transaction.
18
+ *
19
+ * Only safe around a single read-only statement. A write, or a statement that runs inside an
20
+ * interactive transaction, has to be retried at the transaction boundary instead - replaying it
21
+ * alone would either duplicate its effects or re-run it against an aborted transaction.
22
+ *
23
+ * @param {() => Promise<T>} query function issuing the statement, called once per attempt
24
+ * @param {RetryOnSerializationFailureOptions} options retry configuration
25
+ * @return {Promise<T>}
26
+ */
27
+ export const retryOnSerializationFailure = async (query, options) => {
28
+ // resolved per option, as spreading `options` would let an explicit `undefined` erase a default
29
+ const retriesAllowed = options?.retriesAllowed ?? DEFAULT_OPTIONS.retriesAllowed;
30
+ const baseRetryDelayMs = options?.baseRetryDelayMs ?? DEFAULT_OPTIONS.baseRetryDelayMs;
31
+ const maxRetryDelayMs = options?.maxRetryDelayMs ?? DEFAULT_OPTIONS.maxRetryDelayMs;
32
+ for (let retries = 0;; retries++) {
33
+ try {
34
+ return await query();
35
+ }
36
+ catch (error) {
37
+ if (retries >= retriesAllowed || !isSerializationFailure(error))
38
+ throw error;
39
+ await setTimeout(calculateRetryDelay(retries + 1, baseRetryDelayMs, maxRetryDelayMs));
40
+ }
41
+ }
42
+ };
43
+ //# sourceMappingURL=retryOnSerializationFailure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retryOnSerializationFailure.js","sourceRoot":"","sources":["../../src/retry/retryOnSerializationFailure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAG9D,MAAM,eAAe,GAAG;IACtB,cAAc,EAAE,CAAC,EAAE,kCAAkC;IACrD,gBAAgB,EAAE,GAAG;IACrB,eAAe,EAAE,KAAK,EAAE,MAAM;CACwB,CAAA;AAExD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,EAC9C,KAAuB,EACvB,OAA4C,EAChC,EAAE;IACd,gGAAgG;IAChG,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,eAAe,CAAC,cAAc,CAAA;IAChF,MAAM,gBAAgB,GAAG,OAAO,EAAE,gBAAgB,IAAI,eAAe,CAAC,gBAAgB,CAAA;IACtF,MAAM,eAAe,GAAG,OAAO,EAAE,eAAe,IAAI,eAAe,CAAC,eAAe,CAAA;IAEnF,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,EAAE,CAAA;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAAO,IAAI,cAAc,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAA;YAC5E,MAAM,UAAU,CAAC,mBAAmB,CAAC,OAAO,GAAG,CAAC,EAAE,gBAAgB,EAAE,eAAe,CAAC,CAAC,CAAA;QACvF,CAAC;IACH,CAAC;AACH,CAAC,CAAA"}
@@ -0,0 +1,5 @@
1
+ export type RetryOnSerializationFailureOptions = {
2
+ retriesAllowed?: number;
3
+ baseRetryDelayMs?: number;
4
+ maxRetryDelayMs?: number;
5
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/retry/types.ts"],"names":[],"mappings":""}
@@ -2,6 +2,7 @@ import { setTimeout } from 'node:timers/promises';
2
2
  import { deepClone } from '@lokalise/node-core';
3
3
  import { isCockroachDBRetryTransaction } from '../errors/cockroachdbError.js';
4
4
  import { isPrismaClientKnownRequestError, isPrismaTransactionClosedError, PRISMA_SERIALIZATION_ERROR, PRISMA_SERVER_CLOSED_CONNECTION_ERROR, PRISMA_TRANSACTION_ERROR, } from '../errors/index.js';
5
+ import { calculateRetryDelay } from '../retry/calculateRetryDelay.js';
5
6
  const DEFAULT_OPTIONS = {
6
7
  retriesAllowed: 2, // first try + 2 retries = 3 tries
7
8
  baseRetryDelayMs: 100,
@@ -51,11 +52,6 @@ const executeTransactionTry = async (prisma, arg, options) => {
51
52
  return { error };
52
53
  }
53
54
  };
54
- const calculateRetryDelay = (retries, baseRetryDelayMs, maxDelayMs) => {
55
- // exponential backoff -> 2^(retry-1) * baseRetryDelayMs
56
- const expDelay = Math.pow(2, retries - 1) * baseRetryDelayMs;
57
- return Math.min(expDelay, maxDelayMs);
58
- };
59
55
  const PrismaCodesToRetry = [
60
56
  PRISMA_SERIALIZATION_ERROR,
61
57
  PRISMA_SERVER_CLOSED_CONNECTION_ERROR,
@@ -1 +1 @@
1
- {"version":3,"file":"prismaTransaction.js","sourceRoot":"","sources":["../../src/transaction/prismaTransaction.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prismaTransaction.js","sourceRoot":"","sources":["../../src/transaction/prismaTransaction.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokalise/prisma-utils",
3
- "version": "7.0.1",
3
+ "version": "7.1.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "files": [
@@ -27,10 +27,10 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@biomejs/biome": "^2.5.12",
30
- "dotenv": "^17.0.0",
31
- "@lokalise/biome-config": "^3.1.1",
30
+ "dotenv": "^18.0.8",
31
+ "@lokalise/biome-config": "^4.0.0",
32
32
  "@lokalise/tsconfig": "^5.0.0",
33
- "@prisma/adapter-pg": "^7.4.0",
33
+ "@prisma/adapter-pg": "^7.10.1",
34
34
  "@prisma/client": "^7.10.0",
35
35
  "@vitest/coverage-v8": "^4.1.11",
36
36
  "cross-env": "^10.0.0",