Skip to content

TOC/TOU bug on transaction rollback #2551

Description

@zepatrik

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

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

...

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions