fix(server): configure finite DNS cache TTL - #3126
Conversation
Load networkaddress.cache.ttl from a Java security properties file when the HugeGraph security manager is enabled. Add distribution-level coverage for property semantics, startup wiring, disabled mode, and operator overrides. Fixes apache#3124
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3126 +/- ##
============================================
- Coverage 39.19% 39.11% -0.08%
Complexity 264 264
============================================
Files 770 771 +1
Lines 65779 65820 +41
Branches 8726 8732 +6
============================================
- Hits 25779 25745 -34
- Misses 37247 37323 +76
+ Partials 2753 2752 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: The DNS TTL policy can silently disappear with a retained configuration directory, and its regression test can be skipped by unrelated startup prerequisites. Evidence: exact-head static review, a controlled missing-security-properties reproduction, workflow/log inspection, and git diff --check; codecov/project is failing.
There was a problem hiding this comment.
🟢 Ready to approve
The changes are narrowly scoped to Server startup wiring, include a packaged configuration, and add CI coverage validating both the security property value and argument ordering/overrides.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This PR addresses stale JVM DNS caching when HugeGraph Server runs with HugeSecurityManager enabled (Java 11 behavior), which can prevent HStore writes from recovering after a Kubernetes Store pod is replaced behind the same DNS name.
Changes:
- Add a packaged security properties file setting
networkaddress.cache.ttl=30. - Wire the file into Server startup via
-Djava.security.properties=...only whenHugeSecurityManageris enabled. - Add a dedicated CI startup-time verification script and run it in Server CI before existing startup tests.
File summaries
| File | Description |
|---|---|
| hugegraph-server/hugegraph-dist/src/assembly/travis/test-java-security-properties.sh | Adds a focused runtime + launcher-argument test to verify the security property value and startup wiring behavior. |
| hugegraph-server/hugegraph-dist/src/assembly/static/conf/java-security.properties | Introduces the packaged security properties file that sets a finite positive DNS cache TTL. |
| hugegraph-server/hugegraph-dist/src/assembly/static/bin/hugegraph-server.sh | When security is enabled, adds -Djava.security.properties=... alongside HugeSecurityManager to ensure finite DNS caching. |
| .github/workflows/server-ci.yml | Runs the new security-properties verification script as part of the existing Server dist startup test step. |
Review details
- Files reviewed: 3/4 changed files
- Comments generated: 0
- Review effort level: Low
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
- validate the effective Java security DNS TTL - bootstrap before logging or hostname resolution - install HugeSecurityManager across supported JDKs - cover overrides and malformed security properties
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: The security-enabled launcher cannot start on documented JDK 24+, and its success-path regression can accept unrelated early JVM failures. Evidence: six independent exact-head review lanes; OpenJDK JEP 486 and static launcher/bootstrap analysis; HugeGraphServerBootstrapTest passed 2/2 on JDK 11; git diff --check passed; visible exact-head checks are green.
JDK 24 removed the Security Manager (JEP 486), so '-Djava.security.manager=allow' is a fatal VM initialization error and System.setSecurityManager() always throws. Fail the launcher with an actionable message instead of a cryptic VM error, and keep the security-disabled path unchanged. Also assert a positive downstream signal in the launcher success and security-disabled tests, so an unrelated failure such as a missing bootstrap class can no longer pass, and report the underlying cause when bootstrap validation fails.
'java -version' prints a preamble line such as 'Picked up JAVA_TOOL_OPTIONS: ...' whenever JAVA_TOOL_OPTIONS or _JAVA_OPTIONS is set, which is how APM agents are usually installed. Reading 'head -1' then left JAVA_VERSION unusable, so every version-gated branch silently evaluated false: the JDK 24+ guard never fired and '-Djava.security.manager=allow' was dropped on JDK 18-23, which broke the programmatic HugeSecurityManager install there. Select the version line explicitly, strip any pre-release suffix and reject a non-numeric result. Also name the bundled policy file in the server log when it is missing or unreadable, so an upgrade that reuses an older conf/ reports the cause in the log start-hugegraph.sh points at rather than only on the bootstrap's stderr, and include the effective java.security.properties in the bootstrap error.
ba36d5c to
9438a94
Compare
|
Follow-up review found two issues in my own earlier commit, both fixed in 9438a94:
Both are covered by new regression tests, and each was confirmed non-vacuous by reverting the fix and checking the test fails. |
There was a problem hiding this comment.
🟡 Not ready to approve
The new test-java-security-properties.sh reintroduces fragile java -version | head -1 parsing (can break under JAVA_TOOL_OPTIONS preambles), and the launcher logs a misleading “missing bundled properties” error even when an operator override is already provided.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Suppressed comments (1)
hugegraph-server/hugegraph-dist/src/assembly/travis/test-java-security-properties.sh:63
- JAVA_MAJOR parsing uses
head -1onjava -versionoutput, which breaks when the JVM prints a preamble likePicked up JAVA_TOOL_OPTIONS: ...(the same issue this PR fixes inhugegraph-server.sh). Withset -euo pipefail, a non-numeric JAVA_MAJOR can also make the-getest abort the script unexpectedly.
JAVA_MAJOR=$($JAVA_BIN -version 2>&1 | head -1 | cut -d'"' -f2 |
sed 's/^1\.//' | cut -d'.' -f1)
SECURITY_MANAGER_OPTION=""
if [[ "$JAVA_MAJOR" -ge 18 ]]; then
SECURITY_MANAGER_OPTION="-Djava.security.manager=allow"
fi
- Files reviewed: 5/6 changed files
- Comments generated: 0 new
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
An operator may deliberately replace the packaged policy with their own -Djava.security.properties=<file>, which the JVM applies last, so a missing bundled file is harmless there. Reporting it unconditionally logged a misleading error on a startup that then succeeded. Track the last such option instead, remembering that an empty value clears any earlier override. Also select the version line in the test harness the same way the launcher now does, so a 'Picked up JAVA_TOOL_OPTIONS: ...' preamble cannot silently drop -Djava.security.manager=allow on a JDK 18-23 runner.
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: JVM version parsing can still select a tool/agent preamble, and invalid operator policy overrides suppress the actionable upgrade diagnostic. Evidence: six independent exact-head review lanes, focused shell reproductions, HugeGraphServerBootstrapTest passed 2/2, git diff --check passed, and visible exact-head checks are green.
| # "Picked up JAVA_TOOL_OPTIONS: ..." before it whenever JAVA_TOOL_OPTIONS or | ||
| # _JAVA_OPTIONS is set, and reading that line instead would leave JAVA_VERSION | ||
| # unusable and silently skip every version-gated option below. | ||
| JAVA_VERSION=$($JAVA -version 2>&1 | awk -F'"' '/version "/ {print $2; exit}' | |
There was a problem hiding this comment.
version ", not specifically the JVM banner. For example, APM agent version "7.2.0" before openjdk version "21.0.8" makes this pipeline return 7, so a supported JDK is rejected; a preamble containing version "24" can likewise trigger the new upper-bound branch. The test helper uses the same broad match, so it cannot catch this. Please anchor the match to the actual java/openjdk version line and add a tool-options/agent preamble regression.
There was a problem hiding this comment.
Fixed in 9d53f15. The match is now anchored to the JVM banner line itself — only lines starting with java version " or openjdk version " are considered — in both the launcher and the test helper's own JAVA_MAJOR parse.
Added two mock-JVM regressions where an agent banner precedes the JVM's:
Elastic APM agent version "7.2.0"ahead of a JDK 21 banner must still emit-Djava.security.manager=allow(the unanchored match reads 7 and rejects the runtime as below the minimum).APM agent version "24.0.1"ahead of a JDK 11 banner must not trip the JDK 24+ guard and must reach the bootstrap without a security-manager option.
Both fail against the unanchored match (re-verified by mutating the packaged launcher back to /version "/).
| -Djava.security.properties=) | ||
| SECURITY_PROPERTIES_OVERRIDDEN="false" ;; | ||
| -Djava.security.properties=?*) | ||
| SECURITY_PROPERTIES_OVERRIDDEN="true" ;; |
There was a problem hiding this comment.
-Djava.security.properties=... token suppresses the missing bundled-policy diagnostic without checking whether the replacement file exists, is readable, or supplies a valid positive TTL. The bootstrap then rejects the invalid policy, but daemon stderr goes to hugegraph-server-stdout.log while start-hugegraph.sh directs operators to hugegraph-server.log, which now lacks the cause. Please suppress this diagnostic only after validating the effective override, or mirror bootstrap failures into the advertised log, and cover missing/unreadable/invalid override files in daemon mode.
There was a problem hiding this comment.
Fixed in 791947c, via the second option: the bootstrap's rejection is mirrored into the advertised log rather than validated shell-side.
I deliberately did not add launcher-side validation of the override file. Only the JVM's own properties parsing decides what the override loads to — the escaped-key, line-continuation and file:-URL cases in the test suite are all files a plain shell check would mis-classify — so a shell validator could disagree with the bootstrap in both directions and reintroduce exactly this inconsistency. The bootstrap stays the single validator.
Mechanics: in daemon mode the launcher passes -Dhugegraph.bootstrap.error.log=${LOGS}/hugegraph-server.log, and the bootstrap appends the same fatal message it prints to stderr (including the effective java.security.properties value, so the broken override is named) to that file, best-effort, before the security manager is installed and without touching the logging framework. Stdout mode is unchanged since stderr is already operator-visible there.
New daemon-mode tests cover missing, unreadable and infinite-TTL operator overrides, asserting both the cause and the override path land in hugegraph-server.log; the unreadable fixture also carries invalid content so the case still fails closed where permission bits do not apply (root). No-op'ing the error-log property in the packaged launcher makes them fail.
An agent installed through JAVA_TOOL_OPTIONS may print its own banner containing 'version "..."' ahead of the JVM's, so matching the first line with 'version "' can read the agent version instead of the runtime version: a low agent version rejects a supported JDK, and a high one trips the JDK 24+ security guard. Match only lines starting with 'java version' or 'openjdk version', in the launcher and in the test helper, and add agent-banner regressions in both directions.
Any non-empty -Djava.security.properties=... token suppressed the missing-bundled-policy diagnostic without checking what the override loads to, and the bootstrap's rejection of a broken override only reached hugegraph-server-stdout.log while start-hugegraph.sh points operators at hugegraph-server.log. Shell-side validation cannot close this gap: only the JVM's own properties parsing decides the effective policy (escaped keys and line continuations included), so a shell check could pass a file the JVM rejects. Keep the bootstrap as the single validator and have it append its fatal errors to the file named by hugegraph.bootstrap.error.log, which the launcher passes in daemon mode only; stderr already reaches the operator in stdout mode. Cover missing, unreadable and infinite-TTL operator overrides in daemon mode, asserting the cause and the override path land in hugegraph-server.log.
Purpose of the PR
Problem, end to end
same stable DNS name.
HugeSecurityManagerinstalled by default.SecurityManagerpresent and no explicit policy, Java 11 falls back tonetworkaddress.cache.ttl = -1, soInetAddressCachePolicyisFOREVER.HStore writes keep going to an address that no longer exists.
The setting is only honoured as a security property. The ordinary
-Dnetworkaddress.cache.ttlsystem property has no effect here, which is whythat form is deliberately not used.
Scope
In scope — the Server JVM's DNS caching policy, end to end along one path:
hugegraph-server.shbuilds the JVM arguments -> the JVM loads the packagedsecurity properties ->
HugeGraphServerBootstrapvalidates the effectivepolicy and installs
HugeSecurityManager-> control passes toHugeGraphServer.The guarantee this PR is trying to make: with the security check enabled, the
Server either starts with a finite positive DNS TTL, or it does not start at
all. Silently falling back to an indefinite cache is treated as a bug, so
every failure mode along that path fails closed.
Boundary — this makes the Server re-resolve the name within the TTL. It
does not decide what happens to connections that were already open against the
old address.
Out of scope
HugeSecurityManagersandbox so the security check can run onJDK 24+ (JEP 486). This PR only reports that incompatibility clearly instead
of failing with a raw VM initialization error.
Main Changes
conf/java-security.propertieswithnetworkaddress.cache.ttl=30, loaded through-Djava.security.propertiesonly when
HugeSecurityManageris enabled.networkaddress.cache.ttlin a smallHugeGraphServerBootstrapbefore the server starts, so a missing, unreadableor infinite policy fails fast rather than silently restoring the indefinite
cache. Because it reads the effective
Security.getProperty, it also coverspolicies injected through
_JAVA_OPTIONSor an operator override.JEP 486 removed the Security Manager there:
-Djava.security.manager=allowis a fatal VM initialization error and
System.setSecurityManager()alwaysthrows. Security-disabled startup on JDK 24+ is unchanged.
java -versionoutput by selecting the JVM banner line, anchoredto its
java version/openjdk versionprefix, instead ofhead -1or thefirst line containing
version ". A preamble is printed wheneverJAVA_TOOL_OPTIONS/_JAVA_OPTIONSis set, which is how APM agents arenormally installed, and such an agent may print its own
version "..."banner: reading either line leaves
JAVA_VERSIONunusable or reports theagent's version, which can reject a supported JDK or trip the JDK 24+ guard.
unreadable, so an upgrade that reuses an older
conf/reports the cause in thelog
start-hugegraph.shpoints operators at. An operator override suppressesonly this missing-file diagnostic, never validation: in daemon mode, where
stderr only reaches
hugegraph-server-stdout.log, the bootstrap mirrors itsrejection of the effective policy — a missing, unreadable or infinite-TTL
override included — into that same server log.
options including an explicit security-properties override.
CI step, so it is not gated behind the broader startup prerequisites.
Verifying these changes
test-java-security-properties.sh: passed.HugeGraphServerBootstrapTest: 2 passed, 0 failed.Fail-closed behaviour was probed directly: an infinite TTL injected through
_JAVA_OPTIONS, and a-Dnetworkaddress.cache.ttl=30system property pairedwith an infinite security-properties file, are both rejected.
Each new assertion was confirmed non-vacuous by mutating the production code and
checking the test then fails:
ClassNotFoundExceptionpassed the previous negative-only assertion, fails the new positive-sentinel onehead -1version parsingversion "lineDoes this PR potentially affect the following parts?
Documentation Status
Doc - TODODoc - DoneDoc - No Need