Skip to content

Fix stop-edge.sh not stopping its own process when IOTDB_HOME is set - #18546

Open
PDGGK wants to merge 5 commits into
apache:masterfrom
PDGGK:fix-edge-stop-home
Open

PDGGK wants to merge 5 commits into
apache:masterfrom
PDGGK:fix-edge-stop-home

Conversation

@PDGGK

@PDGGK PDGGK commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Description

Fixes #18545.

start-edge.sh honours an IOTDB_HOME taken from the environment and passes that
value to the JVM as -DIOTDB_HOME=.... stop-edge.sh recomputed IOTDB_HOME
from its own location and matched the process command line against that string,
so when the two differed it declined to act on its own process, exited 0, and
removed the PID file. A service manager saw a successful stop while the process
kept running; the following ExecStart then found the ports occupied and also
exited 0, so a configuration change silently never took effect.

The change

start-edge.sh and stop-edge.sh now derive IOTDB_HOME with one shared
function, initEdgeHome in conf/iotdb-common.sh:

  • an IOTDB_HOME from the environment is used by both scripts; otherwise it is
    the parent of the script's directory;
  • a relative value is made absolute against the current directory. Symlinks are
    not resolved, so an installation started through a symlink keeps that identity
    after the symlink is re-pointed to another version;
  • the scripts exit with an error if the directory cannot be entered, and warn
    when it is not the installation the script belongs to.

stop-edge.sh first compares the recorded -DIOTDB_HOME literally, as before.
If that does not match and the recorded value is an absolute path, it compares
the two after resolving symlinks. A process started with IOTDB_HOME=<symlink>
is then found when stopping through the real path, and the other way round.

Behavioural notes

  • Two paths that resolve to the same directory now count as the same installation.
  • stop-edge.sh now honours IOTDB_HOME from the environment. If it is exported
    for another installation, the stop looks for that installation's process and
    prints a warning, where it previously stopped its own.
  • A relative IOTDB_HOME is made absolute before it reaches the JVM, so ps
    shows the absolute path in that case.

Verification

.github/scripts/test-edge-ops.py has a POSIX-only EdgeStopTest that runs the
real stop-edge.sh against stand-in processes for the cases below, plus a process
without -DIOTDB_HOME, CDPATH, the warning and a missing directory.
IoTDBEdgeBasicIT checks that the process from edge.pid has exited after
stop-edge.sh returns.

The same cases with the real start-edge.sh and stop-edge.sh and a JVM whose
main class is org.apache.iotdb.edge.EdgeNode and which only sleeps, on macOS;
each cell is the state of the started process after the stop:

#  case                                                              expected  base     now
-  ----------------------------------------------------------------  --------  -------  -------
1  started with IOTDB_HOME=<symlink>, stopped through the real path  stopped   running  stopped
2  started through iotdb -> v1, re-pointed to v2, stopped via iotdb  stopped   stopped  stopped
3  -DIOTDB_HOME=. in A (earlier launcher), stopped from B            running   running  running
4  same, stopped from B with IOTDB_HOME=.                            running   running  running
5  home x/link/../edge (= y/edge), stopped from x/edge               running   running  running
6  same, stopped from y/edge                                         stopped   running  stopped
7  started and stopped through x/link/../edge/sbin/                  stopped   stopped  stopped

Row 2 was run with IOTDB_HOME unset and with it set to the symlink for both
scripts. The first version of this change was also run against the packaged Edge
archive with the restart sequence from #18545.

Not changed

@JackieTien97 JackieTien97 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reported symlink/real-path mismatch is fixed, but I reproduced two cases where the new fallback stops a process from a different installation. The details are in the inline comments.

I suggest retaining Edge's PID-file and installation-identity checks rather than copying the port-based lookup from stop-datanode.sh / stop-confignode.sh: changing the lookup port before stopping can make those scripts miss the old process. Start and stop should use consistent rules for IOTDB_HOME and configuration directories.

There is also a pre-existing lifecycle concern: the PID file is removed before stop_edge_process succeeds. Retaining it on shutdown failure and returning a nonzero status for genuine stop failures would avoid losing track of a running process. This can be handled separately if it is outside this fix.

Verification: I ran the unchanged base (1106d85) and PR (73a96dc) stop scripts on macOS against isolated minimal JVM processes named org.apache.iotdb.edge.EdgeNode, with process inspection and signals restricted to the test-owned processes. In both cross-installation cases, the base script leaves the unrelated process alive and the PR script terminates it. The ordinary symlink-to-real-path case behaves as intended after this change. These were script-level checks, not full IoTDB server tests.

Comment thread scripts/sbin/stop-edge.sh Outdated
Comment on lines +48 to +51
local home="${command_line#*-DIOTDB_HOME=}"
home="${home%% -D*}"
[ -n "$home" ] && [ "$home" != "$command_line" ] || return 1
[ "$(resolve_home "$home")" = "${IOTDB_HOME_RESOLVED}" ]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Resolve relative process homes using the process's working directory

start-edge.sh accepts IOTDB_HOME=. and passes it unchanged to the JVM. If an instance was started this way in /srv/edge-a, running /srv/edge-b/sbin/stop-edge.sh from /srv/edge-b with IOTDB_HOME unset resolves the process's . to B. Consequently, find_edge_processes considers A a match and terminates it, even if B has no PID file or running instance. The base script rejects this process; the new fallback accepts it.

Please normalize the home to a physical absolute path at launch, or resolve relative process values against that process's working directory; do not resolve them against the stopping shell's working directory.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, and thank you for running it. I reproduced it before changing
anything: a synthetic process with cwd in edge-a and -DIOTDB_HOME=. on its
command line, stopped from edge-b -- the base script correctly declines it, my
fallback resolved the . against the stopping shell and terminated it.

I have taken the first of your two suggestions. start-edge.sh now normalises
IOTDB_HOME to a physical absolute path before exporting it, so the value handed
to the JVM identifies the installation on its own:

started through the symlink, before   -DIOTDB_HOME=$ROOT/iotdb
started through the symlink, now      -DIOTDB_HOME=$ROOT/apache-iotdb-2.0.11-SNAPSHOT-edge-bin

I kept an absolute-path guard in the stop-edge.sh fallback as well, rather than
relying only on the launcher. A process started by an earlier start-edge.sh
during an upgrade can still carry a relative value, and for those the fallback now
declines instead of resolving against our own directory -- the same outcome as the
base script. That leaves resolving against the process's working directory
unnecessary, which I was reluctant to do since it needs lsof or /proc and a
wrong answer there puts us back to stopping the wrong process.

Two things about the normalisation that I would rather state than leave to be
found. It changes the path string that appears in ps, in the console message and
in the logs -- on this machine /tmp becomes /private/tmp -- which is the point
of the change, but anything grepping for the old string is affected. And if the
cd -P cannot run, IOTDB_HOME keeps the value it had, which for the unset case
is still relative; that instance then falls to the literal comparison in
stop-edge.sh, which is today's behaviour. A fallback to a logical cd does not
help here: both still have to reach the directory, so it fails on the same inputs.

Both of your cases are in the regression set now, with the version you reviewed as
a control so they are shown to fail without the change.

Comment thread scripts/sbin/stop-edge.sh Outdated
# Resolve symlinks and relative segments so that the same installation reached
# through a different path still compares equal.
resolve_home() {
(cd "$1" 2>/dev/null && pwd -P) || printf '%s' "$1"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Use physical cd semantics before resolving ..

pwd -P only resolves the directory reached by cd; plain cd has already collapsed .. logically. With /x/link pointing to /y/sub, a process home /x/link/../edge accesses /y/edge, but this helper returns /x/edge when that directory exists. Consequently, stopping /x/edge can terminate the /y/edge instance, while stopping /y/edge can miss it. This also reproduces with an absolute IOTDB_HOME, independently of the relative-path issue.

Use cd -P -- "$1" before pwd -P and cover a symlink followed by .. in the regression cases.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed, and thank you -- this one reproduces as pure path arithmetic, without
needing a process. With x/link pointing at y/sub and a process home of
x/link/../edge, the physical target is y/edge, and the old helper returned
x/edge:

process home          $ROOT/x/link/../edge
physically            $ROOT/y/edge
old resolve_home      $ROOT/x/edge      <- wrong installation
cd -P version         $ROOT/y/edge

Fixed in 739dae2 with cd -P -- "$1", and both of your cases are in the
regression set now:

scenario                                            base    previous  now
--------------------------------------------------  ------  --------  ------
[P1] -DIOTDB_HOME=. in A, stopped from B            keep    stopped   keep
[P2] home x/link/../edge, stopped from x/edge       keep    stopped   keep
[P2] home x/link/../edge, stopped from y/edge       -       -         stopped
symlink home, stopped from the real path            keep    stopped   stopped

The "previous" column is the version you reviewed, so the two cases do fail
without the change rather than passing for an unrelated reason. The four
start/stop environment combinations from the PR description still behave the
same, checked against real Edge processes with 10710 confirmed listening before
each stop.

One limit of the fallback that I should state rather than leave for you to find:
it locates the end of the value by the next -D, which start-edge.sh always
emits after -DIOTDB_HOME. A hand-built command line that ends with
-DIOTDB_HOME=<symlink> is not matched by the fallback. The failure direction is
the safe one -- the extraction keeps the trailing arguments, the resolution fails,
and the process is simply not matched -- so that case behaves as it does today. I have noted it in a comment above the
fallback.

On the PID file being removed before stop_edge_process succeeds: I agree, and I
will open that separately as you suggested, since it changes the exit-code
contract and is worth reviewing on its own.

@PDGGK

PDGGK commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

One more data point on the convention, from the Edge tools added in #18552 on the
same day this branch was opened.

scripts/tools/ops/daemon-edge.sh and scripts/tools/ops/destroy-edge.sh both
start with the same two lines:

IOTDB_HOME="${IOTDB_HOME:-$(cd "$(dirname "$0")"/../.. && pwd)}"
IOTDB_HOME="$(cd "$IOTDB_HOME" && pwd -P)"

That is: honour an IOTDB_HOME from the environment, then resolve it physically.
start-edge.sh honours the environment too, via its -z guard.

So of the four Edge shell entry points, three take the value they are given and
stop-edge.sh is the one that discards it — and the two ops scripts already use
pwd -P for exactly the reason this pull request adds it. This is a smaller
change than it looked: it brings the fourth script into line with the other
three rather than introducing a new convention.

@PDGGK
PDGGK force-pushed the fix-edge-stop-home branch from 25dbf74 to ff1f1c0 Compare October 13, 2026 10:41
@PDGGK

PDGGK commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

@JackieTien97 A gentle ping on this one. Both cases from your review are handled, with details in the inline replies:

  • the symlink followed by .. case: cd -P (now 396fb27);
  • the relative-home case: start-edge.sh normalises IOTDB_HOME to a physical absolute path (now dfb1236).

I rebased onto current master today and reran the regression cases against it. Master still leaves the symlink-started process running and the branch stops it. In both of your cases, the other installation's process is left alone. The CI runs from August expired before they were approved, so the new push will need an approval to run.

@JackieTien97 JackieTien97 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update and for the regression tables. I re-ran the scripts at ff1f1c0 against the base (491d9b3) with the same kind of script-level setup as last time: the real start-edge.sh and stop-edge.sh in fresh directories, a minimal JVM whose main class is org.apache.iotdb.edge.EdgeNode and which only sleeps, and process inspection limited to the test-owned processes.

The reported case is fixed, and both cases from my previous review behave correctly. I did find three cases where this version does worse than base (rows 2, 4 and 7 below), so the description's "it can recognise more processes than before, never fewer" no longer holds. Details are in the inline comments. In short:

  • Rows 4 and 7 come from stop-edge.sh deriving IOTDB_HOME differently from start-edge.sh. Using the same block in both scripts fixes them.
  • Row 2 is a side effect of resolving symlinks at launch, which was the first option I suggested last time. It needs a decision between documenting "stop before re-pointing the symlink" and recording an absolute path without resolving symlinks.

I also ran both directions on top of this branch, together with the two smaller fixes suggested inline. (a) is stop-edge.sh using the same physical normalisation as start-edge.sh; (b) is both scripts recording an absolute path without resolving symlinks, with the resolved-path comparison kept only in the stop fallback.

# Case Expected base PR (a) (b)
1 #18545: started with IOTDB_HOME=<symlink>, stopped through the real path stopped running stopped stopped stopped
2 Started through iotdb -> v1, symlink re-pointed to v2, stopped through iotdb/sbin/ (IOTDB_HOME unset, or set to the symlink for both scripts) stopped stopped running running stopped
3 Previous P1: A runs a process with -DIOTDB_HOME=. from the previous launcher; stop from B with IOTDB_HOME unset A running A running A running A running A running
4 Same, but the stop in B runs with IOTDB_HOME=. A running A running A stopped A running A running
5 Previous P2: home x/link/../edge (x/link -> ../y/sub), stop from x/edge running running running running running
6 Same, stop from y/edge stopped running stopped stopped stopped
7 Started and stopped through x/link/../edge/sbin/, IOTDB_HOME unset stopped stopped running stopped stopped

Every stop in this table exited 0, including the ones that left the process running.

Other points:

  • Tests. Nothing in CI would catch a regression here. IoTDBEdgeBasicIT only checks that stop-edge.sh exits 0, which it also does when no process matches, and stopProcessForciblyIfNeeded then kills the process anyway. test-edge-ops.py replaces stop-edge.sh with a stub. Could you assert in the IT that the PID from edge.pid has exited once stop-edge.sh returns, and add the cases above to test-edge-ops.py? They do not need a JVM: stop-edge.sh recognises a process such as python3 -c "import time; time.sleep(600)" -DIOTDB_HOME=<home> -DCONFIGNODE_HOME=<home> org.apache.iotdb.edge.EdgeNode -s.
  • AGENTS.md. The agent instructions added with the Edge distribution in #18538 (now in AGENTS.md) say "Stop scripts must match both the EdgeNode main class and the exact IOTDB_HOME". If the resolved-path fallback stays, please update that sentence in this PR so the rule and the code agree.
  • Inherited IOTDB_HOME. Because stop-edge.sh now honours the environment, an IOTDB_HOME exported for another installation makes /opt/edge/sbin/stop-edge.sh print "No IoTDB Edge process from is running." and exit 0 while its own instance keeps running. A typo does the same. This is consistent with start-edge.sh, but a warning when the resolved home is not the script's own installation, and an error when the directory does not exist, would make it visible.
  • Windows. stop-edge.bat still sets IOTDB_HOME unconditionally while start-edge.bat honours it, so the same problem remains there. Handling it separately is fine; please open an issue so it is tracked.
  • The PID-file lifecycle point from my previous review is still open. Thanks for planning a separate PR for it.

These were script-level checks on macOS, not full server tests, and I did not run the Windows scripts.

Comment thread scripts/sbin/start-edge.sh Outdated
Comment on lines +26 to +32
# Normalise to a physical absolute path. This value is handed to the JVM as
# -DIOTDB_HOME and is what stop-edge.sh matches on, so it has to identify the
# installation on its own, independently of the path used to launch.
IOTDB_HOME_PHYSICAL="$(cd -P -- "${IOTDB_HOME}" 2>/dev/null && pwd -P)"
if [ -n "${IOTDB_HOME_PHYSICAL}" ]; then
IOTDB_HOME="${IOTDB_HOME_PHYSICAL}"
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Resolving symlinks at launch breaks stopping after a symlink is re-pointed

This makes the physical directory, not the launch path, the identity on the command line. With versioned directories behind a stable symlink, which is the layout in #18545, upgrading by re-pointing the symlink and then restarting through it no longer stops the old process:

# edge-v1 and edge-v2 are two unpacked Edge distributions
ln -s edge-v1 iotdb
iotdb/sbin/start-edge.sh   # this PR: -DIOTDB_HOME=$PWD/edge-v1   base: -DIOTDB_HOME=$PWD/iotdb
ln -sfn edge-v2 iotdb
iotdb/sbin/stop-edge.sh    # this PR: "No IoTDB Edge process from $PWD/iotdb is running.", exit 0

edge.pid is in edge-v1, the literal comparison does not match, and the fallback compares edge-v1 with edge-v2. The next start then exits 0 because the ports are occupied, which is the #18545 symptom again. Setting IOTDB_HOME=$PWD/iotdb for both scripts gives the same result. Base records $PWD/iotdb and stops the process. Units generated by daemon-edge.sh pin the physical path, and systemd's default KillMode=control-group cleans up after ExecStop, so this mainly affects manual and non-systemd operation.

This was the first option I suggested last time, and I had not considered this flow. I see two ways forward:

  1. Keep it, and document that the stop has to run before the symlink is re-pointed, or through the old physical path.

  2. Record an absolute path without resolving symlinks, and keep the resolved-path comparison only in the stop-edge.sh fallback:

    if [ -z "${IOTDB_HOME}" ]; then
        IOTDB_HOME="$(CDPATH= cd -- "$(dirname "$0")"/.. && pwd)"
    fi
    case "${IOTDB_HOME}" in
        /*) ;;
        *) IOTDB_HOME="$(pwd)/${IOTDB_HOME}" ;;
    esac
    export IOTDB_HOME

    with the same block in stop-edge.sh. In my runs this keeps IoTDB Edge: stop-edge.sh does not stop its own process when IOTDB_HOME is set, and reports success #18545 and both cases from my previous review fixed, and also stops the process after the symlink is re-pointed. The trade-off is that the default stays logical, as in base, so launching through x/link/../edge/sbin/ still uses x/edge.

I lean towards 2, but either works as long as both scripts use the same block.

Comment thread scripts/sbin/start-edge.sh Outdated
# Normalise to a physical absolute path. This value is handed to the JVM as
# -DIOTDB_HOME and is what stop-edge.sh matches on, so it has to identify the
# installation on its own, independently of the path used to launch.
IOTDB_HOME_PHYSICAL="$(cd -P -- "${IOTDB_HOME}" 2>/dev/null && pwd -P)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] Guard this cd against CDPATH

If CDPATH is exported, cd looks relative names up in it first and prints the directory it chose, so the substitution captures two lines. With CDPATH=/elsewhere and IOTDB_HOME=iotdb, IOTDB_HOME_PHYSICAL is /elsewhere/iotdb twice, separated by a newline, and that becomes the home. With CDPATH=.:... it is ./iotdb followed by the absolute path. Base used a relative IOTDB_HOME from the environment as-is, so this is new for that case; the default branch already had the problem with a plain cd. CDPATH= cd -P -- ... here and in resolve_home, or unset CDPATH at the top of both scripts, avoids it.

Comment thread scripts/sbin/stop-edge.sh Outdated
Comment on lines +23 to +25
if [ -z "${IOTDB_HOME}" ]; then
IOTDB_HOME="$(cd "$(dirname "$0")"/.. && pwd)"
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Please derive IOTDB_HOME with the same code as start-edge.sh

The two scripts now compute the home differently, and both differences matter.

  1. A relative IOTDB_HOME from the environment is used as-is in the literal comparison, which bypasses the absolute-path guard in the fallback. Suppose installation A runs a process started by the previous start-edge.sh with IOTDB_HOME=., the case the guard is meant for, and B is not running:

    cd /srv/edge-b && IOTDB_HOME=. ./sbin/stop-edge.sh
    # IoTDB Edge process <PID of A's process> stopped.

    B has no edge.pid, so the script scans, and -DIOTDB_HOME=. matches A's command line. Base ignores the environment here and leaves A alone.

  2. The default still uses a logical cd/pwd, while start-edge.sh now uses cd -P/pwd -P. With x/link -> ../y/sub, starting and stopping through x/link/../edge/sbin/ records .../y/edge at start but computes .../x/edge here. The stop prints No IoTDB Edge process from .../x/edge is running. and exits 0, and if x/edge has its own instance, that one is stopped instead. This is last round's P2 again, on the script's own path. Base computes x/edge in both scripts and stops the process.

If the launcher keeps the physical normalisation, this block fixes both, and IOTDB_HOME_RESOLVED then equals IOTDB_HOME:

if [ -z "${IOTDB_HOME}" ]; then
    IOTDB_HOME="$(dirname "$0")/.."
fi
IOTDB_HOME_PHYSICAL="$(CDPATH= cd -P -- "${IOTDB_HOME}" 2>/dev/null && pwd -P)"
if [ -z "${IOTDB_HOME_PHYSICAL}" ]; then
    echo "Cannot resolve IOTDB_HOME ${IOTDB_HOME}."
    exit 1
fi
IOTDB_HOME="${IOTDB_HOME_PHYSICAL}"

If the launcher switches to option 2 from my start-edge.sh comment, use that block here instead. Either way, one function in conf/iotdb-common.sh called by both scripts would stop them from drifting apart again.

Comment thread scripts/sbin/stop-edge.sh Outdated
# process is simply not matched -- never matched to the wrong installation.
local home="${command_line#*-DIOTDB_HOME=}"
home="${home%% -D*}"
[ -n "$home" ] && [ "$home" != "$command_line" ] || return 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] This check cannot detect a missing -DIOTDB_HOME=

It runs after the %% -D* truncation on the previous line. When the command line has no -DIOTDB_HOME=, home is the command line cut at its first -D, which differs from command_line whenever any -D option is present. For /usr/bin/java -Dfoo=1 ... org.apache.iotdb.edge.EdgeNode, only the failing cd prevents a match, and a process whose argv[0] is the installation directory is matched without any -DIOTDB_HOME. That is unlikely in practice, but it contradicts the comment above. Checking for the option first makes the intent explicit:

    case "$command_line" in
        *-DIOTDB_HOME=*) ;;
        *) return 1 ;;
    esac
    local home="${command_line#*-DIOTDB_HOME=}"
    home="${home%% -D*}"
    [ -n "$home" ] || return 1

PDGGK added 5 commits October 23, 2026 23:33
start-edge.sh honours an IOTDB_HOME from the environment and passes that value
to the JVM as -DIOTDB_HOME=..., while stop-edge.sh recomputed IOTDB_HOME from
its own location and matched the command line against that string. When the two
differ -- for example a versioned install reached through a symlink -- the stop
declined to act on its own process, exited 0 and removed the PID file, so a
service manager saw a successful stop while the process kept running.

stop-edge.sh now honours IOTDB_HOME the same way start-edge.sh does, and falls
back to comparing resolved paths when the literal match does not hit. The
original literal comparison is tried first, so any command line that matched
before still matches.
Two problems in the previous version, both of which could stop a process
belonging to a different installation.

The helper used a plain cd before pwd -P. Because cd collapses ".."
logically, a process home such as <symlink>/../edge resolved against the
symlink's parent rather than its target, so stopping one installation could
terminate another. It now uses cd -P.

The fallback also resolved relative process homes, such as the "." that
start-edge.sh accepts and passes through unchanged, against the working
directory of the stopping shell rather than that of the started process. A
process started with IOTDB_HOME=. in one directory could therefore be matched
and stopped from an unrelated installation. The fallback now only considers
absolute values and leaves relative ones to the literal comparison, which
matches the previous behaviour for them.
Taking the first of the two options suggested in review. The value start-edge.sh
hands to the JVM as -DIOTDB_HOME is what stop-edge.sh matches on, so it should
identify the installation on its own rather than depend on the path used to
launch. Normalising it at that point removes the relative and symlinked forms at
the source instead of resolving them later.

The absolute-path guard in stop-edge.sh is kept as well, so a process started by
an earlier start-edge.sh, whose command line may still carry a relative value,
is left alone rather than matched against the stopping shell's directory.
cd succeeds on an empty argument and returns the caller's working directory, so
dropping the emptiness check would reopen the same class of mismatch as a
relative value. Comment only.
start-edge.sh and stop-edge.sh computed IOTDB_HOME differently, so a
stop could miss its own process or match another installation's. Both
now call initEdgeHome from conf/iotdb-common.sh, which only makes the
value absolute and does not resolve symlinks, so stopping through a
symlink still works after it is re-pointed to a new version. The
resolved-path comparison stays as a fallback in stop-edge.sh only.

initEdgeHome fails when IOTDB_HOME cannot be entered and warns when it
is not the installation the script belongs to. Its cd calls ignore
CDPATH. The fallback now requires -DIOTDB_HOME= on the command line.

test-edge-ops.py runs the real stop-edge.sh against stand-in processes
for these cases, and IoTDBEdgeBasicIT checks that the process recorded
in edge.pid has exited once stop-edge.sh returns.

Signed-off-by: Zihan Dai <99155080+PDGGK@users.noreply.github.com>
@PDGGK

PDGGK commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for re-running this and for the table. I went with option 2.

  • start-edge.sh and stop-edge.sh now both call initEdgeHome in conf/iotdb-common.sh. It is the block you suggested: the home is made absolute and symlinks are not resolved. Its cd calls run with CDPATH cleared. The resolved-path comparison is only used in the stop-edge.sh fallback.
  • initEdgeHome exits with an error when IOTDB_HOME cannot be entered, and prints a warning to stderr when it is not the installation the script belongs to.
  • The fallback returns early when the command line has no -DIOTDB_HOME= (P3).
  • The AGENTS.md sentence now says that both scripts must use the shared function, and that the stop matches the recorded home exactly or, for an absolute recorded path, after resolving symlinks on both sides.

I reran your seven rows on macOS with the real start-edge.sh and stop-edge.sh and a JVM whose main class is org.apache.iotdb.edge.EdgeNode and which only sleeps. Each cell is the state of the started process after the stop:

# Expected base (491d9b3) ff1f1c0 this push
1 stopped running stopped stopped
2, IOTDB_HOME unset stopped stopped running stopped
2, IOTDB_HOME=<symlink> stopped stopped running stopped
3 A running A running A running A running
4 A running A running A stopped A running
5 running running running running
6 stopped running stopped stopped
7 stopped stopped running stopped

The base and ff1f1c0 columns match yours. In rows 3 and 4, A's process was started by the base start-edge.sh with IOTDB_HOME=.. In the layout of row 7, where x/edge is a separate installation, the new warning also fires, because the logical default x/edge is not the directory that contains the script.

Tests:

  • test-edge-ops.py has a new POSIX-only EdgeStopTest, which runs the real stop-edge.sh against stand-in processes as you suggested. Each stand-in carries the -DIOTDB_HOME that initEdgeHome derives for start-edge.sh. It covers the seven rows, the P3 case, CDPATH, the warning and a missing directory. The stand-ins are started through a short-lived parent, so that they are reaped once stopped, as the JVM is after nohup; a zombie child of the test would still pass kill -0. Reverting each fix makes its test fail. The file passes on macOS (bash 3.2) and in a Debian container (bash 5.2).
  • IoTDBEdgeBasicIT now checks in tearDown that the PID from edge.pid has exited after stop-edge.sh returns. It waits up to 5 s, because the kill -9 path does not wait for the exit. I have not run this IT locally, as it needs the full distribution build, so CI will be its first run.

I opened #19005 for stop-edge.bat. The PID-file lifecycle stays for a separate PR. The branch is rebased onto current master, where the agent instructions now live in AGENTS.md (#18952). I also updated the description: its "never fewer" sentence no longer holds. When IOTDB_HOME is exported for another installation, stop-edge.sh now looks for that installation's process and warns, where base stopped its own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IoTDB Edge: stop-edge.sh does not stop its own process when IOTDB_HOME is set, and reports success

2 participants