This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 65a33d03ce4 SOLR-18607: Minor fixes to solr stop behavior and PIDs
(#5263)
65a33d03ce4 is described below
commit 65a33d03ce4fe6ddad8968b3d0de8c1aeab77165
Author: Jan Høydahl <[email protected]>
AuthorDate: Fri Nov 6 11:15:05 2026 +0100
SOLR-18607: Minor fixes to solr stop behavior and PIDs (#5263)
---
.../SOLR-18607-foreground-process-discovery.yml | 11 +++
solr/bin/solr | 41 +++++------
.../org/apache/solr/cli/SolrProcessManager.java | 3 +
.../src/java/org/apache/solr/cli/StatusTool.java | 12 +++-
solr/packaging/test/test_no_pid_file.bats | 79 ++++++++++++++++++++++
5 files changed, 125 insertions(+), 21 deletions(-)
diff --git a/changelog/unreleased/SOLR-18607-foreground-process-discovery.yml
b/changelog/unreleased/SOLR-18607-foreground-process-discovery.yml
new file mode 100644
index 00000000000..aa29774c80a
--- /dev/null
+++ b/changelog/unreleased/SOLR-18607-foreground-process-discovery.yml
@@ -0,0 +1,11 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+
+title: >
+ `solr stop -p <port>` again finds nodes started in the foreground, which
write no PID file.
+ `solr status` now lists every running node, and `solr stop --all` reports
when it stopped nothing.
+type: fixed
+authors:
+ - name: Jan Høydahl
+links:
+ - name: SOLR-18607
+ url: https://issues.apache.org/jira/browse/SOLR-18607
diff --git a/solr/bin/solr b/solr/bin/solr
index db487c04bfd..92d7608c8fb 100755
--- a/solr/bin/solr
+++ b/solr/bin/solr
@@ -477,6 +477,11 @@ function solr_pid_by_port() {
fi
}
+# find the pid of a running Solr process listening on the given port
+function solr_pid_by_ps() {
+ ps auxww | grep start\.jar | awk "/\-Dsolr\.port\.listen=$1/"' {print $2}' |
sort -r
+}
+
# extract the value of the -Dsolr.port.listen parameter from a running Solr
process
function solr_port_listen() {
SOLR_PID="$1"
@@ -945,21 +950,19 @@ fi
if [[ "$SCRIPT_CMD" == "stop" ]]; then
if $stop_all; then
none_stopped=true
- find "$SOLR_PID_DIR" -name "solr-*.pid" -type f | while read PIDF
- do
- NEXT_PID=$(cat "$PIDF")
- port=$(solr_port_listen "$NEXT_PID")
- if [ "$port" != "" ]; then
- stop_solr "$SOLR_SERVER_DIR" "$port" "$STOP_KEY" "$NEXT_PID"
- none_stopped=false
- fi
- rm -f "$PIDF"
- done
- # TODO: none_stopped doesn't get reflected across the subshell
- # This can be uncommented once we find a clean way out of it
- # if $none_stopped; then
- # echo -e "\nNo Solr nodes found to stop.\n"
- # fi
+ # Read from a process substitution, not a pipeline, so none_stopped
survives the loop
+ while read -r PIDF; do
+ NEXT_PID=$(cat "$PIDF")
+ port=$(solr_port_listen "$NEXT_PID")
+ if [ "$port" != "" ]; then
+ stop_solr "$SOLR_SERVER_DIR" "$port" "$STOP_KEY" "$NEXT_PID"
+ none_stopped=false
+ fi
+ rm -f "$PIDF"
+ done < <(find "$SOLR_PID_DIR" -name "solr-*.pid" -type f)
+ if $none_stopped; then
+ echo -e "\nNo Solr nodes found to stop.\n"
+ fi
exit
elif [[ -z "${PROVIDED_SOLR_PORT:-}" ]]; then
# not stopping all and don't have a port, but if we can find a single pid
file for Solr, then use that
@@ -1014,7 +1017,7 @@ if [[ "$SCRIPT_CMD" == "start" ]]; then
if [ -z "${SOLR_PID:-}" ]; then
# not found using the pid file ... but use ps to ensure not found
- SOLR_PID=$(ps auxww | grep start\.jar | awk
"/\-Dsolr\.port\.listen=$SOLR_PORT_LISTEN/"' {print $2}' | sort -r)
+ SOLR_PID=$(solr_pid_by_ps "$SOLR_PORT_LISTEN")
fi
if [ -n "${SOLR_PID:-}" ]; then
@@ -1027,7 +1030,7 @@ else
SOLR_PID=$(solr_pid_by_port "$SOLR_PORT_LISTEN")
if [ -z "$SOLR_PID" ]; then
# not found using the pid file ... but use ps to ensure not found
- SOLR_PID=$(ps auxww | grep start\.jar | awk
"/\-Djetty\.port=$SOLR_PORT_LISTEN/"' {print $2}' | sort -r)
+ SOLR_PID=$(solr_pid_by_ps "$SOLR_PORT_LISTEN")
fi
if [ "$SOLR_PID" != "" ]; then
stop_solr "$SOLR_SERVER_DIR" "$SOLR_PORT_LISTEN" "$STOP_KEY" "$SOLR_PID"
@@ -1400,7 +1403,7 @@ function start_solr() {
exit # subshell!
fi
else
- SOLR_PID=$(ps auxww | grep start\.jar | awk
"/\-Dsolr\.port\.listen=$SOLR_PORT_LISTEN/"' {print $2}' | sort -r)
+ SOLR_PID=$(solr_pid_by_ps "$SOLR_PORT_LISTEN")
echo -e "\nStarted Solr server on port $SOLR_PORT_LISTEN
(pid=$SOLR_PID). Happy searching!\n"
exit # subshell!
fi
@@ -1409,7 +1412,7 @@ function start_solr() {
else
echo -e "NOTE: Please install lsof as this script needs it to determine
if Solr is listening on port $SOLR_PORT_LISTEN."
sleep 10
- SOLR_PID=$(ps auxww | grep start\.jar | awk
"/\-Dsolr\.port\.listen=$SOLR_PORT_LISTEN/"' {print $2}' | sort -r)
+ SOLR_PID=$(solr_pid_by_ps "$SOLR_PORT_LISTEN")
echo -e "\nStarted Solr server on port $SOLR_PORT_LISTEN
(pid=$SOLR_PID). Happy searching!\n"
return;
fi
diff --git a/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
b/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
index e51e6c92f9d..1ee81cc85c8 100644
--- a/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
+++ b/solr/core/src/java/org/apache/solr/cli/SolrProcessManager.java
@@ -112,6 +112,9 @@ public class SolrProcessManager {
* @return a list of SolrProcesses for each running Solr instance
*/
public Collection<SolrProcess> scanSolrPidFiles() throws IOException {
+ if (!Files.isDirectory(pidDir)) {
+ return List.of();
+ }
List<SolrProcess> processes = new ArrayList<>();
try (Stream<Path> pidFiles =
Files.list(pidDir)
diff --git a/solr/core/src/java/org/apache/solr/cli/StatusTool.java
b/solr/core/src/java/org/apache/solr/cli/StatusTool.java
index a51e1ba7adc..d9f0c789577 100644
--- a/solr/core/src/java/org/apache/solr/cli/StatusTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/StatusTool.java
@@ -18,6 +18,7 @@
package org.apache.solr.cli;
import java.util.Collection;
+import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
@@ -241,8 +242,15 @@ public class StatusTool extends ToolBase {
}
}
- // No URL or port, scan for running processes
- Collection<SolrProcess> procs = processMgr.scanSolrPidFiles();
+ // No URL or port. Scanning the PID files first prunes stale ones, then
the live process list
+ // is authoritative, as it already is for "status -p <port>". Reporting
only PID files here
+ // would both miss nodes started in the foreground, which write none, and
be non-monotonic:
+ // stopping the last background node would make the listing grow.
+ processMgr.scanSolrPidFiles();
+ Collection<SolrProcess> procs =
+ processMgr.getAllRunning().stream()
+ .sorted(Comparator.comparingInt(SolrProcess::port))
+ .toList();
if (!procs.isEmpty()) {
for (SolrProcess process : procs) {
if (shortFormat) {
diff --git a/solr/packaging/test/test_no_pid_file.bats
b/solr/packaging/test/test_no_pid_file.bats
new file mode 100644
index 00000000000..e10e8475466
--- /dev/null
+++ b/solr/packaging/test/test_no_pid_file.bats
@@ -0,0 +1,79 @@
+#!/usr/bin/env bats
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Solr started in the foreground writes no PID file, which is the normal case
in containers.
+# Hiding the PID file of a node started in the background reproduces that
state.
+#
+# Note that "stop --all" and a bare "stop" intentionally remain limited to
nodes with a PID file
+# in SOLR_PID_DIR, so that they never reap Solr instances belonging to another
installation.
+# The PID file is therefore moved aside rather than deleted, and restored in
teardown, so that
+# teardown can still shut the node down the ordinary way instead of relying on
the code under
+# test or on an asynchronous kill.
+
+load bats_helper
+
+setup() {
+ common_clean_setup
+ hidden_pid_dir="${BATS_TEST_TMPDIR}/hidden-pid"
+ mkdir -p "${hidden_pid_dir}"
+}
+
+teardown() {
+ # save a snapshot of SOLR_HOME for failed tests
+ save_home_on_failure
+
+ # Put any hidden PID file back so that "stop --all" can reap the node
synchronously
+ mv "${hidden_pid_dir}"/solr-*.pid "${SOLR_PID_DIR}/" 2>/dev/null || true
+ SOLR_STOP_WAIT=30 solr stop --all >/dev/null 2>&1
+}
+
+start_solr_without_pid_file() {
+ solr start
+ solr assert --started "http://localhost:${SOLR_PORT}" --timeout 5000
+ mv "${SOLR_PID_DIR}"/solr-${SOLR_PORT}.pid "${hidden_pid_dir}/"
+}
+
+@test "status finds a running node that has no PID file" {
+ start_solr_without_pid_file
+
+ run solr status
+ assert_success
+ assert_output --partial "running on port ${SOLR_PORT}"
+}
+
+@test "status --short finds a running node that has no PID file" {
+ start_solr_without_pid_file
+
+ run solr status --short
+ assert_success
+ assert_output --partial "http://localhost:${SOLR_PORT}/solr"
+}
+
+@test "stop by port works without a PID file" {
+ start_solr_without_pid_file
+
+ run solr stop -p ${SOLR_PORT}
+ assert_success
+ refute_output --partial "No process found"
+ solr assert --not-started "http://localhost:${SOLR_PORT}" --timeout 5000
+}
+
+@test "stop --all reports when it stopped nothing" {
+ run solr stop --all
+ assert_success
+ assert_output --partial "No Solr nodes found to stop."
+}