Skip to content

fix(docker): make the Server startup timeout configurable - #3187

Merged
imbajin merged 8 commits into
apache:masterfrom
bitflicker64:fix/server-startup-timeout-3186
Sep 7, 2026
Merged

fix(docker): make the Server startup timeout configurable#3187
imbajin merged 8 commits into
apache:masterfrom
bitflicker64:fix/server-startup-timeout-3186

Conversation

@bitflicker64

@bitflicker64 bitflicker64 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Purpose of the PR

docker-entrypoint.sh runs start-hugegraph.sh with a literal -t 120, so a Server that needs longer than 120 seconds to answer on its REST port is ended by its own container. Under an orchestrator the startup budget belongs to the startup probe, and a probe cannot extend a process that terminates itself first: with the 450-second probe budget the Helm chart in #3132 configures, the process is gone at 120 seconds. Measured on 2026-09-01, 2 of 6 Server starts across two independent installs died this way and recovered only on container restart (log excerpts and the measurement setup are in the issue).

Server startup: today the hardcoded 120 s timer ends the container inside the probe 450 s budget; with this PR one value sizes both timers

Main Changes

  • docker-entrypoint.sh reads the timeout from HG_SERVER_STARTUP_TIMEOUT_S, defaulting to the current 120, so nothing changes for deployments that do not set it. Both Server images share this entrypoint, so one change covers the RocksDB and HStore images.
  • The value is validated as a positive whole number before init-store runs. A bad value fails the container immediately with a clear message instead of surfacing later as an arithmetic error inside wait_for_startup.
  • docker-entrypoint-test.sh now records the arguments the start-hugegraph.sh stub receives and asserts the default -t 120, an explicit -t 450 override, and rejection of 2m before the server would have been started.
  • Documented as a new section in hugegraph-server/hugegraph-dist/docker/README.md. The issue proposed the environment table in docker/README.md, but chore(docker): refactor docker-compose topologies with Hubble #3149 removed that table, so the Server docker README is the current home for entrypoint variables.

Verifying these changes

  • Need tests and can be verified as follows:
    • bash hugegraph-server/hugegraph-dist/docker/docker-entrypoint-test.sh (already wired into server-ci.yml) covers the default, an override, and an invalid value.

Does this PR potentially affect the following parts?

  • Modify configurations

Documentation Status

  • Doc - Done

The entrypoint ran start-hugegraph.sh with a literal -t 120, so a Server
that needed longer than 120 seconds to answer on its REST port was
terminated by its own container, no matter how much startup budget the
orchestrator's probe allowed. Read the timeout from
HG_SERVER_STARTUP_TIMEOUT_S instead, keep 120 as the default, and reject
values that are not positive whole numbers before init-store runs.

Covered by docker-entrypoint-test.sh: default passthrough, an explicit
override, and rejection of an invalid value. Documented in the Server
docker README.

Closes apache#3186
@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 37.79%. Comparing base (3681148) to head (f24eaaf).

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #3187      +/-   ##
============================================
+ Coverage     37.77%   37.79%   +0.01%     
- Complexity     6560     6566       +6     
============================================
  Files           800      800              
  Lines         68960    68960              
  Branches       9166     9166              
============================================
+ Hits          26052    26064      +12     
+ Misses        39841    39829      -12     
  Partials       3067     3067              

☔ 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.

@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: no. Summary: The configurable timeout is wired correctly for normal values, but its accepted input range can overflow the startup deadline calculation. Evidence: Bash arithmetic reproduces a negative deadline for 9223372036854775807; all reported GitHub checks are successful.

Comment thread hugegraph-server/hugegraph-dist/docker/docker-entrypoint.sh Outdated

@bitflicker64 bitflicker64 left a comment

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.

Blocking: no. Summary: The 120s default is preserved when the variable is unset and the guard runs ahead of every config write and init-store call, but :- also lets an explicitly empty value fall back to 120 silently, which the README section added here states does not happen. The new test asserts that a bad value does not start the server without pinning the ordering guarantee the code comment claims. Evidence: ran bash hugegraph-server/hugegraph-dist/docker/docker-entrypoint-test.sh at 5cad9dc under bash 5.3 (exit 0); drove the entrypoint with a stubbed bin/ for empty, whitespace, 0, +5 and 450, where empty alone exits 0 with -t 120; reran the suite against an entrypoint with the validation block relocated below init-store.sh, which also exits 0; healthcheck values read from hugegraph-server/Dockerfile:74, hugegraph-server/Dockerfile-hstore:76 and docker/docker-compose.yml:41-46.

Comment thread hugegraph-server/hugegraph-dist/docker/docker-entrypoint.sh Outdated
Comment thread hugegraph-server/hugegraph-dist/docker/docker-entrypoint-test.sh Outdated
Comment thread hugegraph-server/hugegraph-dist/docker/README.md Outdated
Review follow-up on the configurable Server startup timeout.

The guard accepted any positive integer, but wait_for_startup computes its
deadline as $((now_s + timeout_s)). Near the 64-bit ceiling that sum wraps
negative, the wait loop exits before its first probe, and the container
reports a startup timeout immediately: the failure the variable exists to
prevent. The accepted range is now 1 to 86400 seconds, bounded first by a
five-digit pattern so the comparison itself cannot overflow.

The default also used ':-', which treats an explicitly empty value as unset
and silently restores 120. Compose writes exactly that whenever an
interpolated host variable is missing, so a deployment that believed it had
set 450 still died at 120, and the README said such a value would stop the
container. Plain '-' keeps the default for an unset variable and rejects an
empty one.

Tests now cover the accepted upper bound, an unset variable, and seven
rejected values including empty, whitespace, 86401 and INT64_MAX, and assert
that a rejected value runs neither start-hugegraph nor init-store, which pins
the ordering the guard's comment claims. The new assertions exit explicitly
rather than relying on set -e with [[ ]], which bash 3.2 ignores.

The README documents the range and that the container health check keeps its
own budget, which does not move with this variable.
Brings the branch onto 3681148 so the coverage report compares against
the current base rather than one commit behind it. No conflicts: master
touched only Java sources, this branch only the Docker entrypoint, its
test and the docker README.

@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: no. Summary: The configurable timeout is not propagated through the provided Compose topologies, and the new shell tests can report success for incorrect values on Bash 3.2. Evidence: reproduced with Docker Compose config rendering and /bin/bash 3.2.57; all findings are in the changed Docker documentation/test paths.

Comment thread hugegraph-server/hugegraph-dist/docker/README.md Outdated
Comment thread hugegraph-server/hugegraph-dist/docker/docker-entrypoint-test.sh Outdated
Comment thread hugegraph-server/hugegraph-dist/docker/docker-entrypoint-test.sh Outdated
Comment thread hugegraph-server/hugegraph-dist/docker/docker-entrypoint-test.sh Outdated
…n its tests

Second review follow-up.

No Compose topology declared HG_SERVER_STARTUP_TIMEOUT_S, so a rendered
Server still took the entrypoint default and the container budget could not
be aligned with the healthcheck start period. All three topologies now pass
it through as ${HG_SERVER_STARTUP_TIMEOUT_S-120}: unset-only, so an absent
host value renders 120 while an explicitly empty one reaches the entrypoint
and is rejected there rather than silently becoming the default. The render
test asserts both the default and a host override on every Server service,
and renders with the variable stripped so the baseline cannot be coloured by
the developer's environment.

The entrypoint tests had three ways to pass while broken. They matched -t
with a substring against a flattened "$*", where -t 1200 satisfies an
assertion for 120; the stub now records one argument per line and the
assertion reads the exact value after -t. The positive cases were bare
[[ ]], which bash 3.2, still the /bin/bash of macOS, does not fail under
set -e; they now exit explicitly and report the value they saw. The unset
case only started a child shell, which inherits an exported value, so it now
runs under env -u.

Verified on bash 3.2.57 and 5.3.15: the suite passes on both, and on 3.2
each of the three regressions above is now reported instead of passing.
@bitflicker64

Copy link
Copy Markdown
Contributor Author

The hstore failure on attempt 1 looks like a timing flake rather than anything in this PR, noting it here so it does not need diagnosing twice.

One failure out of 814 tests: VertexCoreTest.testAddVertexWithTtlAndTtlStartTime, at VertexCoreTest.java:1300. The assertion there is Assert.assertTrue(vertices.hasNext()) immediately after Thread.sleep(1100L), checking a TTL vertex is still alive, so it depends on the sleep landing inside the remaining TTL window on a loaded runner.

Attribution, for what it is worth: this branch changes three Compose files, test-compose.sh, the Docker entrypoint, its test and the docker README, and no Java. The master commit merged in here, 3681148 (#3182), added only testQueryByPrimaryValuesAndPropsWithCachedVertex to that file and left the TTL method alone. The same job flaked once on the previous head and passed on the retry.

Attempt 2 is running now. Happy to open a separate issue for the TTL timing if it is worth tracking.

bitflicker64 added a commit to hugegraph/hugegraph that referenced this pull request Sep 5, 2026
The Server image entrypoint reads HG_SERVER_STARTUP_TIMEOUT_S instead of the
fixed 120 s wait on start-hugegraph.sh, bounded and rejecting an empty value,
and the Compose files pass it through. Carried here so the helm-dev Server
image can be tested with the timeout set from the chart's probe budget
(apache#3186).
bitflicker64 added a commit to hugegraph/hugegraph that referenced this pull request Sep 5, 2026
PD REST now checks the password against auth.secret-key and answers 401 on
refusal (apache#3188). The PD image requires HG_PD_AUTH_SECRET_KEY,
wait-storage.sh sends PD_AUTH_PASSWORD, and Hubble reads
operations.pd.password.

Two conflicts with the pull requests merged before it, both resolved as the
union: the interceptor exclusion list keeps /v1/ready from apache#3185 alongside
the /actuator/** widening from apache#3189, and test-compose.sh runs the startup
timeout asserts from apache#3187 followed by the Hubble helper check from apache#3189.
render_with_timeout from apache#3187 additionally passes HG_PD_AUTH_SECRET_KEY,
which the Compose files require since apache#3189; without it the render step
would fail on the two HStore topologies.

The chart does not yet supply the PD secret, so PD Pods from an image built
at this revision will not start under the chart until that wiring lands.
@bitflicker64
bitflicker64 requested a review from imbajin September 5, 2026 06:57
bitflicker64 added a commit to bitflicker64/hugegraph that referenced this pull request Sep 5, 2026
PD images from 1.8.0 (apache#3189) check the Basic-auth password
of every management call against auth.secret-key and refuse to start without
one. The chart now keeps that value in a kept release-pd-auth Secret, or in
pd.auth.existingSecret, and hands it to the three readers: PD as
HG_PD_AUTH_SECRET_KEY, the Server storage wait as PD_AUTH_PASSWORD, and
Hubble as operations.pd.password written into its properties file by the
existing wrapper. A checksum/pd-auth annotation on the three Pod templates
rolls them when the Secret changes; the Server annotations block is now
rendered unconditionally for it. Priority and lookup semantics mirror
server.auth.token. Older images ignore the password, so the wiring is
harmless on the images the draft currently tracks.

The values schema requires one of existingSecret, value or autoGenerate and
refuses newlines, carriage returns and backslashes in an inline value, since
it lands in a Java properties file; the template guard repeats the first
rule for values that bypass the schema. The three chart-managed variables
join the reserved extraEnv lists.

README: Chart Details bullet, four parameter rows, Disaster Recovery calls
carry the secret, and the Limitations bullet separates the 1.7.0 behaviour
from 1.8.0. NOTES prints how to read the secret. New suite
pd_auth_secret_test.yaml, 9 tests; 58 in total. Lint on three presets;
renders 16 objects by default and 19 with Hubble.

Measured on a kind cluster with images built from master plus apache#3185, apache#3187
and apache#3189: the Secret is created, PD starts with the variable, the Server
storage wait passes with the credential, and Hubble lists all nine nodes.
@bitflicker64

Copy link
Copy Markdown
Contributor Author

Tested on Kubernetes on 2026-09-05, with this branch merged into the hugegraph/hugegraph testing tree.

Build under test. Tag helm-dev-20260905, commit 6ec19838: apache master 36811483 plus this PR at 0ba30a1e, #3185 and #3189. The Server image was built from that tree with docker buildx bake -f docker/bake.hcl server-hstore (linux/amd64) and carries org.opencontainers.image.revision=6ec1983889f58a52995b27a4e1ea89bd00932396, read back from the running pods. The #3132 chart set HG_SERVER_STARTUP_TIMEOUT_S=450 through server.extraEnv, matching its 450 s startup probe.

  • Invalid value: a one-off Pod from the same image with HG_SERVER_STARTUP_TIMEOUT_S=abc exited 1 with ERROR: HG_SERVER_STARTUP_TIMEOUT_S must be a whole number of seconds from 1 to 86400, got 'abc', before init-store ran.
  • Valid value: after deleting a Server Pod, ps in the replacement 2 s into its start showed ./bin/start-hugegraph.sh -j ... -t 450, and /proc/1/environ carried HG_SERVER_STARTUP_TIMEOUT_S=450. Three Servers, two Server rollouts and one image roll all started clean.
  • Not observed: a start slower than 120 s, so the last link, the start script honouring -t, rests on this PR's own tests rather than on this run. The old self-termination did not appear anywhere.

One note for whoever merges second: #3189 makes the Compose files require HG_PD_AUTH_SECRET_KEY, so render_with_timeout in docker/test-compose.sh needs HG_PD_AUTH_SECRET_KEY="${PD_SECRET}" in its env list, or test-compose.sh render fails on the two HStore topologies. That is how the tag resolves it.

@bitflicker64 bitflicker64 left a comment

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.

Blocking: no. Summary: nothing here should hold up the merge; the overflow, empty-value, argv-substring and Bash 3.2 threads are all genuinely closed at this head. One test gap worth fixing: the new Compose render assertions pass just as happily under :- as under -, so they cannot protect the empty-value semantics the entrypoint and README depend on. Four smaller notes follow. Evidence: at 0ba30a1, docker-entrypoint-test.sh and docker/test-compose.sh render both exit 0 locally (Docker Compose v5.1.2, GNU sed on PATH), and all 24 reported checks on this head are successful.

Comment thread docker/test-compose.sh
Comment thread docker/docker-compose.yml
Comment thread docker/test-compose.sh Outdated
Comment thread docker/docker-compose-hstore.yml
Comment thread hugegraph-server/hugegraph-dist/docker/README.md Outdated
…ocument it

Third review follow-up.

The render assertions could not see the "-" against ":-" choice the rest of
the change rests on: unset renders 120 and 450 renders 450 under both
spellings, and the whole suite passed against a ":-" rewrite. Only an
explicitly empty host value separates them, and that value has to reach the
container for the entrypoint to reject it. Every topology is now rendered
with an empty value and asserted to carry one.

render_with_timeout no longer repeats compose_auth's environment. The two
differ by one variable, so that is now an array the override rebinds in a
subshell, and a variable added to compose_auth reaches both renders instead
of only the baseline.

The comment above the assertions claimed alignment with the healthcheck
budget in both directions. The healthcheck values are literals giving every
Server about 360 seconds, and the documented "up -d --wait" gives up there,
so the knob aligns downward only and the comment now says so.

The two Compose files that carried a bare unset-only default without the
reason now carry the same note as the standalone file, and docker/README.md,
the file Compose users read, documents the variable beside the other host
knobs.

Verified: the render suite fails a ":-" rewrite with the expected empty-value
message, a variable added to compose_auth alone now reaches all three
renders, and both suites pass on bash 3.2.57 and 5.3.15.
The example raised HG_SERVER_STARTUP_TIMEOUT_S to 450 while the Server health
check in every Compose topology allows roughly 360 seconds, so on the slow
host the example is written for the pasted command fails: Hubble gates on
depends_on: condition: service_healthy, which a plain "up -d" honours, and
Compose reports the Server dependency unhealthy at 360. Lower the example to
300 and say that the plain "up -d" path hits the same wall as "up -d --wait".

Also strip HG_SERVER_STARTUP_TIMEOUT_S in compose_active, matching the render
path. Without it a developer who exports the variable empty gets Server
containers stopped by the new guard during "test-compose.sh smoke", with no
sign that their shell caused it.
The Server docker README still showed 450 in its Compose example, while
docker/README.md now shows 300. The Compose files give the Server health
check roughly 360 seconds, and Hubble gates on it with
depends_on: condition: service_healthy, so a 450 second startup budget
makes docker compose up -d exit non-zero. Match the two READMEs at 300.

The docker run example keeps 450. That path does not block on health, and
the section already explains the 135 second image health check and how to
raise it with --health-start-period.
@bitflicker64

Copy link
Copy Markdown
Contributor Author

@imbajin Ready for a review pass when you have time. Pushed 585b099 and 7e2818c, both docs and test-harness only.

Why those two commits. The ### Server startup timeout section in docker/README.md demonstrated HG_SERVER_STARTUP_TIMEOUT_S=450 docker compose up -d, and 450 is past the budget that command actually has to clear. The server health check in docker/docker-compose.yml and docker-compose-hstore.yml is start_period: 60s plus retries: 30 at interval: 10s, roughly 360 seconds, and hubble carries depends_on: server: condition: service_healthy in both files. docker compose up -d honors service_healthy without --wait, so on the slow host that example is written for, the pasted command exits non-zero with hubble unstarted. The caveat two sentences below named only up -d --wait, which reads as safe to anyone not passing that flag.

585b099 lowers the example to 300 and reworks the caveat to name the depends_on: service_healthy path as well, so a reader who genuinely needs more than 360 seconds is still told what to change. It also gives compose_active in docker/test-compose.sh the same env -u HG_SERVER_STARTUP_TIMEOUT_S that compose_auth already had, so a developer with the variable exported empty does not get server containers exiting on the new guard during test-compose.sh smoke with the crash looking unrelated to their shell. CI was unaffected either way, since the variable is unset there.

7e2818c is one token. hugegraph-server/hugegraph-dist/docker/README.md showed the same Compose command at 450, so the two READMEs contradicted each other; it is now 300 in both. The docker run example in that file stays at 450 deliberately: docker run does not block on health, the image defaults are --interval=15s --start-period=90s --retries=3, and the section already tells the reader the container reports unhealthy around 135 seconds and to raise --health-start-period.

What I checked on the branch. The validation matrix against the real entrypoint: unset, empty, " 300 " with spaces, +300, 0300, 300s, 1e3, -1, 0, 86401, the 64-bit max, and a trailing newline. Each behaves as documented and the entrypoint refuses loudly rather than proceeding on a garbage value. A trailing newline is rejected because bash ERE $ is end of string, not end of line.

(( )) evaluates variable contents recursively, so I went looking for an expression that could reach it. The guard is [[ ! ... =~ ^[1-9][0-9]{0,4}$ ]] || (( ... )), which short-circuits, so the arithmetic only ever runs on an already-matched plain integer. On the Compose side the unset-only - default means an explicitly empty value still reaches the container and is refused there instead of being dropped as a YAML null, and nothing lets the value inject into the rendered config.

Ordering holds: the guard is at docker-entrypoint.sh:114-122, the first set_prop at 149, init-store.sh at 219, so a bad value mutates nothing. A typo produces a clear refusal on every restart rather than a quiet one, and that is still better than the old behaviour, where a slow host let the server die and loop at 120s with nothing explaining why. docker-entrypoint-test.sh fails with expected start-hugegraph.sh -t 450, got -t 120 when the entrypoint is reverted to the merge base, so it catches the regression it exists for.

One nit I left alone. The regex admits up to 99999 and a second check refuses anything over 86400, so the bounds live in two places. The error message is correct either way and collapsing them would churn the tests, so it did not seem worth it. Happy to do it if you would rather have one check.

A whole top-level section for one environment variable was more room than
the knob deserves, and it wrapped at about 70 characters where the rest of
the file runs long. Section 7 is gone: the variable is now two sentences at
the end of section 6, next to the health check it interacts with, and the
budget arithmetic and the docker run example sit in a collapsed block for
readers who need them. docker/README.md gets the same treatment.

Net 26 lines lighter. The dead #7-server-startup-timeout anchor and the
stale "section 7" comment in test-compose.sh are updated with it.
@bitflicker64

Copy link
Copy Markdown
Contributor Author

Fixed in f24eaaf59, taking all four points.

Section 7 is gone. One environment variable did not need a top-level section, so it is now two sentences at the end of section 6, beside the health check it interacts with. The budget arithmetic and the docker run example moved into a <details> block, and docker/README.md got the same treatment. Added prose now runs long instead of wrapping at about 70, which also matches the rest of that file. Net 26 lines lighter.

Two things that would have broken with the section: docker/README.md linked to #7-server-startup-timeout, now #6-process-supervision--health-checks, and test-compose.sh:323 cited "README section 7".

@imbajin
imbajin merged commit bed2e45 into apache:master Sep 7, 2026
24 checks passed
bitflicker64 added a commit to bitflicker64/hugegraph that referenced this pull request Sep 7, 2026
apache#3187 landed on master and both branches append to the Compose render
contract in docker/test-compose.sh. Kept both sides: the startup timeout
render assertions from master, then hubble_password_helper_check.
bitflicker64 added a commit to bitflicker64/hugegraph that referenced this pull request Sep 7, 2026
Brings in the master merge already made on the hugegraph/hugegraph mirror,
so the fork and the mirror share one history again. Master now carries
apache#3187; it does not touch anything this branch changes.
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.

[Bug] Docker entrypoint hardcodes start-hugegraph.sh -t 120; make the Server startup timeout configurable

2 participants