fix(load-test): repair docker-compose-loadtest.yml stack and native-image 404 handling - #345
Merged
Merged
Conversation
…mage 404 handling
The load-test compose stack and its init script had drifted from working
state (unpinned postgres now resolves to a breaking v18, missing POSTGRES_DB,
Play-era DATABASE_URL env vars the Quarkus app never reads, a deleted realm
file path, legacy Keycloak env vars the current image ignores) — none of it
came up cleanly from a fresh volume. Fixes:
- Pin postgres to 16 and set POSTGRES_DB/POSTGRES_USER defaults; the
entrypoint init script also now grants CREATE on the public schema per
database (Postgres 15+ stopped granting it by default), fixing Flyway/
Keycloak migration failures.
- Fix translatr's datasource env vars to the ones Quarkus actually reads
(JDBC_DATABASE_URL/DATABASE_USER/DATABASE_PASSWORD), and point Keycloak's
realm import at docker/Translatr-realm.json (the compose file still
referenced the pre-rename docker/keycloak-realm.json).
- Migrate Keycloak's env vars/command to the current image's config format,
and add KC_HOSTNAME/KC_HOSTNAME_BACKCHANNEL_DYNAMIC so the browser-facing
and in-network (sso:8080) hostnames resolve independently — without it,
Keycloak's dynamic hostname provider caches whichever host reached it
first, and translatr's own login flow can end up trying to reach it at
localhost:8080 from inside its own container.
- Add healthchecks and depends_on ordering (database -> translatr ->
loadgenerator) so containers don't race each other on a cold start.
- Ignore the histogram-quantile binary init-clickhouse downloads into a
bind-mounted repo path at container start.
Also fixes two native-image-only bugs found while exercising the stack:
- ErrorResponse lacked @RegisterForReflection, so any JAX-RS exception
mapper that returns one 500s in the native build ("No serializer found
... this appears to be a native image", "in which case you may need to
configure reflection for the class") instead of its intended status
code — turning ordinary 404s into 500s.
- GET / and GET /ui (no trailing slash) had no matching static resource
(Quinoa serves the SPA under /ui/) and fell through to that same 404;
added prod-only redirects to /ui/, mirroring the existing dev/test-only
UiLandingResource.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The load-test compose stack (
docker-compose-loadtest.yml+docker/entrypoint-initdb.d/init-translatr.sh) had drifted from working state and didn't come up cleanly from a fresh volume. While running it (with the SigNoz observability overlay) and then browsing the app it launches, I hit and fixed several more issues along the way — some infra, two in the app's native-image build.Compose / init fixes
postgresnow resolves to v18, which breaks the direct/var/lib/postgresql/datamount this file uses. Pinned topostgres:16, addedPOSTGRES_DB/POSTGRES_USERdefaults, and a healthcheck.publicschemaCREATEis no longer granted to all users by default, which was silently breaking Flyway and Keycloak's own schema migrations. The init script now grants it explicitly per database.DATABASE_URL/DATABASE_DRIVER, which the Quarkus app never reads — it readsJDBC_DATABASE_URL/DATABASE_USER/DATABASE_PASSWORD. Without them the container silently fell back tojdbc:postgresql://localhost:5432/translatrand could never reach the DB.KEYCLOAK_USER,DB_VENDOR: h2,KEYCLOAK_IMPORT), and the realm-import mount pointed atdocker/keycloak-realm.json, which was renamed todocker/Translatr-realm.jsonyears ago in the basedocker-compose.ymlfix but never updated here. Migrated to the current config format (mirroring the base compose file).KC_HOSTNAME/KC_HOSTNAME_BACKCHANNEL_DYNAMIC. Without it, Keycloak's dynamic hostname provider derives and caches its issuer/endpoint URLs from whichever host reached it first after boot — if that happened to belocalhost:8080(e.g. a host-side health check), every subsequent discovery response keeps claiminglocalhost:8080, so translatr's own login flow ends up trying to reach Keycloak atlocalhost:8080from inside its own container and getsconnection refused(surfaces asGET /login/keycloakreturning 401 instead of redirecting).depends_on: condition: service_healthy(database→translatr→loadgenerator) so containers don't race each other on a cold start..gitignore: excluded thehistogram-quantilebinaryinit-clickhousedownloads into a bind-mounted repo path at container start — it was showing up as an untracked binary.App fixes (native-image only)
Found while browsing the running stack:
com.translatr.dto.ErrorResponselacked@RegisterForReflection. EveryExceptionMapperthat returns one (404/409/403) 500s in the native build instead — Jackson can't reflectively serialize it ("No serializer found ... this appears to be a native image, in which case you may need to configure reflection for the class"), so an ordinary 404 becomes a 500.GET /andGET /ui(no trailing slash) don't match a Quinoa static resource (the SPA is served at/ui/), so they fell through to that same 404→500. AddedRootResource/UiRedirectResource, prod-only (mirroring the existing dev/test-onlyUiLandingResource), redirecting both to/ui/.Verification
-v(wiping volumes) and brought it back up from the tracked files alone multiple times, both standalone and with thedocker-compose-signoz.ymloverlay — clean boot, no manual grants/patches needed./login/keycloaknow 302s correctly, with browser-facing URLs onlocalhost:8080and backchannel (token/jwks) URLs onsso:8080./→ 303 →/ui/,/ui→ 303 →/ui/, and that a genuine 404 still serializes its JSON body correctly.@RegisterForReflectionfix specifically for the native-image path. A native build was attempted but OOM'd after 31 minutes (Colima's ~7.75GB isn't enough headroom for GraalVM native-image compilation here) — the fix is the standard, well-documented Quarkus remedy for this exact error, but I couldn't do an end-to-end native round-trip in this environment.🤖 Generated with Claude Code