-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Keep connections alive after BEGIN statement for non-FATAL errors #2589
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
53a744e
0ef4e7f
ad6e41a
09081aa
7a3afc8
eb929e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1066,6 +1066,35 @@ func TestConnReleaseWhenBeginFail(t *testing.T) { | |
| assert.NoError(t, err) | ||
| } | ||
|
|
||
| assert.EqualValues(t, 1, db.Stat().TotalConns()) | ||
|
|
||
| var n int | ||
| require.NoError(t, db.QueryRow(ctx, "select 1").Scan(&n)) | ||
| require.EqualValues(t, 1, n) | ||
| } | ||
|
|
||
| func TestConnDestroyedWhenBeginFailsFatally(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) | ||
| defer cancel() | ||
|
|
||
| controllerConn, err := pgx.Connect(ctx, os.Getenv("PGX_TEST_DATABASE")) | ||
| require.NoError(t, err) | ||
| defer controllerConn.Close(ctx) | ||
| pgxtest.SkipCockroachDB(t, controllerConn, "Server does not support pg_terminate_backend() (https://github.com/cockroachdb/cockroach/issues/35897)") | ||
|
|
||
| db, err := pgxpool.New(ctx, os.Getenv("PGX_TEST_DATABASE")) | ||
| require.NoError(t, err) | ||
| defer db.Close() | ||
|
|
||
| tx, err := db.BeginTx(ctx, pgx.TxOptions{BeginQuery: "select pg_terminate_backend(pg_backend_pid())"}) | ||
| assert.Error(t, err) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why isn't this a |
||
| if !assert.Zero(t, tx) { | ||
| err := tx.Rollback(ctx) | ||
| assert.NoError(t, err) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| for range 1000 { | ||
| if db.Stat().TotalConns() == 0 { | ||
| break | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -100,9 +100,12 @@ func (c *Conn) Begin(ctx context.Context) (Tx, error) { | |
| func (c *Conn) BeginTx(ctx context.Context, txOptions TxOptions) (Tx, error) { | ||
| _, err := c.Exec(ctx, txOptions.beginSQL()) | ||
| if err != nil { | ||
| // begin should never fail unless there is an underlying connection issue or | ||
| // a context timeout. In either case, the connection is possibly broken. | ||
| c.die() | ||
| // begin kills the connection upon receiving fatal, panic or non PGError errors, | ||
| // but does not otherwise as the connection should be reusable. | ||
| var pgErr *pgconn.PgError | ||
| if !errors.As(err, &pgErr) || isConnectionFatal(pgErr) { | ||
| c.die() | ||
| } | ||
| return nil, err | ||
| } | ||
|
|
||
|
|
@@ -112,6 +115,14 @@ func (c *Conn) BeginTx(ctx context.Context, txOptions TxOptions) (Tx, error) { | |
| }, nil | ||
| } | ||
|
|
||
| func isConnectionFatal(pgErr *pgconn.PgError) bool { | ||
| severity := pgErr.SeverityUnlocalized | ||
| if severity == "" { | ||
| severity = pgErr.Severity | ||
| } | ||
| return severity == "FATAL" || severity == "PANIC" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably unrelated to this PR, but I wish these magic strings were constants that could be traced through the source.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, I don't mind doing a follow up for that, but should take part in a different PR. |
||
| } | ||
|
|
||
| // Tx represents a database transaction. | ||
| // | ||
| // Tx is an interface instead of a struct to enable connection pools to be implemented without relying on internal pgx | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why isn't this a
requre?