From b96a289dad235e547de9d4b587ae9efa85c65a2f Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Sun, 27 Sep 2026 15:52:40 -0700 Subject: [PATCH 1/3] Give CLI-initiated Solr connections short, shared connect/idle timeouts 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 Claude-Session: https://claude.ai/code/session_01HaP4sXv7KQBw9ZEqtvwug2 --- .../unreleased/cli-configurable-timeouts.yml | 6 ++++++ .../src/java/org/apache/solr/cli/CLIUtils.java | 18 +++++++++++++++++- .../java/org/apache/solr/cli/DeleteTool.java | 4 ++-- .../java/org/apache/solr/cli/ExportTool.java | 6 +++++- .../org/apache/solr/cli/HealthcheckTool.java | 6 +++++- .../java/org/apache/solr/cli/PostLogsTool.java | 2 ++ .../java/org/apache/solr/cli/StreamTool.java | 6 +++++- 7 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 changelog/unreleased/cli-configurable-timeouts.yml diff --git a/changelog/unreleased/cli-configurable-timeouts.yml b/changelog/unreleased/cli-configurable-timeouts.yml new file mode 100644 index 000000000000..20ad57b0af4e --- /dev/null +++ b/changelog/unreleased/cli-configurable-timeouts.yml @@ -0,0 +1,6 @@ +# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc +title: bin/solr CLI commands now fail fast (15s connect / 30s idle) against an unresponsive + node instead of inheriting SolrJ's 60s/600s bulk-indexing defaults. +type: changed +authors: + - name: Eric Pugh diff --git a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java index 06ed95eae075..a4c5d5bed516 100644 --- a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java +++ b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java @@ -63,6 +63,16 @@ private CLIUtils() {} private static final long MAX_WAIT_FOR_CORE_LOAD_NANOS = TimeUnit.NANOSECONDS.convert(1, TimeUnit.MINUTES); + /** + * Connect and idle timeouts used for CLI-initiated Solr connections. These are much shorter than + * {@link org.apache.solr.client.solrj.impl.SolrHttpConstants}' defaults, which are sized for + * long-running requests like bulk indexing or replica recovery; a CLI command run by a human at a + * terminal should fail fast instead of hanging for minutes against an unresponsive node. + */ + public static final int CLI_CONNECTION_TIMEOUT_SECONDS = 15; + + public static final int CLI_IDLE_TIMEOUT_SECONDS = 30; + private static CloudSolrClient.CloudSolrClientConnection resolveSolrConnectionFromCli( CommandLine cli) throws IOException { String solrConnection = @@ -146,6 +156,8 @@ public static SolrClient getSolrClient(String solrUrl, String credentials, boole var builder = new HttpJettySolrClient.Builder(solrUrl) .withMaxConnectionsPerHost(32) + .withIdleTimeout(CLI_IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .withConnectionTimeout(CLI_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) .withKeyStoreReloadInterval(-1, TimeUnit.SECONDS) .withOptionalBasicAuthCredentials(credentials); @@ -254,7 +266,11 @@ public static String solrUrlFromConnection( if (!solrConnection.isZookeeper()) { return normalizeSolrUrl(solrConnection.quorumItems().get(0), false); } - var builder = new HttpJettySolrClient.Builder().withOptionalBasicAuthCredentials(credentials); + var builder = + new HttpJettySolrClient.Builder() + .withIdleTimeout(CLI_IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .withConnectionTimeout(CLI_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .withOptionalBasicAuthCredentials(credentials); try (CloudSolrClient cloudSolrClient = getCloudSolrClient(solrConnection, builder)) { Set liveNodes = cloudSolrClient.getClusterState().getLiveNodes(); if (liveNodes.isEmpty()) diff --git a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java index 59450cf00c14..cb1df2d98baa 100644 --- a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java +++ b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java @@ -104,8 +104,8 @@ public void runImpl(CommandLine cli) throws Exception { protected void deleteCollection(CommandLine cli) throws Exception { var builder = new HttpJettySolrClient.Builder() - .withIdleTimeout(30, TimeUnit.SECONDS) - .withConnectionTimeout(15, TimeUnit.SECONDS) + .withIdleTimeout(CLIUtils.CLI_IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .withConnectionTimeout(CLIUtils.CLI_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) .withKeyStoreReloadInterval(-1, TimeUnit.SECONDS) .withOptionalBasicAuthCredentials( cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION)); diff --git a/solr/core/src/java/org/apache/solr/cli/ExportTool.java b/solr/core/src/java/org/apache/solr/cli/ExportTool.java index 36b0db4fcb04..849d2e7ae013 100644 --- a/solr/core/src/java/org/apache/solr/cli/ExportTool.java +++ b/solr/core/src/java/org/apache/solr/cli/ExportTool.java @@ -254,7 +254,11 @@ DocsSink getSink() { abstract void exportDocs() throws Exception; void fetchUniqueKey() throws SolrServerException, IOException { - var builder = new HttpJettySolrClient.Builder().withOptionalBasicAuthCredentials(credentials); + var builder = + new HttpJettySolrClient.Builder() + .withIdleTimeout(CLIUtils.CLI_IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .withConnectionTimeout(CLIUtils.CLI_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .withOptionalBasicAuthCredentials(credentials); solrClient = new CloudSolrClient.Builder(List.of(baseurl)).withHttpClientBuilder(builder).build(); diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java index 11429805028d..95ad7f077260 100644 --- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java +++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -93,7 +94,10 @@ public void runImpl(CommandLine cli) throws Exception { cli.getOptionValue(COLLECTION_NAME_OPTION), cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION)); var builder = - new HttpJettySolrClient.Builder().withOptionalBasicAuthCredentials(params.credentials()); + new HttpJettySolrClient.Builder() + .withIdleTimeout(CLIUtils.CLI_IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .withConnectionTimeout(CLIUtils.CLI_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .withOptionalBasicAuthCredentials(params.credentials()); try (var cloudSolrClient = CLIUtils.getCloudSolrClient(solrConnection, builder)) { echoIfVerbose("Connecting to Solr at " + solrConnection.toString()); runCloudTool(cloudSolrClient, params); diff --git a/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java b/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java index 04c0ca745d45..d988963edde7 100644 --- a/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java +++ b/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java @@ -119,6 +119,8 @@ public void runCommand(String baseUrl, String root, String credentials) throws I var builder = new HttpJettySolrClient.Builder(URLUtil.extractBaseUrl(baseUrl)) .withDefaultCollection(URLUtil.extractCoreFromCoreUrl(baseUrl)) + .withIdleTimeout(CLIUtils.CLI_IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .withConnectionTimeout(CLIUtils.CLI_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) .withKeyStoreReloadInterval(-1, TimeUnit.SECONDS) .withOptionalBasicAuthCredentials(credentials); try (SolrClient client = builder.build()) { diff --git a/solr/core/src/java/org/apache/solr/cli/StreamTool.java b/solr/core/src/java/org/apache/solr/cli/StreamTool.java index 03e93b7f9162..4cb5973803a4 100644 --- a/solr/core/src/java/org/apache/solr/cli/StreamTool.java +++ b/solr/core/src/java/org/apache/solr/cli/StreamTool.java @@ -34,6 +34,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -303,7 +304,10 @@ void runStream( private StreamContext createStreamContext( CloudSolrClient.CloudSolrClientConnection solrConnection, String credentials) { var jettyClientBuilder = new HttpJettySolrClient.Builder(); - jettyClientBuilder.withOptionalBasicAuthCredentials(credentials); + jettyClientBuilder + .withIdleTimeout(CLIUtils.CLI_IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .withConnectionTimeout(CLIUtils.CLI_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .withOptionalBasicAuthCredentials(credentials); HttpJettySolrClient client = jettyClientBuilder.build(); // subclass so we can ensure our client is closed when the cache is closed From 86f4dce39315196e5c9587881b1fb95fd8020200 Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Wed, 7 Oct 2026 15:54:19 -0400 Subject: [PATCH 2/3] shorten up the connect timeout --- changelog/unreleased/cli-configurable-timeouts.yml | 3 ++- solr/core/src/java/org/apache/solr/cli/CLIUtils.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/changelog/unreleased/cli-configurable-timeouts.yml b/changelog/unreleased/cli-configurable-timeouts.yml index 20ad57b0af4e..f38581bbd13c 100644 --- a/changelog/unreleased/cli-configurable-timeouts.yml +++ b/changelog/unreleased/cli-configurable-timeouts.yml @@ -1,6 +1,7 @@ # See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc -title: bin/solr CLI commands now fail fast (15s connect / 30s idle) against an unresponsive +title: bin/solr CLI commands now fail fast (3s connect / 30s idle) against an unresponsive node instead of inheriting SolrJ's 60s/600s bulk-indexing defaults. type: changed authors: - name: Eric Pugh +links: https://github.com/apache/solr/pull/4955 diff --git a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java index 49ebb1c72969..4be1dc9f4b44 100644 --- a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java +++ b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java @@ -69,7 +69,7 @@ private CLIUtils() {} * long-running requests like bulk indexing or replica recovery; a CLI command run by a human at a * terminal should fail fast instead of hanging for minutes against an unresponsive node. */ - public static final int CLI_CONNECTION_TIMEOUT_SECONDS = 15; + public static final int CLI_CONNECTION_TIMEOUT_SECONDS = 3; public static final int CLI_IDLE_TIMEOUT_SECONDS = 30; From 17ce135aa600f91dad04c246c725e4f660a77b42 Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Wed, 7 Oct 2026 15:55:55 -0400 Subject: [PATCH 3/3] fix format --- changelog/unreleased/cli-configurable-timeouts.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog/unreleased/cli-configurable-timeouts.yml b/changelog/unreleased/cli-configurable-timeouts.yml index f38581bbd13c..46b6b4353a6d 100644 --- a/changelog/unreleased/cli-configurable-timeouts.yml +++ b/changelog/unreleased/cli-configurable-timeouts.yml @@ -4,4 +4,6 @@ title: bin/solr CLI commands now fail fast (3s connect / 30s idle) against an un type: changed authors: - name: Eric Pugh -links: https://github.com/apache/solr/pull/4955 +links: +- name: PR#4955 + url: https://github.com/apache/solr/pull/4955