diff --git a/package-lock.json b/package-lock.json index 93c86b6..55ae10c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@semrush/purr", - "version": "3.15.8", + "version": "3.15.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@semrush/purr", - "version": "3.15.8", + "version": "3.15.10", "license": "MIT", "dependencies": { "@sentry/node": "^7.93.0", diff --git a/package.json b/package.json index 16462de..29bac63 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@semrush/purr", - "version": "3.15.8", + "version": "3.15.10", "description": "", "main": "src/cli.cjs", "scripts": { diff --git a/src/check/runner.js b/src/check/runner.js index 3b19e98..ee42e6f 100644 --- a/src/check/runner.js +++ b/src/check/runner.js @@ -180,8 +180,6 @@ class CheckRunner { result = result.then(async () => { const actionReport = new ActionReport(stepName, step, '***hidden***'); - const maxRetries = 5; - const baseDelay = 1000; // eslint-disable-next-line consistent-return const runAction = async (attempt = 1) => { @@ -215,23 +213,24 @@ class CheckRunner { }); } } catch (err) { - const isResetError = err.message.includes('ERR_CONNECTION_RESET'); - const isClosedError = err.message.includes('ERR_CONNECTION_CLOSED'); - if (isResetError || isClosedError) { - log.error('ERR_CONNECTION_* error occurred: ', err); - } - if ((isResetError || isClosedError) && attempt <= maxRetries) { - const jitter = Math.random() * 1000; - const delay = baseDelay * attempt + jitter; - log.warn( - `Retrying action '${stepName}' (Attempt ${attempt} of ${maxRetries}) after ${delay.toFixed( - 0 - )}ms due to connection issue.` - ); - await new Promise((resolve) => { - setTimeout(resolve, delay); - }); - return runAction(attempt + 1); + const isRetryableError = config.actionRetryErrors.some((item) => + err.message.includes(item) + ); + if (isRetryableError) { + log.error('Retryable error occurred: ', err); + if (attempt <= config.actionRetryCount) { + const jitter = Math.random() * 1000; + const delay = config.actionRetryTimeout * attempt + jitter; + log.warn( + `Retrying action '${stepName}' (Attempt ${attempt} of ${config.actionRetryCount}) after ${delay.toFixed( + 0 + )}ms due to connection issue.` + ); + await new Promise((resolve) => { + setTimeout(resolve, delay); + }); + return runAction(attempt + 1); + } } actionReport.success = false; actionReport.shortMessage = err.message; diff --git a/src/config/__tests__/configuration.test.js b/src/config/__tests__/configuration.test.js index 7d44711..26ecb26 100644 --- a/src/config/__tests__/configuration.test.js +++ b/src/config/__tests__/configuration.test.js @@ -8,6 +8,9 @@ describe('Test Configuration class', () => { const configuration = new Configuration(envParams, rootDir); const defaultConfiguration = { + actionRetryCount: 5, + actionRetryErrors: ['ERR_CONNECTION_RESET', 'ERR_CONNECTION_CLOSED'], + actionRetryTimeout: 1000, apiUrlPrefix: '/api/v1', apiWaitTimeout: 27000, artifactsDir: '/rootDir/storage', diff --git a/src/config/configuration.js b/src/config/configuration.js index 02b2059..b9223f3 100644 --- a/src/config/configuration.js +++ b/src/config/configuration.js @@ -200,6 +200,18 @@ class Configuration { 180000 ); + this.actionRetryErrors = getDefault( + envParams.ACTION_RETRY_ERRORS, + 'ERR_CONNECTION_RESET,ERR_CONNECTION_CLOSED' + ) + .split(',') + .map((msg) => msg.trim()) + .filter((msg) => msg.length > 0); + + this.actionRetryCount = getDefault(envParams.ACTION_RETRY_COUNT, 5); + + this.actionRetryTimeout = getDefault(envParams.ACTION_RETRY_TIMEOUT, 1000); + if (this.artifactsGroupByCheckName && isUndefined(this.artifactsDir)) { throw new Error( 'Enabled group artifacts by check name and artifacts path not specified' diff --git a/src/config/env.js b/src/config/env.js index fd2fbbc..c2260f0 100644 --- a/src/config/env.js +++ b/src/config/env.js @@ -233,6 +233,30 @@ const DEFAULT_ENV_PARAMS = { * @default {180000} */ BROWSER_PROTOCOL_TIMEOUT: 180000, + + /** + * Check action retry comma-separated errors list (checked by err.message.includes()) + * + * @type string + * @default ERR_CONNECTION_RESET,ERR_CONNECTION_CLOSED + */ + ACTION_RETRY_ERRORS: 'ERR_CONNECTION_RESET,ERR_CONNECTION_CLOSED', + + /** + * Check action retry count + * + * @type number + * @default 5 + */ + ACTION_RETRY_COUNT: 5, + + /** + * Check action retry timeout (ms) + * + * @type number + * @default 1000 + */ + ACTION_RETRY_TIMEOUT: 1000, }; class EnvParams {