Skip to content

Commit e49eecd

Browse files
committed
fix(store): fix foreground mode exit code propagation in start-hugegraph-store.sh
Same structural bug as start-hugegraph-pd.sh: no foreground branch, 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. Daemon branch keeps exec ... & with $! as before. Foreground branch writes $$ to the pid file before exec, then exec java without & so the process blocks and Java's exit code propagates out directly. Add test-start-hugegraph-store.sh with 4 tests (daemon regression, foreground blocking, exit code propagation on SIGKILL, SIGTERM forwarding via exec) and wire into pd-store-ci.yml. Note: Store health check is skipped when PD is not running — this is expected and handled gracefully in the test. Baseline on unmodified code: 3 passed, 5 failed. After fix: 11 passed, 0 failed. Related to: #3043
1 parent 14e8905 commit e49eecd

3 files changed

Lines changed: 396 additions & 14 deletions

File tree

.github/workflows/pd-store-ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ jobs:
107107
run: |
108108
mvn clean package -U -Dmaven.javadoc.skip=true -Dmaven.test.skip=true -ntp --fail-at-end
109109
110+
- name: Run start-hugegraph-pd.sh foreground mode tests
111+
run: |
112+
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
113+
PD_DIR=hugegraph-pd/apache-hugegraph-pd-$VERSION/
114+
$TRAVIS_DIR/test-start-hugegraph-pd.sh $PD_DIR
115+
110116
- name: Prepare env and service
111117
run: |
112118
$TRAVIS_DIR/start-pd.sh
@@ -163,6 +169,12 @@ jobs:
163169
run: |
164170
mvn clean package -U -Dmaven.javadoc.skip=true -Dmaven.test.skip=true -ntp --fail-at-end
165171
172+
- name: Run start-hugegraph-store.sh foreground mode tests
173+
run: |
174+
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
175+
STORE_DIR=hugegraph-store/apache-hugegraph-store-$VERSION/
176+
$TRAVIS_DIR/test-start-hugegraph-store.sh $STORE_DIR
177+
166178
- name: Prepare env and service
167179
run: |
168180
$TRAVIS_DIR/start-pd.sh
Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
#!/bin/bash
2+
#
3+
# Licensed to the Apache Software Foundation (ASF) under one or more
4+
# contributor license agreements. See the NOTICE file distributed with
5+
# this work for additional information regarding copyright ownership.
6+
# The ASF licenses this file to You under the Apache License, Version 2.0
7+
# (the "License"); you may not use this file except in compliance with
8+
# the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# test-start-hugegraph-store.sh — Tests for start-hugegraph-store.sh foreground mode fix
19+
#
20+
# Baseline (unmodified code): Test 1 PASS — Tests 2, 3, 4 FAIL
21+
# After chunk 3 fix: All 4 tests PASS
22+
#
23+
# Usage: ./test-start-hugegraph-store.sh [path-to-store-dist-root]
24+
25+
set -uo pipefail
26+
27+
STORE_ROOT="${1:-$(pwd)}"
28+
BIN="$STORE_ROOT/bin"
29+
START_SCRIPT="$BIN/start-hugegraph-store.sh"
30+
STOP_SCRIPT="$BIN/stop-hugegraph-store.sh"
31+
PID_FILE="$BIN/pid"
32+
STORE_URL="http://localhost:8520"
33+
PD_URL="http://localhost:8620"
34+
STARTUP_WAIT=60 # seconds to wait for Store HTTP to respond
35+
SETTLE_WAIT=5 # seconds after kill before checking exit
36+
WAIT_TIMEOUT=30 # seconds for wait_script_exit timeout
37+
38+
PASS=0
39+
FAIL=0
40+
ERRORS=()
41+
42+
GREEN='\033[0;32m'
43+
RED='\033[0;31m'
44+
YELLOW='\033[1;33m'
45+
NC='\033[0m'
46+
47+
pass() { echo -e "${GREEN} PASS${NC} $1"; PASS=$((PASS + 1)); }
48+
fail() { echo -e "${RED} FAIL${NC} $1"; ERRORS+=("$1"); FAIL=$((FAIL + 1)); }
49+
info() { echo -e "${YELLOW} ....${NC} $1"; }
50+
section() { echo ""; echo "── $1 ──"; }
51+
52+
cleanup() {
53+
info "Cleaning up..."
54+
if [[ -s "$PID_FILE" ]]; then
55+
kill "$(cat "$PID_FILE")" 2>/dev/null || true
56+
fi
57+
rm -f "$PID_FILE"
58+
# kill anything holding Store ports (8520 REST, 8510 raft, 8500 gRPC)
59+
lsof -ti :8520 | xargs kill -9 2>/dev/null || true
60+
lsof -ti :8510 | xargs kill -9 2>/dev/null || true
61+
lsof -ti :8500 | xargs kill -9 2>/dev/null || true
62+
sleep 3
63+
}
64+
65+
wait_for_store() {
66+
local elapsed=0
67+
while (( elapsed < STARTUP_WAIT )); do
68+
local status
69+
status=$(curl -s -o /dev/null -w "%{http_code}" \
70+
"$STORE_URL/v1/health" 2>/dev/null || echo "000")
71+
if [[ "$status" == "200" || "$status" == "401" ]]; then
72+
return 0
73+
fi
74+
sleep 2
75+
elapsed=$((elapsed + 2))
76+
done
77+
return 1
78+
}
79+
80+
wait_for_pid_file() {
81+
local elapsed=0
82+
while [[ ! -s "$PID_FILE" ]] && (( elapsed < 30 )); do
83+
sleep 1
84+
elapsed=$((elapsed + 1))
85+
done
86+
}
87+
88+
wait_script_exit() {
89+
local script_pid="$1"
90+
( sleep "$WAIT_TIMEOUT" && kill "$script_pid" 2>/dev/null ) &
91+
local killer_pid=$!
92+
wait "$script_pid" 2>/dev/null
93+
local exit_code=$?
94+
kill "$killer_pid" 2>/dev/null || true
95+
wait "$killer_pid" 2>/dev/null || true
96+
return $exit_code
97+
}
98+
99+
# ── preflight ─────────────────────────────────────────────────────────────────
100+
101+
echo ""
102+
echo "start-hugegraph-store.sh chunk 3 test suite"
103+
echo "root: $STORE_ROOT"
104+
echo ""
105+
106+
if [[ ! -f "$START_SCRIPT" ]]; then
107+
echo -e "${RED}ERROR:${NC} $START_SCRIPT not found."
108+
echo " Pass the Store dist root as \$1"
109+
exit 1
110+
fi
111+
112+
for tool in lsof curl java; do
113+
if ! command -v "$tool" >/dev/null 2>&1; then
114+
echo "SKIP: required tool '$tool' not found — skipping test suite"
115+
exit 0
116+
fi
117+
done
118+
119+
# Store ulimit check (safely handling "unlimited" string)
120+
LIMIT_N=$(ulimit -n)
121+
if [[ "$LIMIT_N" != "unlimited" ]]; then
122+
if (( LIMIT_N < 1024 )); then
123+
echo "SKIP: ulimit -n is $LIMIT_N — store requires >= 1024. Run: ulimit -n 1024"
124+
exit 0
125+
fi
126+
fi
127+
128+
# Check if PD is running, warn if not (Store depends on it for HTTP health check)
129+
PD_RUNNING=false
130+
PD_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$PD_URL/v1/health" 2>/dev/null || echo "000")
131+
if [[ "$PD_STATUS" == "200" || "$PD_STATUS" == "401" ]]; then
132+
PD_RUNNING=true
133+
info "PD is running — Store should become healthy"
134+
else
135+
info "PD is NOT running at $PD_URL — Store will start but health checks may timeout (Acceptable for this test)"
136+
fi
137+
138+
cleanup
139+
140+
# ── test 1: daemon mode regression ───────────────────────────────────────────
141+
142+
section "Test 1 — daemon mode regression"
143+
144+
info "Starting in daemon mode (no -d flag, default behavior)..."
145+
"$START_SCRIPT" >/dev/null 2>&1 &
146+
SCRIPT_PID=$!
147+
148+
sleep 5
149+
if ! ps -p "$SCRIPT_PID" >/dev/null 2>&1; then
150+
pass "script exited after backgrounding Java (daemon mode)"
151+
else
152+
info "script still running after 5s (may still be starting)"
153+
wait_script_exit "$SCRIPT_PID" || true
154+
fi
155+
156+
wait_for_pid_file
157+
DAEMON_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
158+
159+
if [[ -n "$DAEMON_PID" ]]; then
160+
pass "bin/pid written with PID $DAEMON_PID"
161+
else
162+
fail "bin/pid is empty or missing after daemon start"
163+
fi
164+
165+
if [[ -n "$DAEMON_PID" ]] && ps -p "$DAEMON_PID" >/dev/null 2>&1; then
166+
pass "Java process is running (pid $DAEMON_PID)"
167+
else
168+
fail "Java process not found for pid '$DAEMON_PID'"
169+
fi
170+
171+
info "Waiting up to ${STARTUP_WAIT}s for Store health endpoint..."
172+
if wait_for_store; then
173+
pass "Store health endpoint responding at $STORE_URL/v1/health"
174+
else
175+
if [[ "$PD_RUNNING" == "true" ]]; then
176+
fail "Store health endpoint not responding even though PD is up"
177+
else
178+
info "Store health endpoint not responding (Expected since PD is not running)"
179+
fi
180+
fi
181+
182+
cleanup
183+
184+
# ── test 2: foreground mode blocks ───────────────────────────────────────────
185+
186+
section "Test 2 — foreground mode blocks until Java exits"
187+
188+
info "Starting in foreground mode (-d false)..."
189+
"$START_SCRIPT" -d false >/dev/null 2>&1 &
190+
SCRIPT_PID=$!
191+
192+
info "Waiting up to ${STARTUP_WAIT}s for Store to come up..."
193+
if wait_for_store; then
194+
info "Store is up"
195+
else
196+
info "Store did not respond — continuing to check blocking behavior"
197+
fi
198+
199+
if ps -p "$SCRIPT_PID" >/dev/null 2>&1; then
200+
pass "script is still running (blocking correctly)"
201+
else
202+
fail "script exited early — foreground mode is not blocking"
203+
fi
204+
205+
wait_for_pid_file
206+
FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
207+
208+
if [[ -n "$FG_PID" ]]; then
209+
info "pid file written with PID $FG_PID"
210+
else
211+
info "pid file not written (expected before chunk 3 fix)"
212+
fi
213+
214+
if [[ -n "$FG_PID" ]]; then
215+
kill "$FG_PID" 2>/dev/null || true
216+
else
217+
kill "$SCRIPT_PID" 2>/dev/null || true
218+
fi
219+
sleep "$SETTLE_WAIT"
220+
221+
if ! ps -p "$SCRIPT_PID" >/dev/null 2>&1; then
222+
info "script exited after Java was killed"
223+
else
224+
kill "$SCRIPT_PID" 2>/dev/null || true
225+
fi
226+
227+
cleanup
228+
229+
# ── test 3: exit code propagates ─────────────────────────────────────────────
230+
231+
section "Test 3 — exit code propagates from Java"
232+
233+
info "Starting in foreground mode (-d false)..."
234+
"$START_SCRIPT" -d false >/dev/null 2>&1 &
235+
SCRIPT_PID=$!
236+
237+
info "Waiting up to ${STARTUP_WAIT}s for Store..."
238+
if wait_for_store; then
239+
info "Store is up"
240+
else
241+
info "Store did not respond — continuing"
242+
fi
243+
244+
wait_for_pid_file
245+
FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
246+
247+
if [[ -n "$FG_PID" ]]; then
248+
pass "bin/pid written with PID $FG_PID"
249+
else
250+
fail "bin/pid is empty or missing in foreground mode"
251+
fi
252+
253+
if [[ -n "$FG_PID" ]] && ps -p "$FG_PID" >/dev/null 2>&1; then
254+
pass "Java process running (pid $FG_PID)"
255+
else
256+
fail "Java process not found for pid '$FG_PID'"
257+
fi
258+
259+
if [[ -z "$FG_PID" ]]; then
260+
fail "could not get PID — skipping exit code check"
261+
kill "$SCRIPT_PID" 2>/dev/null || true
262+
else
263+
info "Hard-killing Java with SIGKILL (pid $FG_PID)..."
264+
kill -9 "$FG_PID" 2>/dev/null || true
265+
266+
wait_script_exit "$SCRIPT_PID"
267+
ACTUAL_EXIT=$?
268+
269+
if [[ $ACTUAL_EXIT -ne 0 ]]; then
270+
pass "script exited non-zero ($ACTUAL_EXIT) after SIGKILL"
271+
else
272+
fail "script exited 0 after SIGKILL — Docker would NOT restart"
273+
fi
274+
275+
if [[ $ACTUAL_EXIT -eq 137 ]]; then
276+
pass "exit code is 137 (128+9 for SIGKILL) — correctly propagated"
277+
elif [[ $ACTUAL_EXIT -ne 0 ]]; then
278+
info "exit code was $ACTUAL_EXIT (not 137 — acceptable if non-zero)"
279+
fi
280+
fi
281+
282+
cleanup
283+
284+
# ── test 4: SIGTERM forwarded to Java ────────────────────────────────────────
285+
286+
section "Test 4 — SIGTERM forwarded to Java in foreground mode"
287+
288+
info "Starting in foreground mode (-d false)..."
289+
"$START_SCRIPT" -d false >/dev/null 2>&1 &
290+
SCRIPT_PID=$!
291+
292+
info "Waiting up to ${STARTUP_WAIT}s for Store..."
293+
if wait_for_store; then
294+
info "Store is up"
295+
else
296+
info "Store did not respond — continuing"
297+
fi
298+
299+
wait_for_pid_file
300+
FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
301+
302+
if [[ -z "$FG_PID" ]]; then
303+
fail "could not get Java PID — skipping SIGTERM check"
304+
kill "$SCRIPT_PID" 2>/dev/null || true
305+
else
306+
info "Sending SIGTERM to process (pid $SCRIPT_PID)..."
307+
kill -TERM "$SCRIPT_PID" 2>/dev/null || true
308+
309+
wait_script_exit "$SCRIPT_PID"
310+
ACTUAL_EXIT=$?
311+
312+
if ! ps -p "$FG_PID" >/dev/null 2>&1; then
313+
pass "Java process terminated after SIGTERM"
314+
else
315+
fail "Java process still running after SIGTERM — signal not delivered"
316+
kill "$FG_PID" 2>/dev/null || true
317+
fi
318+
319+
if [[ $ACTUAL_EXIT -ne 0 ]]; then
320+
pass "process exited non-zero ($ACTUAL_EXIT) after SIGTERM"
321+
else
322+
fail "process exited 0 after SIGTERM — unexpected"
323+
fi
324+
325+
if [[ $ACTUAL_EXIT -eq 143 ]]; then
326+
pass "exit code is 143 (128+15 for SIGTERM) — correctly propagated"
327+
elif [[ $ACTUAL_EXIT -ne 0 ]]; then
328+
info "exit code was $ACTUAL_EXIT (not 143 — acceptable if non-zero)"
329+
fi
330+
fi
331+
332+
cleanup
333+
334+
# ── summary ───────────────────────────────────────────────────────────────────
335+
336+
echo ""
337+
echo "════════════════════════════════"
338+
echo -e " Results: ${GREEN}$PASS passed${NC} ${RED}$FAIL failed${NC}"
339+
echo "════════════════════════════════"
340+
341+
if [[ ${#ERRORS[@]} -gt 0 ]]; then
342+
echo ""
343+
echo "Failed tests:"
344+
for err in "${ERRORS[@]}"; do
345+
echo -e " ${RED}${NC} $err"
346+
done
347+
fi
348+
349+
echo ""
350+
[[ $FAIL -eq 0 ]] && exit 0 || exit 1

0 commit comments

Comments
 (0)