Skip to content

Give CLI-initiated Solr connections short, shared connect/idle timeouts - #4955

Closed
epugh wants to merge 5 commits into
apache:mainfrom
epugh:SOLR-cli-configurable-timeouts
Closed

epugh wants to merge 5 commits into
apache:mainfrom
epugh:SOLR-cli-configurable-timeouts

Conversation

@epugh

@epugh epugh commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Spun out of review discussion on a bin/solr create refactor (SOLR-18320): CLI commands that construct a SolrClient via CLIUtils.getSolrClient (or by hand-building an HttpJettySolrClient.Builder) currently inherit SolrHttpConstants' defaults — 60s connect / 600s idle. Those defaults are deliberately generous because they're shared by every SolrJ use case, including bulk indexing, shard-to-shard fan-out, and replica recovery, where a single request can legitimately take minutes.

A human running a bin/solr command at a terminal wants the opposite: fail fast against an unreachable or hung node. DeleteTool already carried its own 15s/30s override for exactly this reason (and CreateTool used to, before a recent refactor accidentally dropped it by switching to the shared client builder).

This PR centralizes that override:

  • Adds CLIUtils.CLI_CONNECTION_TIMEOUT_SECONDS (15) and CLIUtils.CLI_IDLE_TIMEOUT_SECONDS (30).
  • Applies them in CLIUtils.getSolrClient's core builder and in CLIUtils.solrUrlFromConnection's builder, which covers every CLI tool that calls getSolrClient (Healthcheck, SnapshotExport, Version, Assert, Export, Config, Create, Api, Delete, SnapshotList, Package, RunExample, Status, SnapshotCreate, SnapshotDelete).
  • Applies the same constants to the handful of tools that bypass CLIUtils.getSolrClient and hand-build their own HttpJettySolrClient.Builder: DeleteTool (replacing its hardcoded 15/30 with the shared constants), HealthcheckTool, ExportTool, PostLogsTool, StreamTool.

Out of scope: RunExampleTool's two internal waitToSeeLiveNodes/example-bootstrap CloudSolrClient builds are left on the long defaults — those are local-example node-readiness polling loops with their own retry/backoff logic, not a user-facing connection attempt, so a short connect timeout there wouldn't offer the same benefit and risks interacting oddly with the poll loop.

Test plan

  • ./gradlew :solr:core:compileJava — compiles cleanly
  • ./gradlew :solr:core:test --tests DeleteToolTest --tests TestExportTool --tests StreamToolTest --tests CreateToolTest --tests HealthcheckToolTest --tests PostLogsToolTest — all pass (32 tests, 1 skipped)
  • Added a changelog fragment under changelog/unreleased/

🤖 Generated with Claude Code

https://claude.ai/code/session_01HaP4sXv7KQBw9ZEqtvwug2

bin/solr commands funneling through CLIUtils.getSolrClient (and several
that hand-built their own HttpJettySolrClient.Builder) inherited
SolrHttpConstants' 60s connect / 600s idle defaults, which are sized
for long-running SolrJ use cases like bulk indexing and replica
recovery. A human running a CLI command against an unreachable node
wants fast failure instead. Centralizes the 15s/30s timeouts DeleteTool
already used (and CreateTool used to, before a recent refactor moved it
onto the shared client) into CLIUtils.CLI_CONNECTION_TIMEOUT_SECONDS /
CLI_IDLE_TIMEOUT_SECONDS, and applies them everywhere a CLI tool builds
an HttpJettySolrClient.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HaP4sXv7KQBw9ZEqtvwug2
@epugh

epugh commented Sep 27, 2026

Copy link
Copy Markdown
Contributor Author

This was claude generated, but looking hrtorugh, it seem sto make sense. I don't know if we want to have a single place to configure hte client, feels like we have gone and back and forth on this....

epugh added 2 commits October 7, 2026 15:49
…le-timeouts

# Conflicts:
#	solr/core/src/java/org/apache/solr/cli/DeleteTool.java
@epugh
epugh requested a review from janhoy October 7, 2026 19:54
@epugh

epugh commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

@janhoy @jaykay12 you guys might be interested in this as part of the picocli work? It appeared that our CLI's had very very long time outs?

@jaykay12 jaykay12 left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

changes lgtm.

how are we planning to test this? pre & post kinds.

Comment on lines +274 to +277
new HttpJettySolrClient.Builder()
.withIdleTimeout(CLI_IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.withConnectionTimeout(CLI_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.withOptionalBasicAuthCredentials(credentials);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

any merits in extracting this HttpJettySolrClient Builder into a common class say CLIUtils & using it in all these 4 tools?

would make the code duplication little lesser.

@epugh
epugh requested a review from HoustonPutman October 13, 2026 21:59
@epugh

epugh commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

@HoustonPutman I miss clicked your name on this PR when I meant a different one.. now i can't remove you as a requested reviewer, sorry about the noise.

@jaykay12 jaykay12 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@epugh can you trigger Copilot review on this PR as well?

*/
public static final int CLI_CONNECTION_TIMEOUT_SECONDS = 3;

public static final int CLI_IDLE_TIMEOUT_SECONDS = 30;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

My local review agent was against this idea:

Here's what it quoted to me.

Shortening idle to 30s is not equivalent to “fail fast on a dead node,” because in SolrJ withRequestTimeout falls back to the idle timeout when unset (HttpSolrClient.Builder.getRequestTimeoutMillis() → getIdleTimeoutMillis()). Jetty then applies that value as the total request timeout (req.timeout(...)), not only as a quiet-socket limit.

So withIdleTimeout(30, SECONDS) without an explicit longer withRequestTimeout caps every HTTP round-trip at 30s wall-clock. That replaces SolrJ’s 600s default and will abort legitimate long CLI work that still has healthy I/O (export, streaming expressions, postlogs, large posts, package install, snapshot export, etc.).

Fail-fast against an unreachable node is a connect timeout concern — 3s there is right. Leave idle/request at SolrJ defaults (600s) so hung-connect fails quickly while long-running commands keep working. If we later want a short idle for “no bytes for N seconds,” we must set withRequestTimeout to DEFAULT_SO_TIMEOUT (or similar) explicitly so request duration is not coupled to idle.

@epugh

epugh commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

I'm definilty not "feeling" this one... I'm going to close.

@epugh epugh closed this Oct 20, 2026
@epugh

epugh commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

also, thanks for the review. The PR that doesn't land is sometimes the best PR!

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants