This is an automated email from the ASF dual-hosted git repository.

imbajin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hugegraph.git


The following commit(s) were added to refs/heads/master by this push:
     new 16382ccfb fix(pd,store): fg mode exit code propagation in startup 
scripts (#3047)
16382ccfb is described below

commit 16382ccfbd4ea871c8560bc622539060d4661a5e
Author: KAI <[email protected]>
AuthorDate: Thu Jun 4 18:55:42 2026 +0530

    fix(pd,store): fg mode exit code propagation in startup scripts (#3047)
    
    In foreground mode (-d false), start-hugegraph-pd.sh had no foreground
    branch — the script always backgrounded Java with exec ... &, wrote $!
    to the pid file, and exited 0, losing Java's exit code entirely.
    
    Fix: add DAEMON="true" default and -d flag to getopts. In the daemon
    branch, keep the existing exec ... & pattern. In the foreground branch,
    write $$ to the pid file before exec (exec replaces the shell with Java,
    so $$ == Java's PID after exec), then exec java without & so the process
    blocks and Java's exit code propagates out directly.
    
    No trap needed in the foreground branch — exec replaces the shell
    process with Java, so signals from Docker/systemd go directly to Java
    without a wrapper to forward through.
    
    Add test-start-hugegraph-pd.sh with 4 tests (daemon regression,
    foreground blocking, exit code propagation on SIGKILL, SIGTERM
    forwarding via exec) — 12 assertions, all pass after the fix.
    
    Baseline on unmodified code: 3 passed, 9 failed.
    After fix: 12 passed, 0 failed.
    
    Wire test into pd-store-ci.yml for the RocksDB backend.
    
    Related to: #3043
---
 .github/workflows/pd-store-ci.yml                  |  12 +
 .../src/assembly/static/bin/start-hugegraph-pd.sh  |  43 ++-
 .../src/assembly/travis/test-start-hugegraph-pd.sh | 352 +++++++++++++++++++++
 .../assembly/travis/test-start-hugegraph-store.sh  | 351 ++++++++++++++++++++
 .../assembly/static/bin/start-hugegraph-store.sh   |  48 ++-
 5 files changed, 779 insertions(+), 27 deletions(-)

diff --git a/.github/workflows/pd-store-ci.yml 
b/.github/workflows/pd-store-ci.yml
index 5e752d609..29ac9c3ac 100644
--- a/.github/workflows/pd-store-ci.yml
+++ b/.github/workflows/pd-store-ci.yml
@@ -107,6 +107,12 @@ jobs:
         run: |
           mvn clean package -U -Dmaven.javadoc.skip=true 
-Dmaven.test.skip=true -ntp --fail-at-end
 
+      - name: Run start-hugegraph-pd.sh foreground mode tests
+        run: |
+          VERSION=$(mvn help:evaluate -Dexpression=project.version -q 
-DforceStdout)
+          PD_DIR=hugegraph-pd/apache-hugegraph-pd-$VERSION/
+          $TRAVIS_DIR/test-start-hugegraph-pd.sh $PD_DIR
+
       - name: Prepare env and service
         run: |
           $TRAVIS_DIR/start-pd.sh
@@ -163,6 +169,12 @@ jobs:
         run: |
           mvn clean package -U -Dmaven.javadoc.skip=true 
-Dmaven.test.skip=true -ntp --fail-at-end
 
+      - name: Run start-hugegraph-store.sh foreground mode tests
+        run: |
+          VERSION=$(mvn help:evaluate -Dexpression=project.version -q 
-DforceStdout)
+          STORE_DIR=hugegraph-store/apache-hugegraph-store-$VERSION/
+          $TRAVIS_DIR/test-start-hugegraph-store.sh $STORE_DIR
+
       - name: Prepare env and service
         run: |
           $TRAVIS_DIR/start-pd.sh
diff --git 
a/hugegraph-pd/hg-pd-dist/src/assembly/static/bin/start-hugegraph-pd.sh 
b/hugegraph-pd/hg-pd-dist/src/assembly/static/bin/start-hugegraph-pd.sh
index 3bbdb1cf0..1329df227 100755
--- a/hugegraph-pd/hg-pd-dist/src/assembly/static/bin/start-hugegraph-pd.sh
+++ b/hugegraph-pd/hg-pd-dist/src/assembly/static/bin/start-hugegraph-pd.sh
@@ -26,14 +26,18 @@ fi
 if [ -z "$OPEN_TELEMETRY" ];then
   OPEN_TELEMETRY="false"
 fi
+if [ -z "$DAEMON" ]; then
+    DAEMON="true"
+fi
 
-while getopts "g:j:y:" arg; do
+while getopts "d:g:j:y:" arg; do
     case ${arg} in
         g) GC_OPTION="$OPTARG" ;;
         j) USER_OPTION="$OPTARG" ;;
         # Telemetry is used to collect metrics, traces and logs
+        d) DAEMON="$OPTARG" ;;
         y) OPEN_TELEMETRY="$OPTARG" ;;
-        ?) echo "USAGE: $0 [-g g1] [-j xxx] [-y true|false]" && exit 1 ;;
+        ?) echo "USAGE: $0 [-d true|false] [-g g1] [-j xxx] [-y true|false]" 
&& exit 1 ;;
     esac
 done
 
@@ -163,20 +167,33 @@ if [ $(ps -ef|grep -v grep| grep java|grep -cE ${CONF}) 
-ne 0 ]; then
    echo "HugeGraphPDServer is already running..."
    exit 0
 fi
-echo "Starting HugeGraphPDServer..."
 
 JVM_OPTIONS="-Dlog4j.configurationFile=${CONF}/log4j2.xml 
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager"
 
 # Turn on security check
-if [[ "${STDOUT_MODE:-false}" == "true" ]]; then
-    exec ${JAVA} -Dname="HugeGraphPD" ${JVM_OPTIONS} ${JAVA_OPTIONS} -jar \
-        -Dspring.config.location=${CONF}/application.yml 
${LIB}/hg-pd-service-*.jar &
+if [[ $DAEMON == "true" ]]; then
+    echo "Starting HugeGraphPDServer in daemon mode..."
+    if [[ "${STDOUT_MODE:-false}" == "true" ]]; then
+        exec ${JAVA} -Dname="HugeGraphPD" ${JVM_OPTIONS} ${JAVA_OPTIONS} -jar \
+            -Dspring.config.location=${CONF}/application.yml 
${LIB}/hg-pd-service-*.jar &
+    else
+        exec ${JAVA} -Dname="HugeGraphPD" ${JVM_OPTIONS} ${JAVA_OPTIONS} -jar \
+            -Dspring.config.location=${CONF}/application.yml 
${LIB}/hg-pd-service-*.jar >> ${OUTPUT} 2>&1 &
+    fi
+    PID="$!"
+    # Write pid to file
+    echo "$PID" > "$PID_FILE"
+    echo "[+pid] $PID"
 else
-    exec ${JAVA} -Dname="HugeGraphPD" ${JVM_OPTIONS} ${JAVA_OPTIONS} -jar \
-        -Dspring.config.location=${CONF}/application.yml 
${LIB}/hg-pd-service-*.jar >> ${OUTPUT} 2>&1 &
+    echo "Starting HugeGraphPDServer in foreground mode..."
+    # Write $$ before exec — exec replaces this shell with Java, so $$ becomes 
Java's PID
+    echo "$$" > "$PID_FILE"
+    echo "[+pid] $$"
+    if [[ "${STDOUT_MODE:-false}" == "true" ]]; then
+        exec ${JAVA} -Dname="HugeGraphPD" ${JVM_OPTIONS} ${JAVA_OPTIONS} -jar \
+            -Dspring.config.location=${CONF}/application.yml 
${LIB}/hg-pd-service-*.jar
+    else
+        exec ${JAVA} -Dname="HugeGraphPD" ${JVM_OPTIONS} ${JAVA_OPTIONS} -jar \
+            -Dspring.config.location=${CONF}/application.yml 
${LIB}/hg-pd-service-*.jar >> ${OUTPUT} 2>&1
+    fi
 fi
-
-PID="$!"
-# Write pid to file
-echo "$PID" > "$PID_FILE"
-echo "[+pid] $PID"
diff --git 
a/hugegraph-server/hugegraph-dist/src/assembly/travis/test-start-hugegraph-pd.sh
 
b/hugegraph-server/hugegraph-dist/src/assembly/travis/test-start-hugegraph-pd.sh
new file mode 100755
index 000000000..db3428b13
--- /dev/null
+++ 
b/hugegraph-server/hugegraph-dist/src/assembly/travis/test-start-hugegraph-pd.sh
@@ -0,0 +1,352 @@
+#!/bin/bash
+#
+# 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.
+#
+# test-start-hugegraph-pd.sh — Tests for start-hugegraph-pd.sh foreground mode 
fix
+#
+# Baseline (unmodified code):  Tests 1 PASS — Tests 2, 3, 4 FAIL
+# After chunk 2 fix:           All 4 tests PASS
+#
+# Usage: ./test-start-hugegraph-pd.sh [path-to-pd-dist-root]
+#   path-to-pd-dist-root: path to extracted PD dist e.g.
+#                         hugegraph-pd/apache-hugegraph-pd-1.7.0/
+#                         defaults to current directory if not provided
+
+set -uo pipefail
+
+PD_ROOT="${1:-$(pwd)}"
+BIN="$PD_ROOT/bin"
+START_SCRIPT="$BIN/start-hugegraph-pd.sh"
+PID_FILE="$BIN/pid"
+PD_URL="http://localhost:8620";
+STARTUP_WAIT=60   # seconds to wait for PD HTTP to respond
+SETTLE_WAIT=5     # seconds after kill before checking exit
+WAIT_TIMEOUT=30   # seconds for wait_script_exit timeout
+
+PASS=0
+FAIL=0
+ERRORS=()
+
+GREEN='\033[0;32m'
+RED='\033[0;31m'
+YELLOW='\033[1;33m'
+NC='\033[0m'
+
+pass() {
+    echo -e "${GREEN}  PASS${NC} $1"
+    PASS=$((PASS + 1))
+}
+
+fail() {
+    echo -e "${RED}  FAIL${NC} $1"
+    ERRORS+=("$1")
+    FAIL=$((FAIL + 1))
+}
+
+info() {
+    echo -e "${YELLOW}  ....${NC} $1"
+}
+
+section() {
+    echo ""
+    echo "── $1 ──"
+}
+
+cleanup() {
+    info "Cleaning up..."
+    if [[ -s "$PID_FILE" ]]; then
+        kill "$(cat "$PID_FILE")" 2>/dev/null || true
+    fi
+    rm -f "$PID_FILE"
+    rm -rf "$PD_ROOT/logs/"
+    # kill anything still holding the PD port
+    lsof -ti :8620 | xargs kill -9 2>/dev/null || true
+    lsof -ti :8686 | xargs kill -9 2>/dev/null || true
+    sleep 3
+}
+
+# Wait until PD health endpoint responds or timeout
+wait_for_pd() {
+    local elapsed=0
+    while (( elapsed < STARTUP_WAIT )); do
+        local status
+        status=$(curl -s -o /dev/null -w "%{http_code}" \
+            "$PD_URL/v1/health" 2>/dev/null || echo "000")
+        if [[ "$status" == "200" || "$status" == "401" ]]; then
+            return 0
+        fi
+        sleep 2
+        elapsed=$((elapsed + 2))
+    done
+    return 1
+}
+
+# Wait until bin/pid is non-empty or timeout
+wait_for_pid_file() {
+    local elapsed=0
+    while [[ ! -s "$PID_FILE" ]] && (( elapsed < 30 )); do
+        sleep 1
+        elapsed=$((elapsed + 1))
+    done
+}
+
+# Wait for a background script PID to exit with timeout
+# Must be called from the same shell that spawned script_pid
+wait_script_exit() {
+    local script_pid="$1"
+    ( sleep "$WAIT_TIMEOUT" && kill "$script_pid" 2>/dev/null ) &
+    local killer_pid=$!
+    wait "$script_pid" 2>/dev/null
+    local exit_code=$?
+    kill "$killer_pid" 2>/dev/null || true
+    wait "$killer_pid" 2>/dev/null || true
+    return $exit_code
+}
+
+# ── preflight 
─────────────────────────────────────────────────────────────────
+
+echo ""
+echo "start-hugegraph-pd.sh chunk 2 test suite"
+echo "root: $PD_ROOT"
+echo ""
+
+if [[ ! -f "$START_SCRIPT" ]]; then
+    echo -e "${RED}ERROR:${NC} $START_SCRIPT not found."
+    echo "       Pass the PD dist root as \$1"
+    exit 1
+fi
+
+for tool in lsof curl java; do
+    if ! command -v "$tool" >/dev/null 2>&1; then
+        echo "SKIP: required tool '$tool' not found — skipping test suite"
+        exit 0
+    fi
+done
+
+cleanup
+
+# ── test 1: daemon mode regression ───────────────────────────────────────────
+
+section "Test 1 — daemon mode regression"
+
+info "Starting in daemon mode (no -d flag, default behavior)..."
+"$START_SCRIPT" >/dev/null 2>&1 &
+SCRIPT_PID=$!
+
+# daemon mode: script backgrounds Java and exits quickly
+sleep 5
+if ! ps -p "$SCRIPT_PID" >/dev/null 2>&1; then
+    pass "script exited after backgrounding Java (daemon mode)"
+else
+    info "script still running after 5s (may still be starting)"
+    wait_script_exit "$SCRIPT_PID" || true
+fi
+
+wait_for_pid_file
+DAEMON_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
+
+if [[ -n "$DAEMON_PID" ]]; then
+    pass "bin/pid written with PID $DAEMON_PID"
+else
+    fail "bin/pid is empty or missing after daemon start"
+fi
+
+if [[ -n "$DAEMON_PID" ]] && ps -p "$DAEMON_PID" >/dev/null 2>&1; then
+    pass "Java process is running (pid $DAEMON_PID)"
+else
+    fail "Java process not found for pid '$DAEMON_PID'"
+fi
+
+info "Waiting up to ${STARTUP_WAIT}s for PD health endpoint..."
+if wait_for_pd; then
+    pass "PD health endpoint responding at $PD_URL/v1/health"
+else
+    fail "PD health endpoint not responding after ${STARTUP_WAIT}s"
+fi
+
+cleanup
+
+# ── test 2: foreground mode blocks ───────────────────────────────────────────
+
+section "Test 2 — foreground mode blocks until Java exits"
+
+info "Starting in foreground mode (-d false)..."
+"$START_SCRIPT" -d false >/dev/null 2>&1 &
+SCRIPT_PID=$!
+
+info "Waiting up to ${STARTUP_WAIT}s for PD to come up..."
+if wait_for_pd; then
+    info "PD is up"
+else
+    info "PD did not respond — continuing to check blocking behavior"
+fi
+
+# foreground mode: script should still be running (blocked on Java)
+if ps -p "$SCRIPT_PID" >/dev/null 2>&1; then
+    pass "script is still running (blocking correctly)"
+else
+    fail "script exited early — foreground mode is not blocking"
+fi
+
+wait_for_pid_file
+FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
+
+if [[ -n "$FG_PID" ]]; then
+    info "pid file written with PID $FG_PID"
+else
+    info "pid file not written (expected before chunk 2 fix)"
+fi
+
+# cleanup: kill Java or the script
+if [[ -n "$FG_PID" ]]; then
+    kill "$FG_PID" 2>/dev/null || true
+else
+    kill "$SCRIPT_PID" 2>/dev/null || true
+fi
+sleep "$SETTLE_WAIT"
+
+if ! ps -p "$SCRIPT_PID" >/dev/null 2>&1; then
+    info "script exited after Java was killed"
+else
+    kill "$SCRIPT_PID" 2>/dev/null || true
+fi
+
+cleanup
+
+# ── test 3: exit code propagates ─────────────────────────────────────────────
+
+section "Test 3 — exit code propagates from Java"
+
+info "Starting in foreground mode (-d false)..."
+"$START_SCRIPT" -d false >/dev/null 2>&1 &
+SCRIPT_PID=$!
+
+info "Waiting up to ${STARTUP_WAIT}s for PD..."
+if wait_for_pd; then
+    info "PD is up"
+else
+    info "PD did not respond — continuing"
+fi
+
+wait_for_pid_file
+FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
+
+if [[ -n "$FG_PID" ]]; then
+    pass "bin/pid written with PID $FG_PID"
+else
+    fail "bin/pid is empty or missing in foreground mode"
+fi
+
+if [[ -n "$FG_PID" ]] && ps -p "$FG_PID" >/dev/null 2>&1; then
+    pass "Java process running (pid $FG_PID)"
+else
+    fail "Java process not found for pid '$FG_PID'"
+fi
+
+if [[ -z "$FG_PID" ]]; then
+    fail "could not get PID — skipping exit code check"
+    kill "$SCRIPT_PID" 2>/dev/null || true
+else
+    info "Hard-killing Java with SIGKILL (pid $FG_PID)..."
+    kill -9 "$FG_PID" 2>/dev/null || true
+
+    wait_script_exit "$SCRIPT_PID"
+    ACTUAL_EXIT=$?
+
+    if [[ $ACTUAL_EXIT -ne 0 ]]; then
+        pass "script exited non-zero ($ACTUAL_EXIT) after SIGKILL — Docker 
restart will fire"
+    else
+        fail "script exited 0 after SIGKILL — Docker would NOT restart (exit 
code lost)"
+    fi
+
+    if [[ $ACTUAL_EXIT -eq 137 ]]; then
+        pass "exit code is 137 (128+9 for SIGKILL) — correctly propagated"
+    elif [[ $ACTUAL_EXIT -ne 0 ]]; then
+        info "exit code was $ACTUAL_EXIT (not 137 — may be shell-wrapped, 
acceptable if non-zero)"
+    fi
+fi
+
+cleanup
+
+# ── test 4: SIGTERM forwarded to Java ────────────────────────────────────────
+
+section "Test 4 — SIGTERM forwarded to Java in foreground mode"
+
+info "Starting in foreground mode (-d false)..."
+"$START_SCRIPT" -d false >/dev/null 2>&1 &
+SCRIPT_PID=$!
+
+info "Waiting up to ${STARTUP_WAIT}s for PD..."
+if wait_for_pd; then
+    info "PD is up"
+else
+    info "PD did not respond — continuing"
+fi
+
+wait_for_pid_file
+FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
+
+if [[ -z "$FG_PID" ]]; then
+    fail "could not get Java PID — skipping SIGTERM check"
+    kill "$SCRIPT_PID" 2>/dev/null || true
+else
+    info "Sending SIGTERM to process (pid $SCRIPT_PID)..."
+    # In foreground mode with exec, SCRIPT_PID == FG_PID after exec replaces 
the shell
+    # Signal goes directly to Java — no wrapper to forward through
+    kill -TERM "$SCRIPT_PID" 2>/dev/null || true
+
+    wait_script_exit "$SCRIPT_PID"
+    ACTUAL_EXIT=$?
+
+    if ! ps -p "$FG_PID" >/dev/null 2>&1; then
+        pass "Java process terminated after SIGTERM"
+    else
+        fail "Java process still running after SIGTERM — signal not delivered"
+        kill "$FG_PID" 2>/dev/null || true
+    fi
+
+    if [[ $ACTUAL_EXIT -ne 0 ]]; then
+        pass "process exited non-zero ($ACTUAL_EXIT) after SIGTERM"
+    else
+        fail "process exited 0 after SIGTERM — unexpected"
+    fi
+
+    if [[ $ACTUAL_EXIT -eq 143 ]]; then
+        pass "exit code is 143 (128+15 for SIGTERM) — correctly propagated"
+    elif [[ $ACTUAL_EXIT -ne 0 ]]; then
+        info "exit code was $ACTUAL_EXIT (not 143 — acceptable if non-zero)"
+    fi
+fi
+
+cleanup
+
+# ── summary 
───────────────────────────────────────────────────────────────────
+
+echo ""
+echo "════════════════════════════════"
+echo -e "  Results: ${GREEN}$PASS passed${NC}  ${RED}$FAIL failed${NC}"
+echo "════════════════════════════════"
+
+if [[ ${#ERRORS[@]} -gt 0 ]]; then
+    echo ""
+    echo "Failed tests:"
+    for err in "${ERRORS[@]}"; do
+        echo -e "  ${RED}✗${NC} $err"
+    done
+fi
+
+echo ""
+[[ $FAIL -eq 0 ]] && exit 0 || exit 1
diff --git 
a/hugegraph-server/hugegraph-dist/src/assembly/travis/test-start-hugegraph-store.sh
 
b/hugegraph-server/hugegraph-dist/src/assembly/travis/test-start-hugegraph-store.sh
new file mode 100755
index 000000000..7da932376
--- /dev/null
+++ 
b/hugegraph-server/hugegraph-dist/src/assembly/travis/test-start-hugegraph-store.sh
@@ -0,0 +1,351 @@
+#!/bin/bash
+#
+# 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.
+#
+# test-start-hugegraph-store.sh — Tests for start-hugegraph-store.sh 
foreground mode fix
+#
+# Baseline (unmodified code):  Test 1 PASS — Tests 2, 3, 4 FAIL
+# After chunk 3 fix:           All 4 tests PASS
+#
+# Usage: ./test-start-hugegraph-store.sh [path-to-store-dist-root]
+
+set -uo pipefail
+
+STORE_ROOT="${1:-$(pwd)}"
+BIN="$STORE_ROOT/bin"
+START_SCRIPT="$BIN/start-hugegraph-store.sh"
+STOP_SCRIPT="$BIN/stop-hugegraph-store.sh"
+PID_FILE="$BIN/pid"
+STORE_URL="http://localhost:8520";
+PD_URL="http://localhost:8620";
+STARTUP_WAIT=60   # seconds to wait for Store HTTP to respond
+SETTLE_WAIT=5     # seconds after kill before checking exit
+WAIT_TIMEOUT=30   # seconds for wait_script_exit timeout
+
+PASS=0
+FAIL=0
+ERRORS=()
+
+GREEN='\033[0;32m'
+RED='\033[0;31m'
+YELLOW='\033[1;33m'
+NC='\033[0m'
+
+pass() { echo -e "${GREEN}  PASS${NC} $1"; PASS=$((PASS + 1)); }
+fail() { echo -e "${RED}  FAIL${NC} $1"; ERRORS+=("$1"); FAIL=$((FAIL + 1)); }
+info() { echo -e "${YELLOW}  ....${NC} $1"; }
+section() { echo ""; echo "── $1 ──"; }
+
+cleanup() {
+    info "Cleaning up..."
+    if [[ -s "$PID_FILE" ]]; then
+        kill "$(cat "$PID_FILE")" 2>/dev/null || true
+    fi
+    rm -f "$PID_FILE"
+    rm -rf "$STORE_ROOT/logs/"
+    # kill anything holding Store ports (8520 REST, 8510 raft, 8500 gRPC)
+    lsof -ti :8520 | xargs kill -9 2>/dev/null || true
+    lsof -ti :8510 | xargs kill -9 2>/dev/null || true
+    lsof -ti :8500 | xargs kill -9 2>/dev/null || true
+    sleep 3
+}
+
+wait_for_store() {
+    local elapsed=0
+    while (( elapsed < STARTUP_WAIT )); do
+        local status
+        status=$(curl -s -o /dev/null -w "%{http_code}" \
+            "$STORE_URL/v1/health" 2>/dev/null || echo "000")
+        if [[ "$status" == "200" || "$status" == "401" ]]; then
+            return 0
+        fi
+        sleep 2
+        elapsed=$((elapsed + 2))
+    done
+    return 1
+}
+
+wait_for_pid_file() {
+    local elapsed=0
+    while [[ ! -s "$PID_FILE" ]] && (( elapsed < 30 )); do
+        sleep 1
+        elapsed=$((elapsed + 1))
+    done
+}
+
+wait_script_exit() {
+    local script_pid="$1"
+    ( sleep "$WAIT_TIMEOUT" && kill "$script_pid" 2>/dev/null ) &
+    local killer_pid=$!
+    wait "$script_pid" 2>/dev/null
+    local exit_code=$?
+    kill "$killer_pid" 2>/dev/null || true
+    wait "$killer_pid" 2>/dev/null || true
+    return $exit_code
+}
+
+# ── preflight 
─────────────────────────────────────────────────────────────────
+
+echo ""
+echo "start-hugegraph-store.sh chunk 3 test suite"
+echo "root: $STORE_ROOT"
+echo ""
+
+if [[ ! -f "$START_SCRIPT" ]]; then
+    echo -e "${RED}ERROR:${NC} $START_SCRIPT not found."
+    echo "       Pass the Store dist root as \$1"
+    exit 1
+fi
+
+for tool in lsof curl java; do
+    if ! command -v "$tool" >/dev/null 2>&1; then
+        echo "SKIP: required tool '$tool' not found — skipping test suite"
+        exit 0
+    fi
+done
+
+# Store ulimit check (safely handling "unlimited" string)
+LIMIT_N=$(ulimit -n)
+if [[ "$LIMIT_N" != "unlimited" ]]; then
+    if (( LIMIT_N < 1024 )); then
+        echo "SKIP: ulimit -n is $LIMIT_N — store requires >= 1024. Run: 
ulimit -n 1024"
+        exit 0
+    fi
+fi
+
+# Check if PD is running, warn if not (Store depends on it for HTTP health 
check)
+PD_RUNNING=false
+PD_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$PD_URL/v1/health" 
2>/dev/null || echo "000")
+if [[ "$PD_STATUS" == "200" || "$PD_STATUS" == "401" ]]; then
+    PD_RUNNING=true
+    info "PD is running — Store should become healthy"
+else
+    info "PD is NOT running at $PD_URL — Store will start but health checks 
may timeout (Acceptable for this test)"
+fi
+
+cleanup
+
+# ── test 1: daemon mode regression ───────────────────────────────────────────
+
+section "Test 1 — daemon mode regression"
+
+info "Starting in daemon mode (no -d flag, default behavior)..."
+"$START_SCRIPT" >/dev/null 2>&1 &
+SCRIPT_PID=$!
+
+sleep 5
+if ! ps -p "$SCRIPT_PID" >/dev/null 2>&1; then
+    pass "script exited after backgrounding Java (daemon mode)"
+else
+    info "script still running after 5s (may still be starting)"
+    wait_script_exit "$SCRIPT_PID" || true
+fi
+
+wait_for_pid_file
+DAEMON_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
+
+if [[ -n "$DAEMON_PID" ]]; then
+    pass "bin/pid written with PID $DAEMON_PID"
+else
+    fail "bin/pid is empty or missing after daemon start"
+fi
+
+if [[ -n "$DAEMON_PID" ]] && ps -p "$DAEMON_PID" >/dev/null 2>&1; then
+    pass "Java process is running (pid $DAEMON_PID)"
+else
+    fail "Java process not found for pid '$DAEMON_PID'"
+fi
+
+info "Waiting up to ${STARTUP_WAIT}s for Store health endpoint..."
+if wait_for_store; then
+    pass "Store health endpoint responding at $STORE_URL/v1/health"
+else
+    if [[ "$PD_RUNNING" == "true" ]]; then
+        fail "Store health endpoint not responding even though PD is up"
+    else
+        info "Store health endpoint not responding (Expected since PD is not 
running)"
+    fi
+fi
+
+cleanup
+
+# ── test 2: foreground mode blocks ───────────────────────────────────────────
+
+section "Test 2 — foreground mode blocks until Java exits"
+
+info "Starting in foreground mode (-d false)..."
+"$START_SCRIPT" -d false >/dev/null 2>&1 &
+SCRIPT_PID=$!
+
+info "Waiting up to ${STARTUP_WAIT}s for Store to come up..."
+if wait_for_store; then
+    info "Store is up"
+else
+    info "Store did not respond — continuing to check blocking behavior"
+fi
+
+if ps -p "$SCRIPT_PID" >/dev/null 2>&1; then
+    pass "script is still running (blocking correctly)"
+else
+    fail "script exited early — foreground mode is not blocking"
+fi
+
+wait_for_pid_file
+FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
+
+if [[ -n "$FG_PID" ]]; then
+    info "pid file written with PID $FG_PID"
+else
+    info "pid file not written (expected before chunk 3 fix)"
+fi
+
+if [[ -n "$FG_PID" ]]; then
+    kill "$FG_PID" 2>/dev/null || true
+else
+    kill "$SCRIPT_PID" 2>/dev/null || true
+fi
+sleep "$SETTLE_WAIT"
+
+if ! ps -p "$SCRIPT_PID" >/dev/null 2>&1; then
+    info "script exited after Java was killed"
+else
+    kill "$SCRIPT_PID" 2>/dev/null || true
+fi
+
+cleanup
+
+# ── test 3: exit code propagates ─────────────────────────────────────────────
+
+section "Test 3 — exit code propagates from Java"
+
+info "Starting in foreground mode (-d false)..."
+"$START_SCRIPT" -d false >/dev/null 2>&1 &
+SCRIPT_PID=$!
+
+info "Waiting up to ${STARTUP_WAIT}s for Store..."
+if wait_for_store; then
+    info "Store is up"
+else
+    info "Store did not respond — continuing"
+fi
+
+wait_for_pid_file
+FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
+
+if [[ -n "$FG_PID" ]]; then
+    pass "bin/pid written with PID $FG_PID"
+else
+    fail "bin/pid is empty or missing in foreground mode"
+fi
+
+if [[ -n "$FG_PID" ]] && ps -p "$FG_PID" >/dev/null 2>&1; then
+    pass "Java process running (pid $FG_PID)"
+else
+    fail "Java process not found for pid '$FG_PID'"
+fi
+
+if [[ -z "$FG_PID" ]]; then
+    fail "could not get PID — skipping exit code check"
+    kill "$SCRIPT_PID" 2>/dev/null || true
+else
+    info "Hard-killing Java with SIGKILL (pid $FG_PID)..."
+    kill -9 "$FG_PID" 2>/dev/null || true
+
+    wait_script_exit "$SCRIPT_PID"
+    ACTUAL_EXIT=$?
+
+    if [[ $ACTUAL_EXIT -ne 0 ]]; then
+        pass "script exited non-zero ($ACTUAL_EXIT) after SIGKILL"
+    else
+        fail "script exited 0 after SIGKILL — Docker would NOT restart"
+    fi
+
+    if [[ $ACTUAL_EXIT -eq 137 ]]; then
+        pass "exit code is 137 (128+9 for SIGKILL) — correctly propagated"
+    elif [[ $ACTUAL_EXIT -ne 0 ]]; then
+        info "exit code was $ACTUAL_EXIT (not 137 — acceptable if non-zero)"
+    fi
+fi
+
+cleanup
+
+# ── test 4: SIGTERM forwarded to Java ────────────────────────────────────────
+
+section "Test 4 — SIGTERM forwarded to Java in foreground mode"
+
+info "Starting in foreground mode (-d false)..."
+"$START_SCRIPT" -d false >/dev/null 2>&1 &
+SCRIPT_PID=$!
+
+info "Waiting up to ${STARTUP_WAIT}s for Store..."
+if wait_for_store; then
+    info "Store is up"
+else
+    info "Store did not respond — continuing"
+fi
+
+wait_for_pid_file
+FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
+
+if [[ -z "$FG_PID" ]]; then
+    fail "could not get Java PID — skipping SIGTERM check"
+    kill "$SCRIPT_PID" 2>/dev/null || true
+else
+    info "Sending SIGTERM to process (pid $SCRIPT_PID)..."
+    kill -TERM "$SCRIPT_PID" 2>/dev/null || true
+
+    wait_script_exit "$SCRIPT_PID"
+    ACTUAL_EXIT=$?
+
+    if ! ps -p "$FG_PID" >/dev/null 2>&1; then
+        pass "Java process terminated after SIGTERM"
+    else
+        fail "Java process still running after SIGTERM — signal not delivered"
+        kill "$FG_PID" 2>/dev/null || true
+    fi
+
+    if [[ $ACTUAL_EXIT -ne 0 ]]; then
+        pass "process exited non-zero ($ACTUAL_EXIT) after SIGTERM"
+    else
+        fail "process exited 0 after SIGTERM — unexpected"
+    fi
+
+    if [[ $ACTUAL_EXIT -eq 143 ]]; then
+        pass "exit code is 143 (128+15 for SIGTERM) — correctly propagated"
+    elif [[ $ACTUAL_EXIT -ne 0 ]]; then
+        info "exit code was $ACTUAL_EXIT (not 143 — acceptable if non-zero)"
+    fi
+fi
+
+cleanup
+
+# ── summary 
───────────────────────────────────────────────────────────────────
+
+echo ""
+echo "════════════════════════════════"
+echo -e "  Results: ${GREEN}$PASS passed${NC}  ${RED}$FAIL failed${NC}"
+echo "════════════════════════════════"
+
+if [[ ${#ERRORS[@]} -gt 0 ]]; then
+    echo ""
+    echo "Failed tests:"
+    for err in "${ERRORS[@]}"; do
+        echo -e "  ${RED}✗${NC} $err"
+    done
+fi
+
+echo ""
+[[ $FAIL -eq 0 ]] && exit 0 || exit 1
diff --git 
a/hugegraph-store/hg-store-dist/src/assembly/static/bin/start-hugegraph-store.sh
 
b/hugegraph-store/hg-store-dist/src/assembly/static/bin/start-hugegraph-store.sh
index 17245e8b4..5a11eeab3 100755
--- 
a/hugegraph-store/hg-store-dist/src/assembly/static/bin/start-hugegraph-store.sh
+++ 
b/hugegraph-store/hg-store-dist/src/assembly/static/bin/start-hugegraph-store.sh
@@ -100,14 +100,18 @@ fi
 if [ -z "$OPEN_TELEMETRY" ];then
   OPEN_TELEMETRY="false"
 fi
+if [ -z "$DAEMON" ]; then
+    DAEMON="true"
+fi
 
-while getopts "g:j:y:" arg; do
+while getopts "d:g:j:y:" arg; do
     case ${arg} in
         g) GC_OPTION="$OPTARG" ;;
         j) USER_OPTION="$OPTARG" ;;
         # Telemetry is used to collect metrics, traces and logs
         y) OPEN_TELEMETRY="$OPTARG" ;;
-        ?) echo "USAGE: $0 [-g g1] [-j xxx] [-y true|false]" && exit 1 ;;
+        d) DAEMON="$OPTARG" ;;
+        ?) echo "USAGE: $0 [-d true|false] [-g g1] [-j xxx] [-y true|false]" 
&& exit 1 ;;
     esac
 done
 
@@ -222,17 +226,33 @@ fi
 echo "Starting HG-StoreServer..."
 
 # Turn on security check
-if [[ "${STDOUT_MODE:-false}" == "true" ]]; then
-    exec ${JAVA} -Dname="HugeGraphStore" ${JVM_OPTIONS} ${JAVA_OPTIONS} -jar \
-        -Dspring.config.location=${CONF}/application.yml \
-        ${LIB}/hg-store-node-*.jar &
+if [[ $DAEMON == "true" ]]; then
+    echo "Starting HugeGraphStoreServer in daemon mode..."
+    if [[ "${STDOUT_MODE:-false}" == "true" ]]; then
+        exec ${JAVA} -Dname="HugeGraphStore" ${JVM_OPTIONS} ${JAVA_OPTIONS} 
-jar \
+            -Dspring.config.location=${CONF}/application.yml \
+            ${LIB}/hg-store-node-*.jar &
+    else
+        exec ${JAVA} -Dname="HugeGraphStore" ${JVM_OPTIONS} ${JAVA_OPTIONS} 
-jar \
+            -Dspring.config.location=${CONF}/application.yml \
+            ${LIB}/hg-store-node-*.jar >> ${OUTPUT} 2>&1 &
+    fi
+    PID="$!"
+    # Write pid to file
+    echo "$PID" > "$PID_FILE"
+    echo "[+pid] $PID"
 else
-    exec ${JAVA} -Dname="HugeGraphStore" ${JVM_OPTIONS} ${JAVA_OPTIONS} -jar \
-        -Dspring.config.location=${CONF}/application.yml \
-        ${LIB}/hg-store-node-*.jar >> ${OUTPUT} 2>&1 &
+    echo "Starting HugeGraphStoreServer in foreground mode..."
+    # Write $$ before exec — exec replaces this shell with Java, so $$ becomes 
Java's PID
+    echo "$$" > "$PID_FILE"
+    echo "[+pid] $$"
+    if [[ "${STDOUT_MODE:-false}" == "true" ]]; then
+        exec ${JAVA} -Dname="HugeGraphStore" ${JVM_OPTIONS} ${JAVA_OPTIONS} 
-jar \
+            -Dspring.config.location=${CONF}/application.yml \
+            ${LIB}/hg-store-node-*.jar
+    else
+        exec ${JAVA} -Dname="HugeGraphStore" ${JVM_OPTIONS} ${JAVA_OPTIONS} 
-jar \
+            -Dspring.config.location=${CONF}/application.yml \
+            ${LIB}/hg-store-node-*.jar >> ${OUTPUT} 2>&1
+    fi
 fi
-
-PID="$!"
-# Write pid to file
-echo "$PID" > "$PID_FILE"
-echo "[+pid] $PID"

Reply via email to