Skip to content

feat: introduce SNYK_REQUEST_CONCURRENCY for dependency request parallelism#6756

Open
bgardiner wants to merge 1 commit intomainfrom
feat/snyk-request-concurrency
Open

feat: introduce SNYK_REQUEST_CONCURRENCY for dependency request parallelism#6756
bgardiner wants to merge 1 commit intomainfrom
feat/snyk-request-concurrency

Conversation

@bgardiner
Copy link
Copy Markdown
Contributor

@bgardiner bgardiner commented Apr 29, 2026

What does this PR do?

Adds a tunable concurrency knob for in-flight dependency-test / dependency-monitor HTTP requests, and bumps the default from 5 to 10.

  • New helper getRequestConcurrency() in src/lib/snyk-test/common.ts — reads SNYK_REQUEST_CONCURRENCY, defaults to 10, clamps to [1, 50].
  • Replaces the hard-coded MAX_CONCURRENCY = 5 constant at the existing pMap call site in sendAndParseResults (src/lib/snyk-test/run-test.ts).

A follow-up PR (#6757) adopts the same helper in src/lib/ecosystems/monitor.ts:monitorDependencies, which is currently fully sequential.

Why?

snyk container test produces one ScanResult per directory of dependencies in the image (lib/analyzer/applications/java.ts:groupJarFingerprintsByPath in snyk-docker-plugin). For Java-heavy images this can be hundreds of ScanResults, each becoming a separate POST /test-dependencies request. With the prior concurrency cap of 5, the request fan-out is the dominant wall-clock cost.

Benchmark — quay.io/wildfly/wildfly:34.0.1.Final-jdk21 (512 ScanResults)

hyperfine --warmup 1 --runs 3 against the same locally-built PR binary, varying only SNYK_REQUEST_CONCURRENCY to isolate the API concurrency change from any other build/runtime differences. The c=5 row reproduces the prior hard-coded value, so it's an apples-to-apples baseline.

Configuration Mean wall-clock vs baseline
baseline (c=5) 68.04 s ± 1.72 s 1.00×
PR default (c=10) 40.96 s ± 0.15 s 1.66× faster (−40%)
PR override (c=20) 25.13 s ± 0.48 s 2.71× faster (−63%)

Min/max within ±2% of mean across all configs — variance is tight. Standard deviation drops with higher concurrency (1.72s → 0.15s → 0.48s) because the wait-time component shrinks and the scan becomes more CPU-bound on the (much steadier) JAR-extraction work in snyk-docker-plugin.

For non-container test workloads the default bump is essentially a no-op:

  • Single-project tests produce one payload.
  • --all-projects rarely produces more than the prior 5-payload ceiling.

Where should the reviewer start?

  • src/lib/snyk-test/common.ts — new helper.
  • src/lib/snyk-test/run-test.ts — single call-site swap.
  • test/jest/unit/lib/snyk-test/common.spec.ts — 9 new unit tests for the helper covering default, override, clamping, and invalid-input cases.

How should this be manually tested?

  1. Run targeted unit tests:
    npx jest --selectProjects coreCli --testPathPattern 'test/jest/unit/lib/snyk-test/common.spec'
  2. Optionally measure on a representative many-ScanResult image (Wildfly works well):
    hyperfine --warmup 1 --runs 3 \
      'SNYK_REQUEST_CONCURRENCY=5 node bin/snyk container test quay.io/wildfly/wildfly:34.0.1.Final-jdk21' \
      'node bin/snyk container test quay.io/wildfly/wildfly:34.0.1.Final-jdk21' \
      'SNYK_REQUEST_CONCURRENCY=20 node bin/snyk container test quay.io/wildfly/wildfly:34.0.1.Final-jdk21'

Risk assessment

Low. The change is scoped to a single constant + a small env-var helper. Default behavior change (5 → 10) only affects test invocations that produce more than 5 payloads, which today means container tests and large --all-projects runs. The override is bounded to [1, 50] to prevent footguns.

Background

Supersedes #6747 (which targeted the wrong code path — src/lib/ecosystems/test.ts:testDependencies is unreachable from snyk container test; getEcosystemForTest returns null for docker, so container test goes through runTest/pMap instead).

…lelism

Add a tunable concurrency knob for in-flight dependency-test/dependency-monitor
HTTP requests, default 10 (raised from the prior hard-coded 5), clamped to
[1, 50]. Override via the SNYK_REQUEST_CONCURRENCY environment variable.

Apply the new helper at the existing pMap call site in
sendAndParseResults (run-test.ts). The default bump is effectively a no-op
for non-container test workloads (single-project tests produce one payload;
--all-projects rarely produces more than the prior 5-payload ceiling), but
materially improves wall-clock for container tests that produce one
ScanResult per directory containing dependencies.

A follow-up PR will adopt the same helper in the container monitor path,
which is currently fully sequential.
@snyk-io
Copy link
Copy Markdown

snyk-io Bot commented Apr 29, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Copy Markdown
Contributor

Warnings
⚠️

"feat: introduce SNYK_REQUEST_CONCURRENCY for dependency request parallelism" is too long. Keep the first line of your commit message under 72 characters.

Generated by 🚫 dangerJS against 6764f65

@bgardiner
Copy link
Copy Markdown
Contributor Author

Benchmark — quay.io/wildfly/wildfly:34.0.1.Final-jdk21 (512 ScanResults)

hyperfine --warmup 1 --runs 3 against the same locally-built PR A binary, varying only SNYK_REQUEST_CONCURRENCY to isolate the API concurrency change from any other build/runtime differences. The c=5 row reproduces the prior hard-coded value, so it's an apples-to-apples baseline.

Configuration Mean wall-clock vs baseline
baseline (c=5) 68.04 s ± 1.72 s 1.00×
PR A default (c=10) 40.96 s ± 0.15 s 1.66× faster (−40%)
PR A override (c=20) 25.13 s ± 0.48 s 2.71× faster (−63%)

Each config: 3 runs after 1 warmup; image pre-pulled. Min/max within ±2% of mean across all configs — variance is tight.

Standard deviation drops with higher concurrency (1.72s → 0.15s → 0.48s) because the wait-time component shrinks and the scan becomes more CPU-bound on the (much steadier) JAR-extraction work in snyk-docker-plugin.

@bgardiner bgardiner marked this pull request as ready for review April 30, 2026 01:21
@bgardiner bgardiner requested review from a team as code owners April 30, 2026 01:21
@bgardiner bgardiner requested a review from bdemeo12 April 30, 2026 01:22
@snyk-pr-review-bot
Copy link
Copy Markdown

PR Reviewer Guide 🔍

🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Sub-optimal fallback logic 🟡 [minor]

The getRequestConcurrency function returns DEFAULT_REQUEST_CONCURRENCY (10) when the parsed value is less than MIN_REQUEST_CONCURRENCY (1). If a user explicitly sets SNYK_REQUEST_CONCURRENCY=0 to attempt to minimize load or debug sequential execution, the code will silently revert to 10. It would be more intuitive to return MIN_REQUEST_CONCURRENCY in this specific case.

if (!Number.isFinite(parsed) || parsed < MIN_REQUEST_CONCURRENCY) {
  return DEFAULT_REQUEST_CONCURRENCY;
}
📚 Repository Context Analyzed

This review considered 15 relevant code sections from 5 files (average relevance: 0.78)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant