@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 +11 -0
- package/package.json +13 -0
- package/src/index.d.ts +2 -0
- package/src/index.js +6 -0
- package/src/index.js.map +1 -0
- package/src/retry-policy.module.d.ts +2 -0
- package/src/retry-policy.module.js +17 -0
- package/src/retry-policy.module.js.map +1 -0
- package/src/retry-policy.mongo.service.d.ts +13 -0
- package/src/retry-policy.mongo.service.js +77 -0
- package/src/retry-policy.mongo.service.js.map +1 -0
package/README.md
ADDED
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
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
|
package/src/index.js.map
ADDED
|
@@ -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,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":"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"}
|