Skip to content

ci(java): split java lane into fast/slow + shared PG sidecar (~3× faster, crash-resistant) - #162

Merged
dmealing merged 1 commit into
mainfrom
ci/java-speedup
Jul 4, 2026
Merged

ci(java): split java lane into fast/slow + shared PG sidecar (~3× faster, crash-resistant)#162
dmealing merged 1 commit into
mainfrom
ci/java-speedup

Conversation

@dmealing

@dmealing dmealing commented Jul 4, 2026

Copy link
Copy Markdown
Member

Problem

The Java local-ci job took ~10m and intermittently died to runner shutdown/cancel ("received a shutdown signal / operation was canceled"). Two root causes: (1) it ran the full 14-module reactor + double-conformance + JaCoCo single-threaded, and (2) local-ci.yml never wired the shared Postgres sidecar, so java+kotlin integration booted ~30-40 serial Testcontainers Postgres — the main wall-clock sink and, contending on one Docker daemon with the other 3 ports, the crash vector.

Changes

  • Split java into two parallel jobs. java-fast (java+kotlin conformance — the correctness signal) and java-slow (full reactor + docker integration). A cancel/OOM mid-integration no longer kills the fast verdict. ci-local.sh grows --only java-fast|java-slow; --only java (and a plain local run) still runs both lanes in order, byte-equivalent to before.
  • Shared Postgres sidecar for every integration-running job (ts/python/csharp/java-slow). Each port already honors METAOBJECTS_TEST_PG_URL and CREATEs a per-scenario database. The sidecar publishes 5432 to a dynamic host port so concurrent jobs on the shared self-hosted host never collide on a fixed port.
  • Skip JaCoCo in the reactor (-Djacoco.skip=true) — its coverage gate is haltOnFailure=false, so it's pure instrumentation overhead for a pass/fail signal.
  • Deliberately not adding -T 1C: this reactor has a documented shared-render-jar race under parallel module builds (integration-test.sh).

Verified

Full workflow_dispatch run on this branch — all jobs green:

Job Before After
java (combined) ~9.8m java-fast 0m46s · java-slow 3m00s (parallel)
ts 5.3m 2m17s
python 2.6m 0m34s
csharp 2.0m 0m56s

Java correctness signal: 10m → <1m. Full Java lane: ~3× faster. Every port benefits from removing the shared-daemon container contention.

🤖 Generated with Claude Code

The Java local-ci job took ~10m and intermittently died to runner
shutdown/cancel. Two structural fixes:

- Split `java` into two parallel jobs: `java-fast` (java+kotlin
  conformance, ~2-3m — the correctness signal) and `java-slow` (full
  reactor + docker integration). A cancel/OOM mid-integration no longer
  kills the fast verdict, and the conformance signal surfaces quickly.
  ci-local.sh grows `--only java-fast|java-slow`; `--only java` (and a
  plain local run) still runs both lanes in order, byte-equivalent to
  before.
- Give every integration-running job (ts/python/csharp/java-slow) a
  shared Postgres SIDECAR instead of each scenario booting its own
  Testcontainers Postgres (java+kotlin alone booted ~30-40 serial
  containers — the main wall-clock sink and daemon-contention/crash
  vector). Each port already honors METAOBJECTS_TEST_PG_URL and CREATEs a
  per-scenario database. The sidecar publishes 5432 to a DYNAMIC host
  port so concurrent jobs on the shared self-hosted host never collide.
- Skip JaCoCo in the reactor (`-Djacoco.skip=true`): its coverage gate is
  haltOnFailure=false, so it never fails the build — pure instrumentation
  overhead for a pass/fail signal.

Deliberately NOT adding `-T 1C`: this reactor has a documented shared-
render-jar race under parallel module builds (integration-test.sh).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ew1XfYSbEAezxjs9opynAe
@dmealing
dmealing merged commit eed48d9 into main Jul 4, 2026
9 checks passed
@dmealing
dmealing deleted the ci/java-speedup branch July 4, 2026 13:00
dmealing added a commit that referenced this pull request Sep 8, 2026
The Postgres sidecar is the biggest wall-clock lever in this repo, and it existed
only in CI. `local-ci.yml` supplies METAOBJECTS_TEST_PG_URL from its `services:`
block, so a hand-run of scripts/ci-local.sh silently fell back to booting a
Testcontainers Postgres per scenario — the exact cost PR #162 removed in July.
Nothing warned; the run just looked slow, which is indistinguishable from the
suite being slow.

Measured on this box while verifying one cross-port change:

  python integration     6m54s  ->  11.4s   (110 passed)
  kotlin integration     part of a 28-minute --only java lane
                                ->  47.7s   (132 passed, 0 containers booted)

Whole-estate verification of a five-port change went from ~40 minutes to ~5.

`ensure_pg_sidecar` hooks the one choke point every integration lane already
passes through (run_integration_for, which gates on `docker info`). It starts —
or REUSES — a named container and exports the same URL shape CI uses, including
MIGRATE_TS_PG_URL, so migrate-ts's real-Postgres suites run locally instead of
self-skipping. Reuse is the point: cold ~6s, warm ~1s, so the second run pays
nothing. It also arms one Kotlin test that previously self-skipped with no
database (131 -> 132).

Three properties are deliberate, two of them learned from prior flakes:

  * If METAOBJECTS_TEST_PG_URL is already set this is a NO-OP, so CI is
    byte-identical and an explicit override still wins.
  * The host port is DYNAMIC (`-p 5432`, read back with `docker port`), never
    5432:5432 — this host already runs several Postgres containers and the
    self-hosted runners share it, so a fixed port collides.
  * Readiness is POLLED, and required twice, before the URL is exported.
    Postgres reports ready once during init and then restarts, and the original
    shared-sidecar mode's missing readiness gate was the dominant flake — the
    first connect to a transiently-slow sidecar hung to the test timeout.

Every failure path degrades to the old behaviour with a warning rather than
failing the run: docker refusing to start it, no published port, or readiness
not reached in 60s all fall back to per-scenario containers. Integration
results are unaffected either way. MO_CI_NO_PG_SIDECAR=1 opts out entirely.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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