Skip to content

ci: speed up integration tests#3235

Open
dobrac wants to merge 8 commits into
mainfrom
jd/integration-test-speedup
Open

ci: speed up integration tests#3235
dobrac wants to merge 8 commits into
mainfrom
jd/integration-test-speedup

Conversation

@dobrac

@dobrac dobrac commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Why

PR wall time for integration tests was ~21.5 min, gated by the slowest job of a 3-config compression matrix where every job rebuilds everything and runs the full suite. Target: under 10 minutes.

Baseline (28977209000, zstd1 = long pole): Build Packages 2m26s, host init + template + services ~4m20s, tests 14m07s, total 21m24s.

Parallelism experiments on this PR showed each host saturates server-side (Firecracker boots + zstd compression) right at go test -parallel=4: at 8, per-test times inflated 70–140% for ~1 min net gain and 10 template builds flaked; at 12, envd inits starved (syncing took too long) and 87 tests failed. Client-side concurrency can't cut wall time — more hosts can.

What

Sharding for wall time (the main change)

  • PRs run the production-like zstd1 config split into four shards on parallel runners, sized by measured test time (TEST_SHARD in tests/integration/Makefile):
    • templates-builds-1 / templates-builds-2: the ^TestTemplateBuild* tests (real FC builds, ~1400s of summed test time that barely parallelizes on one host), split into balanced halves by name prefix — new tests fall into shard 2 automatically.
    • sandboxes: api/sandboxes + the light packages (proxies, metrics, volumes).
    • rest: everything else via go list (auto-picks-up new packages), including the non-build template tests.
  • Push to main keeps the full 3-config matrix running the whole suite per config (full-matrix input).

Setup cut from ~7 min to ~4.5 min per job

  • Package builds run concurrently instead of serially, with a dedicated salted Go cache (setup-go keys purely on dep files, so it kept restoring stale -N -l artifacts and recompiling everything; after merge, push-main runs will keep the salted cache warm for PRs).
  • ClickHouse migrations run goose directly on the host (migrate-host, pinned via go.mod tool directive) — building the migrator image cost ~2 min/job even with gha layer caching, for a container whose migrations are volume-mounted anyway.
  • postgres/clickhouse/redis start in the background right after checkout (new start-databases action), overlapping the Go build; start-services waits with bounded health loops (previously infinite) on pinned images.
  • -gcflags=all=\"-N -l\" is now opt-out (DEBUG_GCFLAGS, empty in CI): race detection stays on, optimizations stay on.

Less test work

  • New RequestTemplateWithoutBuild helper: 7 tests that only exercise template metadata/authz no longer build full FC templates (~60–160s each). Verified none of these API paths filter on build status.
  • envd process tests: fixed 10s/5s sleeps replaced with EventuallyWithT polling.

Learned the hard way

  • The template build cannot overlap a running orchestrator: create-build manages its own sandbox network state and the two conflict, wedging the provisioning VM (reverted after one red run).

Measured result (run 29041851323)

Shard Tests step Total job
zstd1-sandboxes 2m37s 5.7 min
zstd1-templates-1 3m20s 6.4 min
zstd1-templates-2 4m12s 6.8 min
zstd1-rest 4m01s 7.2 min

PR wall time: 21.4 min → 7.2 min (−66%), runner usage ~50 → ~26 min. Verified across the shard junits: all 219 top-level tests run exactly once (none missing, none duplicated), 0 failures.

Not changed (checked and rejected)

  • Raising -parallel (measurements above; knob kept for local/future tuning).
  • MAX_PARALLEL_MEMFILE_SNAPSHOTTING: dead config, nothing reads it.
  • host-init gsutil caching: the whole step is ~20s.
  • Tag-assignment tests keep real builds: POST /templates/tags requires a ready build.

Follow-up worth investigating

The compression path is the dominant remaining cost: the same suite runs 3x faster uncompressed than under zstd1 with compress_workers=8, and the host saturates at 4 concurrent FC operations. Likely production-relevant contention, worth profiling independently of CI.

PR wall time for integration tests is ~21 min; on a recent successful run
the zstd1 shard spent 2m26s building packages, ~4m on host init/template/
services, and 14m07s running tests. Several compounding causes:

- The full suite (incl. the whole build+services pipeline) ran 3x per PR
  in the compression matrix, though most tests don't depend on
  compression/dedup/memfd settings. PRs now run only the production-like
  zstd1 config via a new full-matrix workflow input; push to main keeps
  all three configs.
- go test ran with -parallel=4 while the harness allows 100 concurrent
  sandboxes; api/sandboxes alone is ~1250s of test time squeezed through
  4 slots. Parallelism is now tunable via TEST_PARALLELISM (4 locally,
  12 in CI).
- All debug builds used -race -gcflags=all="-N -l"; disabling
  optimizations slows compilation and every test at runtime (envd runs
  inside every sandbox). -N -l is now opt-out via DEBUG_GCFLAGS, empty
  in CI, and set at the job level so the rebuilds inside run/run-debug
  targets become Go build cache no-ops.
- postgres/clickhouse/redis started serially, each with an unbounded
  sleep-2 health loop, on unpinned images. They now start concurrently
  (overlapping the migrator image build), with bounded health waits and
  pinned tags.
- 7 template tests built full Firecracker templates (~60-160s each) only
  to exercise metadata/authz paths (visibility updates, cross-team 403/
  404, default-tag deletion guard) that don't depend on build state.
  They now use a new RequestTemplateWithoutBuild helper (single API
  call): the env row, waiting build, alias, and default tag assignment
  all exist at request time, and PATCH/list/delete don't filter on
  build status.
- envd process tests slept a fixed 10s/5s waiting for processes to
  start; they now poll the process list with EventuallyWithT.
@cla-bot cla-bot Bot added the cla-signed label Jul 8, 2026
@cursor

cursor Bot commented Jul 8, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Mostly CI and test-harness timing; the main functional risk is weaker preconditions in metadata-only template tests if any path implicitly required a ready build. The @$localhost connection string typo may break ClickHouse client config in .env.test if unchanged from before.

Overview
This PR cuts PR integration test wall time by running only the zstd1 config on four parallel shards instead of the full three-way compression matrix, while main keeps the full matrix via full-matrix. Setup overlaps more work: databases start early in a new action, Go packages build in parallel with a dedicated optimized build cache, ClickHouse migrations run on the host via migrate-host, and CI sets DEBUG_GCFLAGS="" for faster race-instrumented binaries. The integration Makefile adds test-shard / TEST_SHARD splitting (including two halves of TestTemplateBuild*), and several tests skip full Firecracker builds via RequestTemplateWithoutBuild or poll with EventuallyWithT instead of fixed sleeps.

In .github/actions/start-services, CLICKHOUSE_CONNECTION_STRING still uses @$localhost; in bash $localhost expands empty so the host in the URL can be wrong unless something else sets that variable.

Reviewed by Cursor Bugbot for commit 3715e06. Bugbot is set up for automated code reviews on this repo. Configure here.

echo "TESTS_SANDBOX_USER_ID=${TESTS_SANDBOX_USER_ID}" >> .env.test
set -x
make migrate
make -C tests/integration seed

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClickHouse killed between CI steps

High Severity

The ClickHouse server is started with a shell background job in the Start Database Containers step, but that step returns as soon as docker run for Postgres and Redis finishes. When the step’s shell exits, the background make -C packages/clickhouse run process is typically torn down, unlike the detached Postgres and Redis containers, so the later wait_healthy clickhouse step can time out or race against a missing container.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ea47dab. Configure here.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request optimizes CI and integration test performance by starting database containers concurrently, allowing compiler optimization overrides for debug builds, parameterizing test parallelism, replacing static sleeps with dynamic polling in process tests, and introducing a fast template creation helper that bypasses slow VM builds. The review feedback highlights two issues in the GitHub Actions workflow: a background Clickhouse process that will be prematurely terminated when its step ends, and a typo in the Clickhouse connection string where $localhost is used instead of localhost.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

set -x
make migrate
make -C tests/integration seed
make -C packages/clickhouse run &

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In GitHub Actions, the runner automatically terminates all background processes spawned during a step once that step completes (via its process tree killer). Since make -C packages/clickhouse run is executed in the background (&) in the Start Database Containers step, it will be killed before the Wait for Databases and Migrate step runs.

If the Makefile target runs Clickhouse in the foreground, it will be terminated immediately at the end of this step. If it runs Clickhouse in the background (e.g., via docker run -d), the & is redundant.

Recommendation:
To ensure Clickhouse persists across steps, run the container directly using docker run -d (similar to Postgres and Redis), or keep the startup command in the same step where it is waited on and migrated.

echo "CLICKHOUSE_PORT=${CLICKHOUSE_PORT}" >> .env.test
echo "CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD}" >> .env.test
echo "CLICKHOUSE_DATABASE=${CLICKHOUSE_DATABASE}" >> .env.test
echo "CLICKHOUSE_CONNECTION_STRING=clickhouse://${CLICKHOUSE_USERNAME}:${CLICKHOUSE_PASSWORD}@$localhost:${CLICKHOUSE_PORT}/${CLICKHOUSE_DATABASE}" >> .env.test

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The connection string contains @$localhost instead of @localhost. Since localhost is not a defined environment variable in this context, $localhost will expand to an empty string, resulting in an invalid or malformed connection string (clickhouse://e2b:clickity-clicky-click@:9000/default).

Please remove the $ prefix.

        echo "CLICKHOUSE_CONNECTION_STRING=clickhouse://${CLICKHOUSE_USERNAME}:${CLICKHOUSE_PASSWORD}@localhost:${CLICKHOUSE_PORT}/${CLICKHOUSE_DATABASE}" >> .env.test

@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
3259 2 3257 7
View the top 2 failed test(s) by shortest run time
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestCACertFromBuildSurvivesInBakedBundle
Stack Traces | 0s run time
=== RUN   TestCACertFromBuildSurvivesInBakedBundle
=== PAUSE TestCACertFromBuildSurvivesInBakedBundle
=== CONT  TestCACertFromBuildSurvivesInBakedBundle
--- FAIL: TestCACertFromBuildSurvivesInBakedBundle (0.00s)
Executing command cat in sandbox iu6ijy5d301m5mgt2iwmj (user: root)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestCACertFromBuildSurvivesInBakedBundle/added_in_RUN_step_without_update-ca-certificates
Stack Traces | 158s run time
=== RUN   TestCACertFromBuildSurvivesInBakedBundle/added_in_RUN_step_without_update-ca-certificates
=== PAUSE TestCACertFromBuildSurvivesInBakedBundle/added_in_RUN_step_without_update-ca-certificates
=== CONT  TestCACertFromBuildSurvivesInBakedBundle/added_in_RUN_step_without_update-ca-certificates
    template.go:43: test-fs-only-ca-buildstep: [info] Building template 2i86swt07eotm1s0b6rq/6accbc86-93ca-4a2d-a870-019e1eecce31
    template.go:43: test-fs-only-ca-buildstep: [info] [base] FROM ubuntu:22.04 [f9f564014e009a9561a82bf8c84f9314242971e833fb019936654ecba452f184]
    template.go:43: test-fs-only-ca-buildstep: [info] Base Docker image size: 30 MB
    template.go:43: test-fs-only-ca-buildstep: [info] Creating file system and pulling Docker image
    template.go:43: test-fs-only-ca-buildstep: [info] Uncompressing layer sha256:d6834b4a794c03efa2c998853e64969fa8851b11b2ade63292268872a37759d0 30 MB
    template.go:43: test-fs-only-ca-buildstep: [info] Uncompressing layer sha256:1529f1b9ee25fdd944d841121a7281a2f51b1410b1bacd91a23c167c194f218e 14 MB
    template.go:43: test-fs-only-ca-buildstep: [info] Uncompressing layer sha256:8c4b1b28875140ed3abacaf16ad0d696f6bef912f52d2148f261a23e3349465b 168 B
    template.go:43: test-fs-only-ca-buildstep: [info] Layers extracted
    template.go:43: test-fs-only-ca-buildstep: [info] Root filesystem structure: bin, boot, dev, etc, home, lib, lib32, lib64, libx32, media, mnt, opt, proc, root, run, sbin, srv, sys, tmp, usr, var
    template.go:43: test-fs-only-ca-buildstep: [info] Provisioning sandbox template
    template.go:43: test-fs-only-ca-buildstep: [info] Provisioning was successful, cleaning up
    template.go:43: test-fs-only-ca-buildstep: [info] Sandbox template provisioned
    template.go:43: test-fs-only-ca-buildstep: [info] [base] DEFAULT USER user [49e586c2171254c6bc4a09e84eedac32dbcf113a158c24248129af2f49cbed74]
    template.go:43: test-fs-only-ca-buildstep: [info] [builder 1/1] RUN echo 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJjVENDQVJlZ0F3SUJBZ0lCQVRBS0JnZ3Foa2pPUFFRREFqQWdNUjR3SEFZRFZRUURFeFZsTW1JdGRHVnoKZEMxallTMWlkV2xzWkhOMFpYQXdIaGNOTWpZd056QTVNVGcxTURNMVdoY05Nall3TnpBNU1UazFNVE0xV2pBZwpNUjR3SEFZRFZRUURFeFZsTW1JdGRHVnpkQzFqWVMxaWRXbHNaSE4wWlhBd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxCmhrak9QUU1CQndOQ0FBUUFzYlUyemt0R0xuMmpEOUx6ZXQzRWY1c09BaTM3OVg2eHQ2Rm9qU3hLSHlxQkMzb04KeHRGenBMcGZreTVRcmNpSGRoekQrOEVsT0tPWExlV0JBZVZGbzBJd1FEQU9CZ05WSFE4QkFmOEVCQU1DQWdRdwpEd1lEVlIwVEFRSC9CQVV3QXdFQi96QWRCZ05WSFE0RUZnUVVNbVE5aEtPMmhWMHR5aHhiZDNtQVR2U0JheGN3CkNnWUlLb1pJemowRUF3SURTQUF3UlFJZ2VycXhNaGp4YnM0TFZvbzhuTS9GZWMwV3dRSmpyMDg5T0YvOWxRUjUKb1VvQ0lRREFROTZ5OWxmN0tVaFJiWjZFVXJpSGxVaFlQR2xuaVBNSVZLdmpaaWZmRUE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==' | base64 -d > .../share/ca-certificates/e2b-buildstep-ca.crt root [f5401dc5b270c85bd980730b7a1de81987440103a5da127570f3bfffddec54ca]
    template.go:43: test-fs-only-ca-buildstep: [info] [finalize] Finalizing template build [feb9d6a0a7653cd36294a5f636a59ad06bb9a171fde980f9088d5b49c3597922]
    template.go:43: test-fs-only-ca-buildstep: [info] [optimize] Optimizing template [9d2aab3b36955f716cd537baeb2977217ee4d1ffb4be9fa32fd03113f439a44a]
    template.go:43: test-fs-only-ca-buildstep: [info] Build finished, took 2m36s
    ca_cert_build_test.go:123: Build completed successfully
Executing command tar in sandbox i7s47mk99zwvr96rrktdy (user: root)
    ca_cert_build_test.go:132: Command [tar] output: event:{start:{pid:1322}}
    ca_cert_build_test.go:132: 
        	Error Trace:	.../tests/envd/ca_cert_build_test.go:40
        	            				.../tests/envd/ca_cert_build_test.go:132
        	Error:      	Received unexpected error:
        	            	failed to execute command tar in sandbox i7s47mk99zwvr96rrktdy: invalid_argument: protocol error: incomplete envelope: unexpected EOF
        	Test:       	TestCACertFromBuildSurvivesInBakedBundle/added_in_RUN_step_without_update-ca-certificates
        	Messages:   	extract ca-certificates.crt from baked tar
--- FAIL: TestCACertFromBuildSurvivesInBakedBundle/added_in_RUN_step_without_update-ca-certificates (157.75s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea47dab30b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


reqEditors = append(reqEditors, setup.WithTestsUserAgent())

return requestTemplateBuild(tb, name, nil, nil, nil, reqEditors...)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clean up metadata-only template builds

When these tests call RequestTemplateWithoutBuild, requestTemplateBuild still goes through RegisterBuild, which inserts an active_template_builds row for a pending build; because this helper never starts, finishes, cancels, or deletes that build, the row remains until the 40-minute sync timeout and is counted by GetInProgressTemplateBuildsByTeam. In integration runs that create real templates afterward—now with TEST_PARALLELISM=12—these leaked pending builds reduce the team build-concurrency headroom and can make later template requests fail with the max-concurrent-builds 429 even though no actual build is running.

Useful? React with 👍 / 👎.

echo "TESTS_SANDBOX_USER_ID=${TESTS_SANDBOX_USER_ID}" >> .env.test
echo "REDIS_URL=localhost:6379" >> .env.test
echo "CLICKHOUSE_MIGRATOR_IMAGE=clickhouse-migrator" >> .env.test
echo "CLICKHOUSE_USERNAME=${CLICKHOUSE_USERNAME}" >> .env.test

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟣 Pre-existing typo on line 118: @$localhost: references an unset shell variable that expands to empty, so .env.test gets CLICKHOUSE_CONNECTION_STRING=clickhouse://e2b:...@:9000/default (empty host). This PR only relocates the line — since it's already being touched, dropping the $ while it's being moved would be a cheap drive-by fix (compare the neighboring REDIS_URL=localhost:6379 line, which has no dollar sign).

Extended reasoning...

What the bug is

Line 118 of .github/actions/start-services/action.yml writes:

echo "CLICKHOUSE_CONNECTION_STRING=clickhouse://${CLICKHOUSE_USERNAME}:${CLICKHOUSE_PASSWORD}@$localhost:${CLICKHOUSE_PORT}/${CLICKHOUSE_DATABASE}" >> .env.test

The fragment @$localhost: contains a bare $localhost — a bash parameter expansion referencing a variable that is never assigned anywhere in the composite action, the calling workflow, or any sourced file. Inside double quotes without set -u, bash silently expands an undefined variable to the empty string. Compare the neighboring REDIS_URL=localhost:6379 line, which is a literal localhost (no $) — that spelling is clearly the intent here as well.

Step-by-step proof

  1. Step env sets CLICKHOUSE_USERNAME=e2b, CLICKHOUSE_PASSWORD=clickity-clicky-click, CLICKHOUSE_PORT=9000, CLICKHOUSE_DATABASE=default. localhost is not in that env block, nor exported by any earlier step.
  2. Bash performs parameter expansion inside the double-quoted string.
  3. ${CLICKHOUSE_USERNAME}e2b, ${CLICKHOUSE_PASSWORD}clickity-clicky-click, $localhost → `` (empty), ${CLICKHOUSE_PORT} → `9000`, `${CLICKHOUSE_DATABASE}` → `default`.
  4. The literal line appended to .env.test is: CLICKHOUSE_CONNECTION_STRING=clickhouse://e2b:clickity-clicky-click@:9000/default — note the @: with no host between them.

Why existing code doesn't prevent it\n\nThe surrounding run: block doesn't use set -u, so undefined-variable references are non-fatal. Tests still pass because clickhouse-go/v2's DSN parser tolerates an empty host and defaults it to localhost — so runtime impact is masked. A stricter URL parser (or a different ClickHouse client) would reject the URL outright.\n\nImpact\n\nMinimal today, since integration tests are green with this typo. The risk is latent: swapping the ClickHouse driver, adding a URL-validation step, or reusing this env file in another tool would surface the broken host silently. Marked as pre_existing because the typo lives in commit 8f83959 (before this PR); this PR only relocates the line verbatim from the removed Run Clickhouse step to the new Wait for Databases and Migrate step without altering it.\n\nHow to fix\n\nDrop the $ on line 118:\n\nbash\necho "CLICKHOUSE_CONNECTION_STRING=clickhouse://${CLICKHOUSE_USERNAME}:${CLICKHOUSE_PASSWORD}@localhost:${CLICKHOUSE_PORT}/${CLICKHOUSE_DATABASE}" >> .env.test\n\n\nSingle-character edit, no behavior change (empty host already resolves to localhost in the client), and this PR is already touching the line — a natural drive-by cleanup.

dobrac added 2 commits July 8, 2026 16:52
At -parallel=12 the zstd1 shard failed 87 tests: concurrent Firecracker
template builds (each with 8 zstd frame-encode workers) starved envd
inits inside freshly booted sandboxes, surfacing as 'failed to init
envd: syncing took too long' (44 occurrences in the orchestrator log,
no OOM/data races).
Measured on the zstd1 shard: at -parallel=8 per-test times inflated
70-140% (api/templates 3232s vs 1898s, api/sandboxes 2994s vs 1248s of
summed test time) for a net wall-clock win of ~1 min, with 10 template
build flakes rescued by --rerun-fails; at 12, envd inits starved into
'syncing took too long' across 87 tests. The host saturates server-side
(FC boots + zstd compression), so client concurrency beyond 4 only adds
contention. The TEST_PARALLELISM knob stays for local runs and future
tuning.
@dobrac

dobrac commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Parallelism tuning results from this PR's CI runs (zstd1 shard):

TEST_PARALLELISM Result Test step Notes
4 (baseline, main) pass 14m07s
8 pass 13m11s per-test times +70-140% (summed test time 4166s -> 7595s), 10 template-build flakes rescued by --rerun-fails=1
12 87 failures 9m17s envd init starvation: failed to init envd: syncing took too long x44 in orchestrator log

The host saturates server-side (FC VM boots + zstd compression with 8 workers per operation), so raising client-side -parallel past 4 only adds contention. Kept CI at 4; the TEST_PARALLELISM knob remains for local runs and future tuning if the compression path gets cheaper.

This strengthens the follow-up note: the compression path is the dominant cost (uncompressed shard runs the same suite in 4m51s vs 14m07s for zstd1).

dobrac added 3 commits July 8, 2026 17:28
Each CI host saturates at go test -parallel=4 (Firecracker boots + zstd
compression), so client-side concurrency can't cut wall time - more
hosts can. PRs now split the zstd1 config into three package shards on
parallel runners: api/templates (~1900s of summed test time) and
api/sandboxes (~1250s) each get a dedicated runner, everything else
shares the third. Shard resolution lives in tests/integration/Makefile
(TEST_SHARD=templates|sandboxes|rest|all); the rest shard is computed
via go list so new packages are picked up automatically. Push to main
still runs the full suite per compression config.

Also start the postgres/clickhouse/redis containers in the background
right after checkout (new start-databases action), so their image pulls
and boots overlap with the ~3 min package build instead of serializing
in front of the migrations.
The previous long pole (zstd1-templates, 14.7 min) was ~7 min of setup
plus ~7 min of tests. Both halves shrink:

- Split api/templates across two runners by test-name prefix
  (^TestTemplateBuild = 417s vs the rest = 460s of measured test time),
  since each host saturates at -parallel=4.
- Build packages concurrently instead of serially (2m48s -> bounded by
  the slowest binary) and give the integration jobs their own salted Go
  cache: setup-go keys purely on dependency files, so it kept restoring
  stale -N -l artifacts and forced full recompiles every run.
- Stop building the clickhouse migrator image (1m54s per job even with
  gha layer caching): its only job is running goose against migrations
  that get volume-mounted anyway, so run goose directly on the host via
  a new migrate-host target (pinned by go.mod tool directive).
- Run the sandbox template build (~40s) in the background overlapping
  service startup; tests gate on a wait step that surfaces the log and
  exit status.
The background overlap wedged every job: create-build manages its own
sandbox network state (slot pools, namespaces) and conflicts with the
orchestrator starting up concurrently, so the provisioning VM never
came ready and hit its 5-minute deadline. The overlap only saved ~40s;
run it synchronously before Start Services as before.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8311d9280

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

--health-interval=5s \
--health-timeout=2s \
--health-retries=5 \
postgres:18 &

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wait for backgrounded database launches

In the integration job running on the self-hosted infra-tests runner, this docker run is now backgrounded and its exit status is never observed. If a cancelled or failed prior job leaves a healthy container named postgres (similarly redis below), the new launch fails with a name conflict but the step still succeeds; the later wait_healthy postgres only checks by name and can accept the stale database, so migrations/seeds/tests run against leftover state instead of a fresh container. Before this change the foreground docker run failure stopped the job immediately.

Useful? React with 👍 / 👎.

dobrac added 2 commits July 9, 2026 11:29
The templates-builds shard was 11.6 min: its 18 tests are all real FC
builds (~1400s of summed test+subtest time) that barely parallelize on
one host. Split them in two balanced halves by name prefix
(^TestTemplateBuild(ENV|COPY|RUN|W) = ~680s vs the rest = ~740s) and
fold the non-build template tests into the rest shard (separate
packages run concurrently within one gotestsum invocation; the -skip
only affects the templates package since no other package has
TestTemplateBuild* tests). Still four runners total.
The rest shard came in at 9.9 min - too close to the 10-minute budget.
Move proxies, api/metrics, and api/volumes from rest (261s of tests) to
the sandboxes runner (160s) to even the two out at roughly 210s each.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3715e06. Configure here.


wait_healthy postgres 120
wait_healthy clickhouse 120
wait_healthy redis 60

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Health wait ignores timeout failure

Medium Severity

The new wait_healthy helper returns a non-zero status when a database container does not become healthy in time, but the step never enables set -e or checks those return codes. After a timeout the job still runs migrations and seeds against containers that may not be running.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3715e06. Configure here.

@dobrac

dobrac commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Final result after the sharding + setup work: PR wall time 21.4 min → 7.2 min, under the 10-minute budget with ~3 min of headroom.

Shard Tests step Total job
zstd1-sandboxes 2m37s 5.7 min
zstd1-templates-1 3m20s 6.4 min
zstd1-templates-2 4m12s 6.8 min
zstd1-rest 4m01s 7.2 min

Coverage verified from the shard junits: all 219 top-level tests run exactly once across the four shards (none missing, none duplicated), 0 failures, no rerun-rescues. Steady-state should improve slightly further once push-main runs keep the salted Go cache warm for PR branches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant