mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 11:48:42 +02:00
* test(migrate): add concurrent migration race test using real Postgres (MUL-2956) Follow-up to MUL-2923 / #3658, which added a Postgres advisory lock to serialize the migration loop across concurrent runners (multi-replica backend startup, scale-up, manual `migrate up` overlap). That PR shipped without a test because cmd/migrate/ had no harness; this commit adds it. Refactor: extract runMigrations(ctx, pool, runOptions) from main(), with the lock key, the bookkeeping table, and the file list now injectable. main() behavior is unchanged. Identifier interpolation goes through pgx.Identifier{}.Sanitize so callers can pass "schema.schema_migrations" safely. Tests (cmd/migrate/migrate_concurrent_test.go) — every case isolates itself in a unique throwaway schema and a unique lock key, so they never touch the real schema_migrations table or block real production runners that share the database. Skip cleanly when DATABASE_URL is unreachable, matching the pattern already used in internal/handler/handler_test.go and internal/metrics/business_sampler_pgsleep_test.go. - TestRunMigrationsConcurrentPending: 16 goroutines apply 5 deliberately non-idempotent migrations (bare CREATE TABLE + ALTER TABLE ADD COLUMN). Without the lock, concurrent CREATE TABLE races trip "duplicate key value violates unique constraint pg_type_typname_nsp_index" — proving the lock is doing its job. - TestRunMigrationsConcurrentAlreadyApplied: 16 goroutines hit the EXISTS no-op path against a pre-populated bookkeeping table; the state must be unchanged. - TestRunMigrationsAdvisoryLockSerializes: an external connection holds the same advisory lock; we assert that zero of the 16 runners complete during a 1 s observation window, then release the side lock and let them all finish. Catches the original MUL-2923 bug where the lock got attached to a random pooled connection. - TestRunMigrationsConcurrentMixedPoolStress: same pending case but with a deliberately small pool (runners/2), forcing pgxpool.Acquire contention to overlap with pg_advisory_lock contention. Verified locally: `go test -race -count=10 ./cmd/migrate/` passes in ~15 s. Mutation test (lock acquire/release replaced with `SELECT 1`) confirms the pending and lock-serializes tests both fail loudly, catching the regression they were written to detect. go.mod tidy promotes golang.org/x/sync to a direct dependency (now imported by the test for errgroup) and incidentally fixes a stale `// indirect` annotation on prometheus/client_model, which is already imported directly by internal/metrics/testutil.go. Co-authored-by: multica-agent <github@multica.ai> * test(migrate): gofmt + address review nits (MUL-2956) - gofmt -w cmd/migrate/migrate_concurrent_test.go: fixture struct field alignment. - quoteQualifiedIdentifier: actually reject identifiers with more than one dot (the previous version split on the first dot only and would silently sanitize "a.b.c" into "a"."b.c", contradicting the comment). Inline the splitter via strings.Split now that we explicitly check the component count. - Soften the test's lock-key comment from "never collide" to the accurate probabilistic statement (~1 in 2^62 collision odds with the production constant). go test -race -count=10 ./cmd/migrate/ still passes (~15 s). Co-authored-by: multica-agent <github@multica.ai> * test(migrate): direction whitelist + tidy go.mod (MUL-2956) Address two follow-ups from review: - runMigrations now whitelist-checks opts.Direction up-front and returns an error for anything that is not "up" or "down". The previous shape relied on `opts.Direction == "up"` and an else branch, so a typo or empty string would silently fall through to the rollback path. Add TestRunMigrationsRejectsInvalidDirection covering the empty string, "UP"/"DOWN" case mismatches, "rollback", and a whitespace-padded value; the check fires before any pool work, so the test runs without Postgres. - go mod tidy: promotes google.golang.org/protobuf to a direct dependency (it is imported directly elsewhere in the module and was stale-marked indirect). go test -race -count=10 ./cmd/migrate/ green (~15.7 s, 50/50). Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: wei-heshang <wei-heshang@multica.ai> Co-authored-by: multica-agent <github@multica.ai>
66 lines
2.8 KiB
Modula-2
66 lines
2.8 KiB
Modula-2
module github.com/multica-ai/multica/server
|
|
|
|
go 1.26.1
|
|
|
|
require (
|
|
github.com/aws/aws-sdk-go-v2 v1.41.5
|
|
github.com/aws/aws-sdk-go-v2/config v1.32.13
|
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.13
|
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3
|
|
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.41.5
|
|
github.com/go-chi/chi/v5 v5.2.5
|
|
github.com/go-chi/cors v1.2.2
|
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
|
github.com/google/uuid v1.6.0
|
|
github.com/gorilla/websocket v1.5.3
|
|
github.com/jackc/pgx/v5 v5.9.2
|
|
github.com/lmittmann/tint v1.1.3
|
|
github.com/mattn/go-shellwords v1.0.13
|
|
github.com/oklog/ulid/v2 v2.1.1
|
|
github.com/pelletier/go-toml/v2 v2.3.0
|
|
github.com/prometheus/client_golang v1.23.2
|
|
github.com/prometheus/client_model v0.6.2
|
|
github.com/redis/go-redis/v9 v9.18.0
|
|
github.com/resend/resend-go/v2 v2.28.0
|
|
github.com/robfig/cron/v3 v3.0.1
|
|
github.com/spf13/cobra v1.10.2
|
|
golang.org/x/sync v0.20.0
|
|
google.golang.org/protobuf v1.36.8
|
|
gopkg.in/yaml.v3 v3.0.1
|
|
)
|
|
|
|
require (
|
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
|
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
|
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
|
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
|
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
|
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
|
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
|
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
|
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
|
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
|
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect
|
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.14 // indirect
|
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 // indirect
|
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
|
|
github.com/aws/smithy-go v1.24.2 // indirect
|
|
github.com/beorn7/perks v1.0.1 // indirect
|
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
|
github.com/kr/text v0.2.0 // indirect
|
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
|
github.com/prometheus/common v0.66.1 // indirect
|
|
github.com/prometheus/procfs v0.16.1 // indirect
|
|
github.com/spf13/pflag v1.0.9 // indirect
|
|
go.uber.org/atomic v1.11.0 // indirect
|
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
|
golang.org/x/sys v0.35.0 // indirect
|
|
golang.org/x/text v0.35.0 // indirect
|
|
)
|