Skip to content

fix(bin): replace lsof with /dev/tcp in server check_port; remove dea…#3105

Open
bitflicker64 wants to merge 1 commit into
apache:masterfrom
bitflicker64:fix/lsof-port-check
Open

fix(bin): replace lsof with /dev/tcp in server check_port; remove dea…#3105
bitflicker64 wants to merge 1 commit into
apache:masterfrom
bitflicker64:fix/lsof-port-check

Conversation

@bitflicker64

@bitflicker64 bitflicker64 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Purpose of the PR

Fix a startup hang in kind/Kubernetes environments caused by lsof scanning an enormous file descriptor table, and remove dead check_port code from pd/store.

Main Changes

hugegraph-server/.../util.sh

  • Replace lsof -i :PORT with a layered probe: ss -ltnnetstat -ltn/netstat -anbash /dev/tcp with timeout 1
  • Resolve hostnames to numeric IPs via getent/dscacheutil before matching against ss -n/netstat -n output
  • Address-family-aware wildcard matching so IPv4 targets don't conflict with IPv6 wildcard listeners
  • Fall through to next tool when ss exits non-zero
  • 2-second watchdog deadline for /dev/tcp when timeout is unavailable
  • Strip leading/trailing whitespace from URL and host
  • Parse IPv6 bracket notation (http://[::1]:8080 and [::1]:8080)
  • Validate port as decimal in range 1–65535 (force base-10 to prevent octal misinterpretation)
  • Add curl -fL in download() so HTTP errors exit non-zero

hugegraph-pd/.../util.sh and hugegraph-store/.../util.sh

  • Remove check_port — never called by PD or Store startup scripts
  • Fix command_available: was using command substitution that always produced empty result
  • Fix download() curl branch: was writing to broken path; now uses mkdir -p + basename
  • Add curl -fL

.github/workflows/server-ci.yml

  • Remove lsof from preflight prerequisites
  • Add fuser preflight check on Linux for port cleanup

test-start-hugegraph*.sh

  • Replace lsof -ti :PORT | xargs kill -9 with OS-aware cleanup (fuser -k PORT/tcp on Linux, lsof on macOS)
  • Warn when neither tool is available

Tests

New test-check-port.sh — 34 mock-driven unit tests covering ss, GNU netstat, BSD netstat, /dev/tcp + timeout, real ephemeral-port listener, IPv4/IPv6, wildcard vs specific-address matching, tool-failure fallthrough, hostname resolution, URL whitespace normalization, IPv6 hextet guard, edge cases, and download() curl fallback for PD and Store.

Verifying these changes

  • 34 mock-driven unit tests (all pass)
  • lsof removed from CI preflight; fuser enforced on Linux
  • For the original kind/k8s hang: start server in pod with large ulimit -n — starts without hanging

Does this PR potentially affect the following parts?

  • Nope

Documentation Status

  • Doc - No Need

@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. bug Something isn't working labels Jul 23, 2026
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 36.55%. Comparing base (89b648a) to head (745a8bb).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #3105      +/-   ##
============================================
- Coverage     37.43%   36.55%   -0.89%     
+ Complexity      520      498      -22     
============================================
  Files           808      808              
  Lines         69581    69594      +13     
  Branches       9165     9169       +4     
============================================
- Hits          26048    25440     -608     
- Misses        40750    41402     +652     
+ Partials       2783     2752      -31     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@bitflicker64

Copy link
Copy Markdown
Contributor Author

@copilot review

@imbajin imbajin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: yes. Summary: The new probe can miss or stall on real port conflicts, and the corrected command detection exposes a broken curl-only path. Evidence: static review of 6395c0c; bash -n passed; codecov/project is failing on this head.

Comment thread hugegraph-pd/hg-pd-dist/src/assembly/static/bin/util.sh
Comment thread hugegraph-server/hugegraph-dist/src/assembly/static/bin/util.sh Outdated
Comment thread hugegraph-server/hugegraph-dist/src/assembly/static/bin/util.sh Outdated
Comment thread hugegraph-server/hugegraph-dist/src/assembly/static/bin/util.sh Outdated

@imbajin imbajin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: yes. Summary: The fallback probe can execute configuration text as shell code and misparses a supported IPv6 URL form; the no-lsof branch matrix also remains untested. Evidence: exact-head static review, bash -n on all three changed scripts, and controlled reproductions for command execution and [::1]:8080 parsing.

Comment thread hugegraph-server/hugegraph-dist/src/assembly/static/bin/util.sh Outdated
Comment thread hugegraph-server/hugegraph-dist/src/assembly/static/bin/util.sh Outdated
Comment thread hugegraph-server/hugegraph-dist/src/assembly/static/bin/util.sh
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels Jul 23, 2026

@imbajin imbajin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: yes. Summary: The listener-table probes can reject valid binds or silently skip collision detection, and the PD startup test still does not enforce its new lsof-free cleanup path. Evidence: exact-head static inspection, targeted shell reproductions, the 19-test check_port suite, and successful visible checks.


local in_use=0
if command_available "ss"; then
if ss -ltn 2>/dev/null | grep -qE "(:${port}\b|:${port}$)"; then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

⚠️ The primary probes do not identify the configured local endpoint: they match every listener by port, and the ss expression can even match the target digits inside an IPv6 address (for example, [2001:db8:8080::1]:9090 matches a check for port 8080). A listener on another specific interface can therefore reject a valid bind, while an IPv6 hextet can produce the same false positive. Please parse the local-address field and compare the configured host/port with wildcard and dual-stack conflict semantics, then add distinct-address and IPv6-hextet regressions.

fi

local in_use=0
if command_available "ss"; then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

⚠️ Tool presence is treated as a successful probe. If an installed ss exits nonzero, this branch neither distinguishes that failure from an empty listener table nor tries netstat or /dev/tcp; the same silent-free result occurs when both netstat invocations fail. Please capture each probe's execution status, advance to the next layer on failure, and add cases where a present tool returns nonzero.

lsof -ti :8620 | xargs kill -9 2>/dev/null || true
lsof -ti :8686 | xargs kill -9 2>/dev/null || true
# kill anything still holding the PD port (fuser avoids lsof dependency)
fuser -k 8620/tcp 2>/dev/null || true

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

⚠️ This cleanup now depends on fuser, but the PD workflow preflight still requires lsof curl java and never checks fuser. An lsof-free environment therefore skips the revised suite, while a runner without fuser executes it with silently ineffective port cleanup. Please remove the obsolete lsof gate, require fuser (or make cleanup dependency-free), and fail explicitly when cleanup cannot release the test ports.

@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Jul 24, 2026

@imbajin imbajin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: yes — 6.0/10 on current head d315ae8. The main direction and CI are good, but the following issues still need to be addressed before merge.

New current-head findings:

Existing comments that remain applicable on d315ae8 (not reposted; +1 used for deduplication):

Please do not treat these linked threads as fully resolved merely because some are outdated or marked resolved against older revisions; the specific residual behaviors above were revalidated on the current head.

if command_available "ss"; then
if out=$(ss -ltn 2>/dev/null); then
port_checked=1
if echo "$out" | grep -qE "$linux_pattern"; then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

‼️ ss -n and netstat -n report numeric addresses, but this pattern compares them with the configured hostname text. ServerOptionsTest.testUrlNormalizationPreservesHostnameCase explicitly supports hostnames; with restserver.url=http://localhost:8080 and ss reporting 127.0.0.1:8080, this branch sets port_checked=1, misses the listener, and never reaches the resolving /dev/tcp fallback. A controlled run on this head returns success (port free) for that occupied endpoint. Please resolve hostnames to their IPv4/IPv6 addresses before matching, or use a deadline-bounded probe for hostname inputs, and add a hostname-to-numeric-listener regression.

python3 -m http.server 54321 --bind 127.0.0.1 >/dev/null 2>&1 &
PY_PID=$!
trap "kill -9 $PY_PID 2>/dev/null || true" EXIT
sleep 1 # wait for it to bind

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

⚠️ This is a fixed port, not an ephemeral one, and the test never verifies that the Python child actually bound it. If 54321 is already occupied, Python exits immediately, check_port connects to the unrelated listener, and the test still passes. Please bind port 0 and return the selected port to the test (or at least assert child liveness/readiness before calling check_port) so this cannot produce a false-positive green result.

@bitflicker64

Copy link
Copy Markdown
Contributor Author

fixing remaining issues

@bitflicker64
bitflicker64 force-pushed the fix/lsof-port-check branch 2 times, most recently from 9d75341 to 745a8bb Compare July 24, 2026 20:38
bitflicker64 added a commit to bitflicker64/hugegraph that referenced this pull request Jul 24, 2026
…-test suite

Replace lsof with layered probe (ss → netstat → /dev/tcp) in server
check_port to eliminate startup hang in Kubernetes pods where ulimit -n
can be ~1 billion causing lsof to scan the entire FD table.

Core changes (server util.sh):
- ss -ltn: kernel socket table query, no FD scan
- netstat -ltn / netstat -an: fallback for systems without ss
- bash /dev/tcp with timeout 1: last resort, null-command probe
- Hostname resolution (getent/dscacheutil) for localhost → numeric IP
- Address-family-aware wildcard matching (no cross-family false conflicts)
- ss non-zero exit fallthrough to netstat
- No-timeout watchdog with 2s hard deadline
- URL whitespace stripping (handles ServerOptions edge case)
- IPv6 bracket notation parsing
- Octal-safe port validation via $((10#port))
- curl -fL for HTTP error detection (was curl -L)

PD and Store util.sh:
- Remove dead check_port (never called by PD/Store startup scripts)
- Fix command_available return-value inversion bug
- Fix curl download path from broken concatenation to basename output
- curl -fL for HTTP error detection

CI and cleanup:
- fuser preflight check in server-ci.yml (Linux port cleanup)
- OS-aware cleanup in test-start-hugegraph*.sh (lsof on macOS, fuser on Linux)
- Warning when neither tool available

Tests (test-check-port.sh, 34 tests, 0 failures):
- ss branch: IPv4/IPv6 occupied/free, wildcard, dot-escaping guard
- netstat branch: Linux/BSD formats, IP octet false-positive guard
- /dev/tcp: timeout mock, real ephemeral-port listener with liveness check
- Host/port conflict: cross-family wildcard, specific IP vs loopback
- Tool failure fallthrough: ss→netstat, ss+netstat→/dev/tcp
- Hostname collision: localhost resolution, unresolved fallthrough
- URL normalization + IPv6 hextet guard
- Edge cases: empty URL, port >65535, port 0, non-numeric
- download() curl fallback: PD, Store, failure propagation
- SKIP exit-code handling for setup failures

Closes apache#3105
@bitflicker64
bitflicker64 force-pushed the fix/lsof-port-check branch from 745a8bb to 21829e6 Compare July 24, 2026 22:08
…-test suite

Replace lsof with layered probe (ss → netstat → /dev/tcp) in server
check_port to eliminate startup hang in Kubernetes pods where ulimit -n
can be ~1 billion causing lsof to scan the entire FD table.

Server util.sh:
- ss -ltn, netstat -ltn/-an, bash /dev/tcp with timeout 1
- Hostname resolution (getent/dscacheutil) for localhost → numeric IP
- Address-family-aware wildcard matching (no cross-family conflicts)
- ss non-zero exit → automatic fallthrough to netstat
- Watchdog with 2s hard deadline when timeout unavailable
- URL whitespace stripping, IPv6 bracket parsing
- Octal-safe port validation via $((10#port))
- curl -fL for HTTP error detection

PD and Store util.sh:
- Remove dead check_port (never called by PD/Store)
- Fix command_available return-value inversion
- Fix download() curl path, add curl -fL

CI and cleanup:
- fuser preflight check on Linux, OS-aware port cleanup
- Warning when neither lsof nor fuser available

Tests: test-check-port.sh — 34 mock-driven tests, 0 failures
@bitflicker64
bitflicker64 force-pushed the fix/lsof-port-check branch from 937624c to 6f87b8f Compare July 24, 2026 22:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants