Repository navigation
test(migrations): run integration tests in parallel; harden shared test server - #1401
Merged
Merged
Conversation
A review of LeanerCloud#1400 found three issues in shared.go that bite harder once tests on the shared server run in parallel: - The migrated template was built under the first caller's context and its result, errors included, is cached for the whole binary. A first caller that was canceled or hit its deadline mid-build failed every later RequireMigratedDatabase call. The build now runs detached from the caller, like the container start, with its own 10-minute backstop. - The Docker health probe ran on every call. Under CI load a probe that times out would skip a test whose database is reachable, a #1597-style false green. It now runs only until the shared server is up. - dropDatabase read s.admin without the mutex while TerminateSharedPostgres sets it to nil, so a cleanup after Terminate could race or panic. It now holds the mutex and treats a stopped server as nothing left to drop. Each fix has a test that fails without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tests on the shared server are about to run in parallel. Each running test can hold a pool of up to 10 connections plus the migrator's own and a spare session, about 13, and the admin pool adds up to 10. At the default -parallel (GOMAXPROCS, 16 on a dev machine) that worst case is over 200, past PostgreSQL's default max_connections of 100. Raising the server's limit to 500 is the least invasive fix: every test's pool keeps its size, so no test that holds several connections at once can start waiting on its own pool. Dedicated containers from SetupPostgresContainer are unchanged; only the shared server gets the flag, through an unexported variant that takes extra container options. Measured peak in the migrations package: 50 connections at -parallel=16, 21 at -parallel=4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since LeanerCloud#1400 every integration test here gets its own database on the shared server, so most of them can run at once. The package was the slowest integration package on CI (190s), mostly tests walking the migration chain one after another. 62 of 69 top-level tests now call t.Parallel, and table subtests do too where each subtest creates its own database and the parent holds none. Subtests that share their parent's database stay sequential. Seven tests stay sequential, each with a one-line reason: - t.Setenv: TestMigrations_AutoHealDirty, and TestPGPasswordAndKeywordDSN_ConnectWithHostilePassword (which also keeps its dedicated server for ALTER ROLE) - swaps os.Stdout and the standard logger: TestAdminBootstrap_LogsToStderr_NotStdout - wall-clock deadline assertions: the four deadline and cancel tests in migrate_cancel_integration_test.go Advisory locks and pg_stat_activity checks in the parallel tests are scoped to the test's own database (advisory locks are per database; the pg_stat_activity queries filter by PID or current_database()), so other databases cannot affect them. Local wall time of the -race test binary: 252s sequentially on origin/main, 111s at -parallel=4, 81s at -parallel=16. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
IAmJSD
marked this pull request as ready for review
October 25, 2026 03:29
IAmJSD
added a commit
to IAmJSD/cloud-commitments-platform
that referenced
this pull request
Oct 25, 2026
Brings in LeanerCloud#1401 (parallel migrations tests) and gives the per-shard build caches a second, warm run. Co-Authored-By: Claude Opus 5.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.
Why
On main's CI run 40153816430 (after #1400 moved integration tests onto one shared Postgres server per package), the Integration Tests job took 531s. Its test step was 480s of wall time holding 856s of package time on a 4-vCPU runner.
internal/database/postgres/migrationswas the slowest package at 190s (152 tests), andTestMigrations_DownAllRoundTripalone took 21s. Most of the package's time went to tests walking the 127 migrations one after another. Since #1400 each test has its own database (RequireEmptyDatabase/RequireMigratedDatabase), so most of them can run at once.What changed
Three commits:
fix(testhelpers): hardens the shared test server for parallel callers. These are the three non-blocking findings from the review of test: share one Postgres server per package across integration tests #1400:RequireMigratedDatabasecall fail. The build now runs undercontext.WithoutCancelwith its own 10-minute backstop, the same waystart()already handles the container.dropDatabasereads.adminwithout the mutex whileTerminateSharedPostgressets it to nil. It now holds the mutex and treats a stopped server as nothing left to drop.shared_integration_test.go, one per fix. Each fails on the old code: a cached template error, a nil-pointer panic in cleanup, and a probe on a running server.test(testhelpers): raisesmax_connectionson the shared server to 500. In the worst case each running test holds about 13 connections (a pool of up to 10, the migrator's connection, and a spare session), and the admin pool adds up to 10 more. At-parallel=16that is over 200, past PostgreSQL's default of 100. I raised the server limit instead of shrinking the per-database pool because it is the less invasive change: every test's pool stays the same size, so no test that holds several connections can end up waiting on its own pool. Only the shared server gets the flag, through an unexportedsetupPostgresContainer(..., extra...).SetupPostgresContainerand dedicated containers are unchanged.test(migrations): runs the integration tests in parallel.t.Parallel().TestMigrations_AutoHealDirty:t.Setenv.TestPGPasswordAndKeywordDSN_ConnectWithHostilePassword:t.Setenv. It also keeps its dedicated server because ofALTER ROLE.TestAdminBootstrap_LogsToStderr_NotStdout: swapsos.Stdoutand the standard logger.migrate_cancel_integration_test.go: they assert wall-clock bounds.pg_stat_activityonly see their own database. Advisory locks are per database (I checked: the same key is lockable from two databases at once). Thepg_stat_activityqueries filter by PID orcurrent_database().captureStdoutandcaptureLogOutputnow say their callers must not be parallel.No assertion was changed or removed.
Verification
All runs used the
-race -tags integrationtest binary (go test -c -race -tags integration, then run from the package directory). The machine has 16 cores and was shared with other agents' container workloads (load average 7 to 17), so absolute times are noisy.-test.paralleldefault, nothing parallel)-test.parallel=4(CI-like)-test.parallel=16-test.shuffle=onx3 (seeds 1792896703861082493, 1792896816583106797, 1792896954462828728)-test.count=2SELECT count(*) FROM pg_stat_activityevery ~0.2s viadocker execon the shared container during each run.go test -race -tags integration -v ./internal/database/postgres/testhelpers/: all 16 tests pass, including the false-green guard tests and the three new ones.go test -race -shortfortesthelpersandmigrations: pass.scripts/test-git-secrets-allowlist.sh: clean.Not verified locally:
go test -p 4), so CPU contention will be higher than in my-parallel=4run.Expected effect
-parallel=4.TestMigrations_DownAllRoundTripplus the sequential tests (AutoHealDirty, which runs the full chain four times, the PGPASSWORD test with its own container, and a few short ones).Note for cristim
max_connections=500flag only applies to the shared server and doesn't change any pool size. If you'd rather cap pools instead (for example,MaxConnections4 for shared test databases), that's a one-line alternative, but it could make a test that holds several connections at once wait on its own pool.TestMigrations_AutoHealDirtystays sequential because oft.Setenv, and it is now one of the longer items on the critical path. A follow-up could start its subtests fromRequireMigratedDatabaseinstead of migrating an empty database four times. I left it out here to keep this PR to parallelism only.🤖 Generated with Claude Code