Describe the bug
I have observed flaky behavior in a test that causes a transaction to be rolled back by canceling the context. I think it is caused by a toctou issue here:
|
if tx.closed { |
|
return ErrTxClosed |
|
} |
|
|
|
_, err := tx.conn.Exec(ctx, "rollback") |
|
tx.closed = true |
To Reproduce
It is a hard to reproduce issue without lots of setup, this is what is happening roughly:
func TestRollbackOnContextCanceled(t *testing.T) {
dsn := dockertest.RunTestPostgreSQL(t) // or simply load from the env
db, err := sql.Open("pgx/v5", dsn)
require.NoError(t, err)
t.Cleanup(func() { _ = db.Close() })
require.EventuallyWithT(t, func(collect *assert.CollectT) {
assert.NoError(collect, db.PingContext(t.Context()))
}, 10*time.Second, 100*time.Millisecond)
ctx, cancel := context.WithCancel(t.Context())
t.Cleanup(cancel)
tx, err := db.BeginTx(ctx, nil)
require.NoError(t, err)
_, err = tx.ExecContext(ctx, "SELECT 1")
require.NoError(t, err)
cancel()
time.Sleep(1 * time.Nanosecond) // give the database/sql auto-rollback some time to kick in
// this races now with the database/sql auto-rollback on context cancellation
// https://github.com/golang/go/blob/07840ceeed4afd10324a552e8c87a8ee363aa24a/src/database/sql/sql.go#L2211-L2226
// depending on the outcome, we either get sql.ErrTxDone or "conn closed"
require.NoError(t, tx.Rollback())
}
Expected behavior
The close bool flag should be managed atomically, similar to how it is done in the database/sql package: https://github.com/golang/go/blob/07840ceeed4afd10324a552e8c87a8ee363aa24a/src/database/sql/sql.go#L2331
Actual behavior
Flaky behavior.
Version
- Go:
$ go version -> go version go1.26.2 darwin/arm64
- PostgreSQL:
$ psql --no-psqlrc --tuples-only -c 'select version()' -> PostgreSQL 16.13 (Debian 16.13-1.pgdg13+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
- pgx:
$ grep 'github.com/jackc/pgx/v[0-9]' go.mod -> v5.9.2
Additional context
...
Describe the bug
I have observed flaky behavior in a test that causes a transaction to be rolled back by canceling the context. I think it is caused by a toctou issue here:
pgx/tx.go
Lines 210 to 215 in c00cd65
To Reproduce
It is a hard to reproduce issue without lots of setup, this is what is happening roughly:
Expected behavior
The close bool flag should be managed atomically, similar to how it is done in the
database/sqlpackage: https://github.com/golang/go/blob/07840ceeed4afd10324a552e8c87a8ee363aa24a/src/database/sql/sql.go#L2331Actual behavior
Flaky behavior.
Version
$ go version-> go version go1.26.2 darwin/arm64$ psql --no-psqlrc --tuples-only -c 'select version()'-> PostgreSQL 16.13 (Debian 16.13-1.pgdg13+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit$ grep 'github.com/jackc/pgx/v[0-9]' go.mod-> v5.9.2Additional context
...