fix(bin): replace lsof with /dev/tcp in server check_port; remove dea…#3105
fix(bin): replace lsof with /dev/tcp in server check_port; remove dea…#3105bitflicker64 wants to merge 3 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3105 +/- ##
============================================
- Coverage 42.32% 36.21% -6.12%
- Complexity 460 498 +38
============================================
Files 819 808 -11
Lines 70745 69594 -1151
Branches 9366 9169 -197
============================================
- Hits 29944 25204 -4740
- Misses 37705 41669 +3964
+ Partials 3096 2721 -375 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@copilot review |
imbajin
left a comment
There was a problem hiding this comment.
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.
imbajin
left a comment
There was a problem hiding this comment.
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.
imbajin
left a comment
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
- Hostname inputs bypass collision detection:
ss -n/netstat -nreport numeric addresses, so a supported value such aslocalhostis missed. - The real-listener test can pass without creating its listener: fixed port 54321 may already be occupied and Python bind success is not verified.
Existing comments that remain applicable on d315ae8 (not reposted; +1 used for deduplication):
- No hard deadline without the external
timeoutcommand: the raw/dev/tcpfallback can still stall on dropped SYN packets. - URL normalization is still incomplete: bracketed IPv6 was fixed, but leading/trailing whitespace accepted by
ServerOptionsstill bypasses this check. - Endpoint/dual-stack conflict semantics remain incomplete: the old IPv6-hextet false positive was fixed, but IPv4 and
IPV6_V6ONLYIPv6 wildcard listeners are still treated as unconditional cross-family conflicts. fusercleanup remains unenforced: Linux startup tests usefuserwithout checking that it exists and suppress cleanup failures.
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.
|
fixing remaining issues |
9d75341 to
745a8bb
Compare
…-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
937624c to
6f87b8f
Compare
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: This head reverts a merged query-correctness fix and also leaves new URL parsing, resolver timeout, and test-cleanup gaps. Evidence: exact-head static review by 6 independent lanes; bash -n passed; focused shell suite reported 33 pass, 0 fail, 1 environment-limited skip; visible exact-head checks are green.
4cf5886 to
8307426
Compare
8307426 to
9fdc941
Compare
|
Pushed a follow-up addressing the outstanding threads on the current head. Status per open item: Fixed:
Documented, not changed:
Re-requesting review on the latest commit — let me know if any of the above needs more than a comment. |
There was a problem hiding this comment.
Blocking: yes — 5.5/10 on current head 9fdc941d.
Why 5.5/10
Only two new inline comments were added because the other blockers already have review threads; the score reflects the whole current implementation, not the number of new comments.
actual port conflict / stalled resolver
|
v
probe output is missing, ambiguous, or not bind-equivalent
|
v
check_port returns "free" — or never returns
|
v
Java bind fails with EADDRINUSE / startup hangs
‼️ Endpoint correctness: dual-stack semantics and the wildcard fallback can still produce a falsefree.⚠️ Bounded execution: the resolver deadline is not a guaranteed TERM-to-KILL deadline.⚠️ Configuration and CI: URL/default-port parsing remains partial, and PD/Store cleanup prerequisites do not match the implementation.⚠️ Fallback confidence: a successful but unusable listener table is treated asfree, while the production no-timeoutwatchdog is not exercised.
Expected design
Make every probe return occupied, free, or unknown:
- Normalize and validate the configured endpoint once; explicitly reject unsupported ambiguous forms such as unbracketed IPv6.
- Parse listener records into address-family/address/port tuples and apply wildcard plus
v6onlybind semantics. - Treat execution failure and empty/unparseable output as
unknown, then continue to a bounded fallback. - Put both resolution and connection attempts under a TERM-to-KILL deadline.
- Test the actual production branches and align CI prerequisites with the cleanup tool being used.
Only validated, bind-equivalent evidence should produce free.
9fdc941 to
1f3f01e
Compare
|
All three 5.5/10 items addressed in
Also fixed a regression from authority parsing: invalid ports like 35/35 tests pass. Re-requesting review. |
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: The no-timeout port probe passes its arguments in the wrong order, and its new regression test is invalid; the latest-head server CI is failing. Evidence: exact-head static inspection; a controlled helper invocation printed 8080|2; HugeGraph-Server CI reported 34 passed and 1 failed.
1f3f01e to
9b60fbb
Compare
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: This head still has address-normalization and URL-parsing gaps that can miss occupied ports, while failed downloads can poison subsequent retries. Evidence: exact-head static review, controlled shell reproductions, bash -n, 35/35 focused shell tests, and visible codecov/project failure.
5237843 to
0e60673
Compare
…lbacks and harden startup scripts 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: - Token-based listener matching with normalize_addr() for IPv6 canonicalization - IPv6 addresses canonicalized via getent ahosts (Linux) or python3 (macOS/BSD) - Authority extraction strips ? and # in addition to / and path - Same-family wildcard matching (IPv4/IPv6 isolated) - run_with_deadline() helper: deadline-bounded /dev/tcp without timeout - Atomic download(): temp file + mv on success, rm on failure - curl -fL with correct -o before -- argument ordering - wget progress flags in indexed array for set -u / Bash 3.2 safety - Helper functions scoped with _check_port_ prefix and unset -f cleanup - netstat -an fallback uses Bash built-in case-insensitive match - normalize_addr() wraps getent in timeout 2, tr fallback for case - process_num() returns boolean 0/1, process_id() echoes result - command_available() uses command -v instead of [[ -x ]] - get_ip() falls back to loopback on empty parse - free_memory() Darwin branch guards empty values - wait_for_startup() separates local/assignment, quotes http_code - read_property() localizes file_name/property_name - port_checked=1 set when ss/netstat succeeds and comparison is reliable PD and Store util.sh: - Remove dead check_port, fix command_available - Atomic download() matching server pattern - wget progress flags in indexed array - read_property() localizes file_name/property_name CI and test scripts: - test-check-port.sh runs as fast gate after Compile - fuser preflight on Linux, lsof removed - OS-aware port cleanup (fuser on Linux, safe lsof loop on macOS) - PD/Store/Server tests include fuser in non-Darwin prereqs - PD/Store tests call STOP_SCRIPT before manual cleanup - test-start-hugegraph.sh no longer masks java_home exit status Tests: test-check-port.sh - 40 mock-driven tests, 0 failures - timeout mock makes ss/netstat free cases deterministic - IPv6 canonicalization (compressed vs expanded) - download tests verify temp file + atomic rename + cleanup - wget success-path tests for PD and Store - Bash 3.2-safe array expansion throughout
0e60673 to
e960cc5
Compare
- Localize read_property variables (match PD/Store) - Use if ! download / if ! tar in ensure_package_exist so atomic download failures are detected without a fragile $? check Also merges upstream master so the branch is no longer diverged (RISC-V libatomic + CI from apache#3102 kept intact with check_port).
Status since imbajin’s last reviewAddressing the blocking items from the later review rounds (including heads Port check correctness
URL / authority parsing
download() / PD–Store
CI / tests / cleanup
Also
Local: Happy to take another look when you have time — thanks for the thorough reviews. |
There was a problem hiding this comment.
Blocking: yes — 5.5/10 on current head 986c95f1.
The implementation has improved, but the preflight still has verified false-positive and false-negative behavior. Below is the complete current-head status so resolved threads do not hide residual cases.
Fixed on this head
- Shell configuration values are passed as positional parameters instead of being reparsed as code.
- Authority parsing now stops at
/,?, or#; surrounding whitespace and bracketed IPv6 are handled. - Hostnames and equivalent IPv6 spellings are normalized before listener comparison.
- Nonzero
ss/netstatexecution falls through instead of being treated as “free”. - The no-
timeoutargument-order regression, invalidlocaldeclarations, and listener cleanup were fixed. - Normal downloads use a temporary file and rename only after successful transfer.
- The accidental rollback of the merged count-optimization fix was removed.
Still required
‼️ macOS false occupied:netstat -ltnincludes non-listening states, while the parser scans every token in every row. An unrelatedESTABLISHEDpeer port can block startup. BSD wildcard rows also carry their family intcp4/tcp6; discarding that field creates cross-family false positives.‼️ Linux dual-stack false free: the dual-stack thread remains applicable.[::]:PORTcan also occupy IPv4 whenIPV6_V6ONLY=0, but the implementation and test currently assume the families are independent.⚠️ Wildcard false free: the/dev/tcpfallback checks loopback only, so a listener bound only to another local interface is missed before a wildcard bind.⚠️ Unknown is treated as free: a successful but empty or unparseable listener table can still setport_checked=1; this is reproducible for thess/ Linux-netstatpaths.⚠️ URL normalization is still partial: the default-port thread still applies to uppercase schemes and scheme-less values accepted byServerOptions. Ambiguous unbracketed IPv6 should be explicitly rejected rather than guessed.⚠️ Resolution is not always bounded: the resolver deadline thread still has a no-timeoutpath that can rungetentsynchronously.⚠️ CI prerequisites disagree with the implementation: the PD/Store workflow still checkslsof, while Linux cleanup usesfuser.⚠️ The production no-timeoutpath lacks end-to-end coverage: the existing watchdog test tests the helper, notcheck_port → run_with_deadlinewith a real occupied/free endpoint.⚠️ Verified Store download can report success after installation failure:mvfailure is overwritten byreturn 0.⚠️ The configured startup timeout is not a hard deadline:wait_for_startup()calls synchronouscurlwithout--connect-timeout/--max-time, so one blackholed request can exceed the outer timeout indefinitely.⚠️ PID-based temporary names are not exclusive:...tmp.$$is shared by concurrent background subshells. Current callers are sequential, so either usemktempas a small fix or explicitly keep concurrency unsupported; no locking framework is needed.- 🧹 Scope and hygiene: unrelated
process_num/process_id/ memory and shared-helper contract changes have no focused compatibility tests, andtest-check-port.sh:319contains trailing whitespace.
Minimal design for this PR
Keep this as a best-effort startup preflight; the real server bind remains authoritative.
- Normalize only enough to obtain a validated port: case-insensitive
http/https, explicit/default port, scheme-less value, and bracketed IPv6. Reject ambiguous input with a clear warning. - Detect any LISTEN socket on that port, matching the original conservative behavior:
- Linux: filtered
ss -H -ltn(orfuserfallback). - macOS/BSD:
netstat -an -p tcp, inspecting onlyLISTENrows and the local-address column.
- Linux: filtered
- Return
busy,free, orunknown. Tool failure and unparseable output areunknown; warn and let the server perform the authoritative bind. - Remove DNS resolution, address canonicalization, cross-family endpoint matching,
/dev/tcp, and its watchdog from this path. Port-level detection makes the dual-stack and wildcard cases conservative without reproducing kernel socket semantics in Bash. - In the same PR, check
mv, usemktemp, bound the startupcurlby the remaining deadline, align CI prerequisites with the selected OS tool, and revert or split unrelated helper refactors. - Replace the broad mock matrix with a compact contract suite: URL-to-port cases; Linux
ssbusy/free/failure; BSDLISTENversusESTABLISHED;unknownfallback; one real ephemeral listener; rename failure; and a blocking startup request.
This handles the current blockers in one PR without adding a new abstraction or expanding support for rare socket configurations.
|
|
||
| if [[ $in_use -eq 0 && $port_checked -eq 0 ]] && command_available "netstat"; then | ||
| matched_token=0 | ||
| if out=$(netstat -ltn 2>/dev/null) && echo "$out" | grep -qi "listen"; then |
There was a problem hiding this comment.
netstat -ltn succeeds but includes non-listening TCP states. This branch only checks whether the complete output contains any LISTEN, then scans every token in every row, so an unrelated ESTABLISHED peer port can be mistaken for a local listener. I reproduced this on macOS: no listener existed on 443 (lsof -iTCP:443 -sTCP:LISTEN was empty), but an outbound connection to :443 made check_port "http://0.0.0.0:443" exit 1. Please select the parser by OS and inspect only the local-address field of LISTEN rows; a conservative port-only comparison is sufficient here.
| rm -f -- "$tmp" | ||
| return 1 | ||
| fi | ||
| mv -f -- "$tmp" "$filepath" |
There was a problem hiding this comment.
return 0 below, so callers can export LD_PRELOAD as if the verified library were installed even when mv failed. Keep this simple: mv -f -- "$tmp" "$filepath" || { rm -f -- "$tmp"; return 1; }, and add one rename-failure test.
Purpose of the PR
Fix a startup hang in kind/Kubernetes environments caused by
lsofscanning an enormous file descriptor table, and remove deadcheck_portcode from pd/store.Main Changes
hugegraph-server/.../util.shlsof -i :PORTwith a layered probe:ss -ltn→netstat -ltn/netstat -an→bash /dev/tcpwithtimeout 1normalize_addr()for IPv6 canonicalizationgetent/dscacheutilbefore matchingrun_with_deadline()helper: deadline-bounded/dev/tcpwithouttimeoutcommanddownload(): temp file +mvon success,rmon failurecurl -fLwith correct-obefore--argument orderingwgetprogress flags in indexed array forset -u/Bash 3.2 safety_check_port_prefix andunset -fcleanupcommand_available()usescommand -vinstead of[[ -x ]]get_ip()falls back to loopback on empty parsefree_memory()Darwin branch guards empty valuesprocess_num()returns boolean0/1,process_id()echoes resultwait_for_startup()separateslocal/assignment, quotes%{http_code}read_property()localizesfile_name/property_nameensure_package_exist()usesif ! download/if ! tar(no fragile$?check)port_checked=1set whenss/netstatsucceeds and comparison is reliablehugegraph-pd/.../util.shandhugegraph-store/.../util.shcheck_portcommand_availableinversiondownload()matching server patternwgetprogress flags in indexed arrayread_property()localizesfile_name/property_name.github/workflows/server-ci.ymltest-check-port.shruns as fast gate after Compilelsoffrom preflight; addfuseron Linuxtest-start-hugegraph*.shfuserin non-Darwin prereqsSTOP_SCRIPTbefore manual cleanuptest-start-hugegraph.shno longer masksjava_homeexit statusLatest
master(includes RISC-V / RocksDB support from feat(server): support build & use(RocksDB) on RISC-V #3102); branch is no longer divergedread_propertylocals +ensure_package_existerror handling aligned with PD/Store986c95f1(feature commit + merge + polish)Tests
New
test-check-port.sh— 40 mock-driven unit tests, 0 failures.Documentation Status
Doc - No Need