From 972e3e4fbd88ace9494b6a8c639119852770636b Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Wed, 5 Aug 2026 19:43:42 +0200 Subject: pgruntime: protect against concurrent migrations --- CHANGELOG.md | 6 ++++++ pgruntime/behavior.go | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77b3972..e7bfdf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ SPDX-FileCopyrightText: 2026 Stefan Majewsky SPDX-License-Identifier: Apache-2.0 --> +# v1.13.2 (TBD) + +Changes: + +- pgruntime: Add transaction locking to protect against concurrent migrations. + # v1.13.1 (2026-08-05) Changes: diff --git a/pgruntime/behavior.go b/pgruntime/behavior.go index 3ffff1e..03ff338 100644 --- a/pgruntime/behavior.go +++ b/pgruntime/behavior.go @@ -110,7 +110,22 @@ func applyMigrations(ctx context.Context, db gsql.ConnectionHandle, migrations m // apply migrations for _, version := range pendingVersions { err := db.GSQLTransact(ctx, func(tx gsql.Handle) error { - _, err := execQuery(ctx, db, migrations[version], nil) + // ensure that nobody else is migrating until we are done + var actualVersion int64 + err := queryRow(ctx, db, `SELECT version FROM schema_migrations FOR UPDATE`, nil, []any{&actualVersion}) + if err != nil { + return fmt.Errorf("could not obtain lock for schema migration: %w", err) + } + + // ensure that nobody else migrated just before we started our transaction block + if version <= actualVersion { + return fmt.Errorf("tried to perform migration to version %d, but schema is already at version %d (multiple migrations might be running concurrently)", + version, actualVersion, + ) + } + + // perform the next migration + _, err = execQuery(ctx, db, migrations[version], nil) if err != nil { return fmt.Errorf("could not execute schema migration: %w", err) } -- cgit v1.3.1