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."
+}

Reply via email to