TOC/TOU bug on transaction rollback
- Dominant language
- Go
- Stars
- 14.3k
- Forks
- 1.1k
- Avg merge
- 6d 9h
- Merged PRs (30d)
- 11
Description
**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:
https://github.com/jackc/pgx/blob/c00cd65970e579cb722fc65fe2508e4cd1e4cb8f/tx.go#L210-L215
**To Reproduce**
It is a hard to reproduce issue without lots of setup, this is what is happening roughly:
```go
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**
...
Contributor guide
Research direction
Start with tx.go lines 210-215 and compare the close-flag handling with the referenced database/sql implementation around sql.go line 2331. Use the provided TestRollbackOnContextCanceled scenario to investigate the race; done means rollback behavior is consistent and the regression is covered by a test.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, postgresql
- Domain
- database
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100