Skip to content

fix(load-test): repair docker-compose-loadtest.yml stack and native-image 404 handling - #345

Merged
resamsel merged 1 commit into
mainfrom
fix/load-test-compose-and-native-redirects
Sep 17, 2026
Merged

resamsel merged 1 commit into
mainfrom
fix/load-test-compose-and-native-redirects

Conversation

@resamsel

Copy link
Copy Markdown
Owner

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

  • Postgres: unpinned postgres now resolves to v18, which breaks the direct /var/lib/postgresql/data mount this file uses. Pinned to postgres:16, added POSTGRES_DB/POSTGRES_USER defaults, and a healthcheck.
  • Postgres 15+ schema privileges: public schema CREATE is 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.
  • translatr's datasource env vars were wrong: the file set the Play-era DATABASE_URL/DATABASE_DRIVER, which the Quarkus app never reads — it reads JDBC_DATABASE_URL/DATABASE_USER/DATABASE_PASSWORD. Without them the container silently fell back to jdbc:postgresql://localhost:5432/translatr and could never reach the DB.
  • Keycloak: env vars/command were still on the pre-Quarkus (legacy WildFly) Keycloak config format the current image ignores (KEYCLOAK_USER, DB_VENDOR: h2, KEYCLOAK_IMPORT), and the realm-import mount pointed at docker/keycloak-realm.json, which was renamed to docker/Translatr-realm.json years ago in the base docker-compose.yml fix but never updated here. Migrated to the current config format (mirroring the base compose file).
  • Keycloak split-horizon hostname: added 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 be localhost:8080 (e.g. a host-side health check), every subsequent discovery response keeps claiming localhost:8080, so translatr's own login flow ends up trying to reach Keycloak at localhost:8080 from inside its own container and gets connection refused (surfaces as GET /login/keycloak returning 401 instead of redirecting).
  • Dependency ordering: added healthchecks and depends_on: condition: service_healthy (database → translatr → loadgenerator) so containers don't race each other on a cold start.
  • .gitignore: excluded the histogram-quantile binary init-clickhouse downloads 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.ErrorResponse lacked @RegisterForReflection. Every ExceptionMapper that 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 / and GET /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. Added RootResource/UiRedirectResource, prod-only (mirroring the existing dev/test-only UiLandingResource), redirecting both to /ui/.

Verification

  • Tore the stack down with -v (wiping volumes) and brought it back up from the tracked files alone multiple times, both standalone and with the docker-compose-signoz.yml overlay — clean boot, no manual grants/patches needed.
  • Confirmed /login/keycloak now 302s correctly, with browser-facing URLs on localhost:8080 and backchannel (token/jwks) URLs on sso:8080.
  • Built a local prod-profile fast-jar (JVM, not native) and confirmed / → 303 → /ui/, /ui → 303 → /ui/, and that a genuine 404 still serializes its JSON body correctly.
  • Not verified: the @RegisterForReflection fix 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

…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>
@resamsel
resamsel merged commit ea302b4 into main Sep 17, 2026
6 checks passed
@resamsel
resamsel deleted the fix/load-test-compose-and-native-redirects branch September 17, 2026 05:24
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.

1 participant