@tc-libs/retry-policy 2.0.1

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 ADDED
@@ -0,0 +1,11 @@
1
+ # retry-policy
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build retry-policy` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test retry-policy` to execute the unit tests via [Jest](https://jestjs.io).
package/package.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "@tc-libs/retry-policy",
3
+ "version": "2.0.1",
4
+ "dependencies": {
5
+ "@nestjs/common": "^11.0.12",
6
+ "mongodb": "6.15.0",
7
+ "tslib": "^2.8.1"
8
+ },
9
+ "type": "commonjs",
10
+ "main": "./src/index.js",
11
+ "typings": "./src/index.d.ts",
12
+ "types": "./src/index.d.ts"
13
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './retry-policy.module';
2
+ export * from './retry-policy.mongo.service';
package/src/index.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./retry-policy.module"), exports);
5
+ tslib_1.__exportStar(require("./retry-policy.mongo.service"), exports);
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/retry-policy/src/index.ts"],"names":[],"mappings":";;;AAAA,gEAAsC;AACtC,uEAA6C"}
@@ -0,0 +1,2 @@
1
+ export declare class RetryPolicyModule {
2
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RetryPolicyModule = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const retry_policy_mongo_service_1 = require("./retry-policy.mongo.service");
7
+ let RetryPolicyModule = class RetryPolicyModule {
8
+ };
9
+ exports.RetryPolicyModule = RetryPolicyModule;
10
+ exports.RetryPolicyModule = RetryPolicyModule = tslib_1.__decorate([
11
+ (0, common_1.Global)(),
12
+ (0, common_1.Module)({
13
+ providers: [retry_policy_mongo_service_1.RetryPolicyMongoService],
14
+ exports: [retry_policy_mongo_service_1.RetryPolicyMongoService],
15
+ })
16
+ ], RetryPolicyModule);
17
+ //# sourceMappingURL=retry-policy.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-policy.module.js","sourceRoot":"","sources":["../../../../packages/retry-policy/src/retry-policy.module.ts"],"names":[],"mappings":";;;;AAAA,2CAAgD;AAChD,6EAAuE;AAOhE,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAAG,CAAA;AAApB,8CAAiB;4BAAjB,iBAAiB;IAL7B,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE,CAAC,oDAAuB,CAAC;QACpC,OAAO,EAAE,CAAC,oDAAuB,CAAC;KACnC,CAAC;GACW,iBAAiB,CAAG"}
@@ -0,0 +1,13 @@
1
+ import type { ClientSession } from 'mongodb';
2
+ export type TxRetryOptions = {
3
+ maxRetries?: number;
4
+ baseDelayMs?: number;
5
+ maxDelayMs?: number;
6
+ jitter?: boolean;
7
+ };
8
+ export declare class RetryPolicyMongoService {
9
+ runInTransactionWithRetry<T>(session: ClientSession, work: (session: ClientSession) => Promise<T>, opts?: TxRetryOptions): Promise<T>;
10
+ private _isRetryableMongoError;
11
+ private computeDelay;
12
+ private sleep;
13
+ }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RetryPolicyMongoService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const mongodb_1 = require("mongodb");
7
+ let RetryPolicyMongoService = class RetryPolicyMongoService {
8
+ async runInTransactionWithRetry(session, work, opts = {}) {
9
+ /**
10
+ * Destructures retry configuration from `opts`, applying sensible defaults.
11
+ *
12
+ * @param opts - Optional configuration object for retry behavior.
13
+ * @param opts.maxRetries - Maximum number of retry attempts (default: 5).
14
+ * @param opts.baseDelayMs - Initial delay between retries in milliseconds (default: 50).
15
+ * @param opts.maxDelayMs - Maximum delay cap in milliseconds to prevent excessive waits (default: 2000).
16
+ * @param opts.jitter - Whether to apply randomized jitter to delays to avoid request bursts (default: true).
17
+ */
18
+ const { maxRetries = 5, baseDelayMs = 50, maxDelayMs = 2000, jitter = true, } = opts;
19
+ let attempt = 0;
20
+ while (true) {
21
+ try {
22
+ // Se qualcuno non l’ha già fatto (guard), la avviamo noi
23
+ if (!session.inTransaction()) {
24
+ await session.startTransaction();
25
+ }
26
+ const result = await work(session);
27
+ // Commit: può fallire con UnknownTransactionCommitResult → retry
28
+ await session.commitTransaction();
29
+ return result;
30
+ }
31
+ catch (err) {
32
+ attempt++;
33
+ // Se siamo in transazione, abort (ignore errori abort)
34
+ if (session.inTransaction()) {
35
+ try {
36
+ await session.abortTransaction();
37
+ }
38
+ catch {
39
+ /* noop */
40
+ }
41
+ }
42
+ if (!this._isRetryableMongoError(err) || attempt > maxRetries) {
43
+ throw err;
44
+ }
45
+ const delay = this.computeDelay(attempt, baseDelayMs, maxDelayMs, jitter);
46
+ await this.sleep(delay);
47
+ // Riparti con una nuova transaction sullo stesso session
48
+ // (startTransaction verrà richiamata all’inizio del loop)
49
+ }
50
+ }
51
+ }
52
+ _isRetryableMongoError(err) {
53
+ const e = err;
54
+ // Driver/Session labels
55
+ const labels = Array.isArray(e?.errorLabels) ? e.errorLabels : [];
56
+ const isWriteConflict = e instanceof mongodb_1.MongoServerError && e.code === 112; // WriteConflict
57
+ const msg = String(e?.message ?? '');
58
+ const looksLikeWriteConflict = msg.includes('Write conflict') || msg.includes('WriteConflict');
59
+ const transientTxn = labels.includes('TransientTransactionError');
60
+ const unknownCommit = labels.includes('UnknownTransactionCommitResult');
61
+ return (isWriteConflict || looksLikeWriteConflict || transientTxn || unknownCommit);
62
+ }
63
+ computeDelay(attempt, base, max, jitter) {
64
+ const raw = Math.min(max, base * Math.pow(2, attempt - 1));
65
+ if (!jitter)
66
+ return raw;
67
+ return Math.floor(Math.random() * raw);
68
+ }
69
+ sleep(ms) {
70
+ return new Promise((res) => setTimeout(res, ms));
71
+ }
72
+ };
73
+ exports.RetryPolicyMongoService = RetryPolicyMongoService;
74
+ exports.RetryPolicyMongoService = RetryPolicyMongoService = tslib_1.__decorate([
75
+ (0, common_1.Injectable)()
76
+ ], RetryPolicyMongoService);
77
+ //# sourceMappingURL=retry-policy.mongo.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-policy.mongo.service.js","sourceRoot":"","sources":["../../../../packages/retry-policy/src/retry-policy.mongo.service.ts"],"names":[],"mappings":";;;;AAAA,2CAA4C;AAE5C,qCAA2C;AAUpC,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IAClC,KAAK,CAAC,yBAAyB,CAC7B,OAAsB,EACtB,IAA4C,EAC5C,OAAuB,EAAE;QAEzB;;;;;;;;WAQG;QACH,MAAM,EACJ,UAAU,GAAG,CAAC,EACd,WAAW,GAAG,EAAE,EAChB,UAAU,GAAG,IAAI,EACjB,MAAM,GAAG,IAAI,GACd,GAAG,IAAI,CAAC;QAET,IAAI,OAAO,GAAG,CAAC,CAAC;QAEhB,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,yDAAyD;gBACzD,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;oBAC7B,MAAM,OAAO,CAAC,gBAAgB,EAAE,CAAC;gBACnC,CAAC;gBAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;gBAEnC,iEAAiE;gBACjE,MAAM,OAAO,CAAC,iBAAiB,EAAE,CAAC;gBAClC,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,EAAE,CAAC;gBAEV,uDAAuD;gBACvD,IAAI,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;oBAC5B,IAAI,CAAC;wBACH,MAAM,OAAO,CAAC,gBAAgB,EAAE,CAAC;oBACnC,CAAC;oBAAC,MAAM,CAAC;wBACP,UAAU;oBACZ,CAAC;gBACH,CAAC;gBAED,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;oBAC9D,MAAM,GAAG,CAAC;gBACZ,CAAC;gBAED,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAC7B,OAAO,EACP,WAAW,EACX,UAAU,EACV,MAAM,CACP,CAAC;gBACF,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAExB,yDAAyD;gBACzD,0DAA0D;YAC5D,CAAC;QACH,CAAC;IACH,CAAC;IAEO,sBAAsB,CAAC,GAAY;QACzC,MAAM,CAAC,GAAQ,GAAG,CAAC;QAEnB,wBAAwB;QACxB,MAAM,MAAM,GAAa,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAE5E,MAAM,eAAe,GAAG,CAAC,YAAY,0BAAgB,IAAI,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,gBAAgB;QAEzF,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;QACrC,MAAM,sBAAsB,GAC1B,GAAG,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;QAElE,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC;QAClE,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,gCAAgC,CAAC,CAAC;QAExE,OAAO,CACL,eAAe,IAAI,sBAAsB,IAAI,YAAY,IAAI,aAAa,CAC3E,CAAC;IACJ,CAAC;IAEO,YAAY,CAClB,OAAe,EACf,IAAY,EACZ,GAAW,EACX,MAAe;QAEf,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM;YAAE,OAAO,GAAG,CAAC;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,EAAU;QACtB,OAAO,IAAI,OAAO,CAAO,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IACzD,CAAC;CACF,CAAA;AApGY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;GACA,uBAAuB,CAoGnC"}