@mimik/request-retry 2.0.11 → 2.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 +4 -0
- package/index.js +26 -10
- package/manual-test/man.js +2 -2
- package/manual-test/retryMockMan.js +5 -3
- package/manual-test/testEnv.js +23 -0
- package/package.json +6 -6
- package/test/testEnv.js +2 -0
package/README.md
CHANGED
|
@@ -29,6 +29,10 @@ The following properties are added to the `options` object under the `retry` pro
|
|
|
29
29
|
- *responseName*: name to associate with the response. The default is `response`or is the value is not a string or and empty string it is set to `response`,
|
|
30
30
|
- timeout `{int}`: in seconds the timeout to be set for the request and retries. If the timeout is reached, a TimeoutError will be generated. The default is `50 seconds`. If the value is less than `10 seconds` the value is set to `10 seconds`, and if the value is more than `120 seconds` the value is set to `120 seconds`,
|
|
31
31
|
- retryStrategy `{function}`: function describing the retry strategy. The parameters are (`nbRetry`, `err`, `options`, `correlationId`). Must return a `boolean`, if not the function strategy is ignored.
|
|
32
|
+
The following properties are added to the `options` object under the 'metrics' property:
|
|
33
|
+
- HTTPRequestDuration `function`: prom-client function to measure the delay of the request,
|
|
34
|
+
- url: optional url to be added for labelling the metric. If not present the url of the request will be used.
|
|
35
|
+
|
|
32
36
|
The `default` retryStategy is:
|
|
33
37
|
|
|
34
38
|
``` javascript
|
package/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const Promise = require('bluebird');
|
|
2
|
-
const axios = require('axios');
|
|
3
2
|
|
|
4
3
|
const logger = require('@mimik/sumologic-winston-logger');
|
|
5
4
|
const { getRichError } = require('@mimik/response-helper');
|
|
@@ -50,6 +49,10 @@ Promise.config({ cancellation: true });
|
|
|
50
49
|
* - *responseName*: name to associate with the response. The default is `response`or is the value is not a string or and empty string it is set to `response`,
|
|
51
50
|
* - timeout `{int}`: in seconds the timeout to be set for the request and retries. If the timeout is reached, a TimeoutError will be generated. The default is `50 seconds`. If the value is less than `10 seconds` the value is set to `10 seconds`, and if the value is more than `120 seconds` the value is set to `120 seconds`,
|
|
52
51
|
* - retryStrategy `{function}`: function describing the retry strategy. The parameters are (`nbRetry`, `err`, `options`, `correlationId`). Must return a `boolean`, if not the function strategy is ignored.
|
|
52
|
+
* The following properties are added to the `options` object under the 'metrics' property:
|
|
53
|
+
* - HTTPRequestDuration `function`: prom-client function to measure the delay of the request,
|
|
54
|
+
* - url: optional url to be added for labelling the metric. If not present the url of the request will be used.
|
|
55
|
+
*
|
|
53
56
|
* The `default` retryStategy is:
|
|
54
57
|
*
|
|
55
58
|
*``` javascript
|
|
@@ -69,9 +72,10 @@ Promise.config({ cancellation: true });
|
|
|
69
72
|
* - `qs` is used to assign to `params`
|
|
70
73
|
*/
|
|
71
74
|
const rpRetry = (origOptions) => {
|
|
72
|
-
const
|
|
73
|
-
const source = CancelToken.source();
|
|
75
|
+
const startHrTime = process.hrtime();
|
|
74
76
|
const options = origOptions;
|
|
77
|
+
const { metrics } = options;
|
|
78
|
+
const enteredUrl = options.uri || options.url;
|
|
75
79
|
const correlationId = (options.headers && options.headers['x-correlation-id']) ? options.headers['x-correlation-id'] : getCorrelationId();
|
|
76
80
|
const errors = [];
|
|
77
81
|
const delayPromises = [];
|
|
@@ -80,14 +84,25 @@ const rpRetry = (origOptions) => {
|
|
|
80
84
|
const logLevelLength = Object.keys(logLevel).length;
|
|
81
85
|
let nbRetries = 0;
|
|
82
86
|
let mainTimeout;
|
|
87
|
+
const measure = (statusCode) => {
|
|
88
|
+
if (metrics && metrics.HTTPRequestDuration) {
|
|
89
|
+
const elapsedHrTime = process.hrtime(startHrTime);
|
|
90
|
+
const elapsedTimeInMs = elapsedHrTime[0] * 1000 + elapsedHrTime[1] / 1e6;
|
|
91
|
+
|
|
92
|
+
metrics.HTTPRequestDuration
|
|
93
|
+
.labels('rpRetry', options.method, metrics.url || enteredUrl, enteredUrl.includes('?'), statusCode)
|
|
94
|
+
.observe(elapsedTimeInMs);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
83
97
|
|
|
84
98
|
if (!options.headers) options.headers = { 'user-agent': setUserAgent() };
|
|
85
99
|
else if (!options.headers['user-agent']) options.headers['user-agent'] = setUserAgent();
|
|
86
|
-
|
|
100
|
+
const optionsInfo = { ...options };
|
|
101
|
+
delete optionsInfo.metrics;
|
|
87
102
|
|
|
88
103
|
const retryProcess = (nbRetry) => rp(options)
|
|
89
104
|
.then((response) => {
|
|
90
|
-
const info = { options, nbRetries, errors };
|
|
105
|
+
const info = { options: optionsInfo, nbRetries, errors };
|
|
91
106
|
|
|
92
107
|
if (logLevel.response) {
|
|
93
108
|
info[logLevel.responseName] = setResponse(response, logLevel.responseDetails);
|
|
@@ -130,8 +145,7 @@ const rpRetry = (origOptions) => {
|
|
|
130
145
|
const retryPromise = retryProcess(0);
|
|
131
146
|
const mainTimeoutPromise = new Promise((resolve, reject) => {
|
|
132
147
|
mainTimeout = setTimeout(() => {
|
|
133
|
-
delayPromises.forEach((delayPromise) => delayPromise.cancel(`retry timeout, ${criteria.timeout}, ${nbRetries}`));
|
|
134
|
-
source.cancel(`retry timeout, ${criteria.timeout}, ${nbRetries}`);
|
|
148
|
+
delayPromises.forEach((delayPromise) => delayPromise.cancel(`retry timeout delay, ${criteria.timeout}, ${nbRetries}`));
|
|
135
149
|
let error = new Error('retry timeout');
|
|
136
150
|
|
|
137
151
|
if (nbRetries !== 0) {
|
|
@@ -141,7 +155,7 @@ const rpRetry = (origOptions) => {
|
|
|
141
155
|
error = getRichError(
|
|
142
156
|
'System',
|
|
143
157
|
'request error response',
|
|
144
|
-
{ options },
|
|
158
|
+
{ options: optionsInfo },
|
|
145
159
|
error,
|
|
146
160
|
logLevel.error || ((logLevelLength === 0) ? DEFAULT_LOGLEVEL_ERROR : undefined),
|
|
147
161
|
correlationId,
|
|
@@ -150,17 +164,19 @@ const rpRetry = (origOptions) => {
|
|
|
150
164
|
}, criteria.timeout * 1000);
|
|
151
165
|
});
|
|
152
166
|
|
|
153
|
-
if (logLevel.request) logger[logLevel.request]('making a request', { options, criteria }, correlationId);
|
|
154
|
-
else if (logLevelLength === 0) logger[DEFAULT_LOGLEVEL_REQUEST]('making a request', { options, criteria }, correlationId);
|
|
167
|
+
if (logLevel.request) logger[logLevel.request]('making a request', { options: optionsInfo, criteria }, correlationId);
|
|
168
|
+
else if (logLevelLength === 0) logger[DEFAULT_LOGLEVEL_REQUEST]('making a request', { options: optionsInfo, criteria }, correlationId);
|
|
155
169
|
return Promise.race([retryPromise, mainTimeoutPromise])
|
|
156
170
|
.then((result) => {
|
|
157
171
|
mainTimeoutPromise.cancel('main timeout');
|
|
158
172
|
clearTimeout(mainTimeout);
|
|
173
|
+
measure(200);
|
|
159
174
|
return result;
|
|
160
175
|
})
|
|
161
176
|
.catch((err) => {
|
|
162
177
|
mainTimeoutPromise.cancel(`main timeout error: ${err.message}`);
|
|
163
178
|
clearTimeout(mainTimeout);
|
|
179
|
+
measure(err.statusCode || 500);
|
|
164
180
|
throw err;
|
|
165
181
|
});
|
|
166
182
|
};
|
package/manual-test/man.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
|
-
require('
|
|
2
|
+
require('./testEnv');
|
|
3
3
|
|
|
4
4
|
const { rpRetry } = require('../index');
|
|
5
5
|
|
|
@@ -15,7 +15,7 @@ const options = {
|
|
|
15
15
|
retry: {},
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
options.retry.delayStrategy = () =>
|
|
18
|
+
options.retry.delayStrategy = () => 1000;
|
|
19
19
|
options.retry.timeout = 10;
|
|
20
20
|
options.retry.retries = 2;
|
|
21
21
|
|
|
@@ -11,6 +11,7 @@ const config = {
|
|
|
11
11
|
nbRetry: 400,
|
|
12
12
|
success: 200,
|
|
13
13
|
error: 500,
|
|
14
|
+
timeout: 4000,
|
|
14
15
|
},
|
|
15
16
|
post: {
|
|
16
17
|
success: 201,
|
|
@@ -37,9 +38,10 @@ app.get(`${config.base}${config.path}`, (req, res) => {
|
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
39
40
|
nbRequest += 1;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
res.statusCode = config.get.error;
|
|
43
|
+
res.send({ statusCode: config.get.error });
|
|
44
|
+
}, config.get.timeout);
|
|
43
45
|
});
|
|
44
46
|
app.post(`${config.base}${config.path}`, (req, res) => {
|
|
45
47
|
console.log('----->', 'Received a POST request');
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* eslint no-process-env: "off" */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The following environment variables are set for the test:
|
|
5
|
+
*
|
|
6
|
+
* | Env variable name | Description | Default | Comments |
|
|
7
|
+
* | ----------------- | ----------- | ------- | -------- |
|
|
8
|
+
* | SUMO_LOGIC_ENDPOINT | endpoint to use to log on sumologic | null
|
|
9
|
+
* | SUMO_LOGIC_COLLECTOR_CODE | code to use to log on sumologic | null
|
|
10
|
+
* | NO_STACK | flag to have a stack associated with the log | yes
|
|
11
|
+
* | LOG_LEVEL | log level to log | error
|
|
12
|
+
* | CONSOLE_LEVEL | log level to diplay | debug
|
|
13
|
+
* | LOG_MODE | log mode | none
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// process.env.SUMO_LOGIC_ENDPOINT = 'http://localhost:9080/logs/';
|
|
17
|
+
// process.env.SUMO_LOGIC_COLLECTOR_CODE = '1234';
|
|
18
|
+
process.env.SUMO_LOGIC_ENDPOINT = null;
|
|
19
|
+
process.env.SUMO_LOGIC_COLLECTOR_CODE = null;
|
|
20
|
+
process.env.NO_STACK = 'yes';
|
|
21
|
+
process.env.LOG_LEVEL = 'debug';
|
|
22
|
+
process.env.CONSOLE_LEVEL = 'debug';
|
|
23
|
+
process.env.LOG_MODE = 'none';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mimik/request-retry",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Request retry wrapping axios",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@mimik/request-helper": "^1.7.7",
|
|
33
33
|
"@mimik/response-helper": "^2.6.2",
|
|
34
|
-
"@mimik/sumologic-winston-logger": "^1.6.
|
|
35
|
-
"axios": "1.1
|
|
34
|
+
"@mimik/sumologic-winston-logger": "^1.6.13",
|
|
35
|
+
"axios": "1.2.1",
|
|
36
36
|
"bluebird": "3.7.2",
|
|
37
37
|
"lodash": "4.17.21"
|
|
38
38
|
},
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"acorn": "8.8.1",
|
|
43
43
|
"body-parser": "1.20.1",
|
|
44
44
|
"chai": "4.3.7",
|
|
45
|
-
"eslint": "8.
|
|
45
|
+
"eslint": "8.30.0",
|
|
46
46
|
"eslint-config-airbnb": "19.0.4",
|
|
47
47
|
"eslint-plugin-import": "2.26.0",
|
|
48
48
|
"eslint-plugin-jsx-a11y": "6.6.1",
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"express": "4.18.2",
|
|
52
52
|
"husky": "8.0.2",
|
|
53
53
|
"jsdoc-to-markdown": "8.0.0",
|
|
54
|
-
"mocha": "10.
|
|
54
|
+
"mocha": "10.2.0",
|
|
55
55
|
"mochawesome": "7.1.3",
|
|
56
56
|
"nyc": "15.1.0",
|
|
57
|
-
"sinon": "15.0.
|
|
57
|
+
"sinon": "15.0.1",
|
|
58
58
|
"verror": "1.10.1"
|
|
59
59
|
}
|
|
60
60
|
}
|
package/test/testEnv.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* | NO_STACK | flag to have a stack associated with the log | yes
|
|
11
11
|
* | LOG_LEVEL | log level to log | error
|
|
12
12
|
* | CONSOLE_LEVEL | log level to diplay | debug
|
|
13
|
+
* | LOG_MODE | log mode | none
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
// process.env.SUMO_LOGIC_ENDPOINT = 'http://localhost:9080/logs/';
|
|
@@ -19,3 +20,4 @@ process.env.SUMO_LOGIC_COLLECTOR_CODE = null;
|
|
|
19
20
|
process.env.NO_STACK = 'yes';
|
|
20
21
|
process.env.LOG_LEVEL = 'debug';
|
|
21
22
|
process.env.CONSOLE_LEVEL = 'debug';
|
|
23
|
+
process.env.LOG_MODE = 'none';
|