Skip to content

Navigation Menu

Sign in
Appearance settings
Sign up
Appearance settings

Add configurable timeouts to SCM API requests - #7536

#7536
Open
robsyme wants to merge 2 commits into
masternextflow-io/nextflow:masterfrom
fix/scm-http-timeoutsnextflow-io/nextflow:fix/scm-http-timeoutsCopy head branch name to clipboard
Open

Add configurable timeouts to SCM API requests#7536
robsyme wants to merge 2 commits into
masternextflow-io/nextflow:masterfrom
fix/scm-http-timeoutsnextflow-io/nextflow:fix/scm-http-timeoutsCopy head branch name to clipboard

Conversation

@robsyme

@robsyme robsyme commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

The HTTP client used by nextflow.scm.RepositoryProvider to call the Git hosting service API (GitHub, GitLab, etc.) hardcodes a 60-second connect timeout and sets no per-request timeout. An API call that connects and then receives no response waits indefinitely, and a dropped connection attempt costs a fixed 60 seconds with no retry. In server contexts that embed this code, a single stalled request is enough to produce multi-minute API latency.

Changes

Commit 1 makes both timeouts explicit and configurable:

  • Every SCM API request now carries a request (read) timeout in addition to the connect timeout.
  • Both are tunable via NXF_GIT_CONNECT_TIMEOUT and NXF_GIT_READ_TIMEOUT as duration strings (default: 60s each).
  • Timeout errors surface as IOException reporting the affected URL and the variable to tune.

Commit 2 makes connect timeouts retryable while read timeouts stay fail-fast:

  • A connect timeout means the TCP handshake never completed, so no request data reached the server and a retry is always safe. The failure mode observed in production is a per-connection stall on the network path while the server itself is healthy, where a fresh connection attempt typically succeeds.
  • A read timeout means the request reached the server, so it is not retried. The instanceof check targets HttpConnectTimeoutException specifically since it extends HttpTimeoutException.
  • The retry runs inside the HxClient retry loop, so the error is translated to IOException (now reporting the attempt count) only once all attempts are exhausted.

Note the retryability multiplies the connect timeout for a persistently unreachable host: with defaults, 60s x 5 attempts is about 5 minutes before failing. Deployments that want bounded interactive latency should lower the connect timeout, e.g. NXF_GIT_CONNECT_TIMEOUT=5s caps the worst case at roughly 30 seconds including backoff (tunable further via the existing NXF_RETRY_POLICY_* variables), while a transient stall recovers in about 5-6 seconds on retry. The docs entry spells this out.

Tests

  • Env var parsing, invalid-value fallback, and the read timeout being applied to the outgoing request.
  • An end-to-end read-timeout test against a local stalled HTTP server.
  • isRetryable covering connect timeout (retryable) vs plain HttpTimeoutException (not retryable), alongside the existing SocketException/UnresolvedAddressException cases.
  • The HxConfig retry-condition wiring, including cause traversal and null-cause safety.

Complements #7401, #7402 and #7403, which reduce the volume of SCM API calls made during revision resolution.

🤖 Generated with Claude Code

The HTTP client used to access the Git hosting service API (GitHub,
GitLab, etc.) hardcoded a 60-second connect timeout and never set a
request timeout, so a request that connected but received no response
could wait indefinitely.

Set a read timeout on every SCM API request and make both timeouts
configurable via the NXF_GIT_CONNECT_TIMEOUT and NXF_GIT_READ_TIMEOUT
environment variables (default: 60s each). Timeout errors now report
the affected URL and the variable to tune.

Assisted-by: Claude Code (Fable 5)
Signed-off-by: Rob Syme <rob.syme@gmail.com>
A connect timeout means the TCP handshake never completed, therefore
no request data reached the server and a retry is always safe. The
observed failure mode is a per-connection stall on the network path,
where a fresh connection attempt typically succeeds. With a lowered
NXF_GIT_CONNECT_TIMEOUT (e.g. 5s) a stalled request now recovers in a
few seconds instead of failing the run.

Make HttpConnectTimeoutException retryable in the SCM HTTP client
retry condition. Read timeouts (the parent HttpTimeoutException) stay
fail-fast because the request reached the server. The connect timeout
error message now also reports the number of attempts made.

Assisted-by: Claude Code (Fable 5)
Signed-off-by: Rob Syme <rob.syme@gmail.com>
@robsyme
robsyme requested a review from a team as a code owner August 24, 2026 19:12
@netlify

netlify Bot commented Aug 24, 2026

Copy link
Copy Markdown

Deploy Preview for nextflow-docs ready!

Name Link
🔨 Latest commit 7986372
🔍 Latest deploy log https://app.netlify.com/projects/nextflow-docs/deploys/6a8c9783e901790008ce9bd4
😎 Deploy Preview https://deploy-preview-7536--nextflow-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@pditommaso pditommaso left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @robsyme, this is really nice work — the connect-vs-read retry reasoning is spot on. 🙌

One thought before we merge: could we converge this on the pattern we just landed in nf-seqera instead of adding env vars? Concretely:

Now: swap NXF_GIT_* for an scm.httpClient config scope mirroring seqera.executor.httpClient (io.seqera.config.HttpClientOpts) — same connectTimeout / requestTimeout names and the same zero-handling (requestTimeout = 0 = unbounded, connectTimeout must be > 0). Nice side effect: that model already avoids the 0/0s crash the current timeout0() hits at HttpRequest.timeout(ZERO). And since these env vars aren't released yet, there's nothing to deprecate.

A couple of things we can just reuse:

  • Retry's already converged — RetryConfig implements Retryable.Config, and HxConfig.defaultRetryCondition() already does exactly the "retry connect timeout, not read timeout" rule you hand-rolled in isRetryable. So no new HxClient interface needed — let's lean on what's there.
  • Small correctness note: HttpRequest.timeout() also covers the connect phase, so if requestTimeout < connectTimeout a connect stall surfaces as the non-retryable exception. Setting the per-request timeout to connect + request sidesteps it.

Later (separate PR): we'll then have three near-identical client-config classes (wave, seqera, scm), so I'd like to lift a shared HttpClientOpts into nf-commons — but that's follow-up, no need to block this on it.

WDYT?

@robsyme

robsyme commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks Paolo.

I agree that there are already a bewildering number of NXF env vars to contend with. That said, the really important client of this change is Platform, which imports this SCM code as a library. It's not totally clear to me if/how configuration options are read in there. I'll have a look tonight.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.