Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/lib/snyk-test/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,28 @@ export type FailOn = 'all' | 'upgradable' | 'patchable';
export const RETRY_ATTEMPTS = 3;
export const RETRY_DELAY = 500;

const DEFAULT_REQUEST_CONCURRENCY = 10;
const MIN_REQUEST_CONCURRENCY = 1;
const MAX_REQUEST_CONCURRENCY = 50;

/**
* Returns the maximum number of in-flight Snyk dependency-test or
* dependency-monitor HTTP requests permitted at once. Override with the
* SNYK_REQUEST_CONCURRENCY environment variable; values are clamped to
* [MIN_REQUEST_CONCURRENCY, MAX_REQUEST_CONCURRENCY].
*/
export function getRequestConcurrency(): number {
const raw = process.env.SNYK_REQUEST_CONCURRENCY;
if (!raw) {
return DEFAULT_REQUEST_CONCURRENCY;
}
const parsed = parseInt(raw, 10);
if (!Number.isFinite(parsed) || parsed < MIN_REQUEST_CONCURRENCY) {
return DEFAULT_REQUEST_CONCURRENCY;
}
return Math.min(parsed, MAX_REQUEST_CONCURRENCY);
}

/**
* printDepGraph writes the given dep-graph and target name to the destination
* stream as expected by the `depgraph` CLI workflow.
Expand Down
6 changes: 2 additions & 4 deletions src/lib/snyk-test/run-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { isCI } from '../is-ci';
import {
RETRY_ATTEMPTS,
RETRY_DELAY,
getRequestConcurrency,
printDepGraph,
printEffectiveDepGraph,
printEffectiveDepGraphError,
Expand Down Expand Up @@ -94,9 +95,6 @@ import { ProblemError } from '@snyk/error-catalog-nodejs-public';

const debug = debugModule('snyk:run-test');

// Controls the number of simultaneous test requests that can be in-flight.
const MAX_CONCURRENCY = 5;

function prepareResponseForParsing(
payload: Payload,
response: TestDependenciesResponse,
Expand Down Expand Up @@ -293,7 +291,7 @@ async function sendAndParseResults(
};

const responses = await pMap(payloads, sendRequest, {
concurrency: MAX_CONCURRENCY,
concurrency: getRequestConcurrency(),
});

for (const { payload, originalPayload, response } of responses) {
Expand Down
62 changes: 61 additions & 1 deletion test/jest/unit/lib/snyk-test/common.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { CLI, ProblemError } from '@snyk/error-catalog-nodejs-public';
import { CustomError } from '../../../../../src/lib/errors';
import { FailedProjectScanError } from '../../../../../src/lib/plugins/get-multi-plugin-result';
import { getOrCreateErrorCatalogError } from '../../../../../src/lib/snyk-test/common';
import {
getOrCreateErrorCatalogError,
getRequestConcurrency,
} from '../../../../../src/lib/snyk-test/common';

describe('getOrCreateErrorCatalogError', () => {
const defaultErrMessage = 'Default error message';
Expand Down Expand Up @@ -116,3 +119,60 @@ describe('getOrCreateErrorCatalogError', () => {
expect(result.detail).toBe(defaultErrMessage);
});
});

describe('getRequestConcurrency', () => {
const originalValue = process.env.SNYK_REQUEST_CONCURRENCY;

afterEach(() => {
if (originalValue === undefined) {
delete process.env.SNYK_REQUEST_CONCURRENCY;
} else {
process.env.SNYK_REQUEST_CONCURRENCY = originalValue;
}
});

it('returns the default of 10 when SNYK_REQUEST_CONCURRENCY is unset', () => {
delete process.env.SNYK_REQUEST_CONCURRENCY;
expect(getRequestConcurrency()).toBe(10);
});

it('returns the default of 10 when SNYK_REQUEST_CONCURRENCY is empty', () => {
process.env.SNYK_REQUEST_CONCURRENCY = '';
expect(getRequestConcurrency()).toBe(10);
});

it('returns the parsed value when SNYK_REQUEST_CONCURRENCY is a valid integer', () => {
process.env.SNYK_REQUEST_CONCURRENCY = '15';
expect(getRequestConcurrency()).toBe(15);
});

it('clamps to the maximum of 50 when SNYK_REQUEST_CONCURRENCY exceeds the cap', () => {
process.env.SNYK_REQUEST_CONCURRENCY = '500';
expect(getRequestConcurrency()).toBe(50);
});

it('returns the default when SNYK_REQUEST_CONCURRENCY is below the minimum', () => {
process.env.SNYK_REQUEST_CONCURRENCY = '0';
expect(getRequestConcurrency()).toBe(10);
});

it('returns the default when SNYK_REQUEST_CONCURRENCY is negative', () => {
process.env.SNYK_REQUEST_CONCURRENCY = '-5';
expect(getRequestConcurrency()).toBe(10);
});

it('returns the default when SNYK_REQUEST_CONCURRENCY is non-numeric', () => {
process.env.SNYK_REQUEST_CONCURRENCY = 'abc';
expect(getRequestConcurrency()).toBe(10);
});

it('honors the minimum boundary', () => {
process.env.SNYK_REQUEST_CONCURRENCY = '1';
expect(getRequestConcurrency()).toBe(1);
});

it('honors the maximum boundary', () => {
process.env.SNYK_REQUEST_CONCURRENCY = '50';
expect(getRequestConcurrency()).toBe(50);
});
});
Loading