mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
database/sql: on Tx rollback, retain connection if driver can reset session
Previously the Tx would drop the connection after rolling back from a context cancel. Now if the driver can reset the session, keep the connection. Change-Id: Ie6a3124275632787629844d91a06bb2e70cc060b Reviewed-on: https://go-review.googlesource.com/c/go/+/216241 Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com> Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
d8f0a229b5
commit
c9af5523f3
2 changed files with 50 additions and 7 deletions
|
|
@ -1746,7 +1746,11 @@ func (db *DB) begin(ctx context.Context, opts *TxOptions, strategy connReuseStra
|
||||||
// beginDC starts a transaction. The provided dc must be valid and ready to use.
|
// beginDC starts a transaction. The provided dc must be valid and ready to use.
|
||||||
func (db *DB) beginDC(ctx context.Context, dc *driverConn, release func(error), opts *TxOptions) (tx *Tx, err error) {
|
func (db *DB) beginDC(ctx context.Context, dc *driverConn, release func(error), opts *TxOptions) (tx *Tx, err error) {
|
||||||
var txi driver.Tx
|
var txi driver.Tx
|
||||||
|
keepConnOnRollback := false
|
||||||
withLock(dc, func() {
|
withLock(dc, func() {
|
||||||
|
_, hasSessionResetter := dc.ci.(driver.SessionResetter)
|
||||||
|
_, hasConnectionValidator := dc.ci.(driver.Validator)
|
||||||
|
keepConnOnRollback = hasSessionResetter && hasConnectionValidator
|
||||||
txi, err = ctxDriverBegin(ctx, opts, dc.ci)
|
txi, err = ctxDriverBegin(ctx, opts, dc.ci)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1758,12 +1762,13 @@ func (db *DB) beginDC(ctx context.Context, dc *driverConn, release func(error),
|
||||||
// The cancel function in Tx will be called after done is set to true.
|
// The cancel function in Tx will be called after done is set to true.
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
tx = &Tx{
|
tx = &Tx{
|
||||||
db: db,
|
db: db,
|
||||||
dc: dc,
|
dc: dc,
|
||||||
releaseConn: release,
|
releaseConn: release,
|
||||||
txi: txi,
|
txi: txi,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
ctx: ctx,
|
keepConnOnRollback: keepConnOnRollback,
|
||||||
|
ctx: ctx,
|
||||||
}
|
}
|
||||||
go tx.awaitDone()
|
go tx.awaitDone()
|
||||||
return tx, nil
|
return tx, nil
|
||||||
|
|
@ -2025,6 +2030,11 @@ type Tx struct {
|
||||||
// Use atomic operations on value when checking value.
|
// Use atomic operations on value when checking value.
|
||||||
done int32
|
done int32
|
||||||
|
|
||||||
|
// keepConnOnRollback is true if the driver knows
|
||||||
|
// how to reset the connection's session and if need be discard
|
||||||
|
// the connection.
|
||||||
|
keepConnOnRollback bool
|
||||||
|
|
||||||
// All Stmts prepared for this transaction. These will be closed after the
|
// All Stmts prepared for this transaction. These will be closed after the
|
||||||
// transaction has been committed or rolled back.
|
// transaction has been committed or rolled back.
|
||||||
stmts struct {
|
stmts struct {
|
||||||
|
|
@ -2050,7 +2060,10 @@ func (tx *Tx) awaitDone() {
|
||||||
// transaction is closed and the resources are released. This
|
// transaction is closed and the resources are released. This
|
||||||
// rollback does nothing if the transaction has already been
|
// rollback does nothing if the transaction has already been
|
||||||
// committed or rolled back.
|
// committed or rolled back.
|
||||||
tx.rollback(true)
|
// Do not discard the connection if the connection knows
|
||||||
|
// how to reset the session.
|
||||||
|
discardConnection := !tx.keepConnOnRollback
|
||||||
|
tx.rollback(discardConnection)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Tx) isDone() bool {
|
func (tx *Tx) isDone() bool {
|
||||||
|
|
|
||||||
|
|
@ -442,6 +442,7 @@ func TestTxContextWait(t *testing.T) {
|
||||||
}
|
}
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
tx.keepConnOnRollback = false
|
||||||
|
|
||||||
// This will trigger the *fakeConn.Prepare method which will take time
|
// This will trigger the *fakeConn.Prepare method which will take time
|
||||||
// performing the query. The ctxDriverPrepare func will check the context
|
// performing the query. The ctxDriverPrepare func will check the context
|
||||||
|
|
@ -454,6 +455,35 @@ func TestTxContextWait(t *testing.T) {
|
||||||
waitForFree(t, db, 5*time.Second, 0)
|
waitForFree(t, db, 5*time.Second, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestTxContextWaitNoDiscard is the same as TestTxContextWait, but should not discard
|
||||||
|
// the final connection.
|
||||||
|
func TestTxContextWaitNoDiscard(t *testing.T) {
|
||||||
|
db := newTestDB(t, "people")
|
||||||
|
defer closeDB(t, db)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
// Guard against the context being canceled before BeginTx completes.
|
||||||
|
if err == context.DeadlineExceeded {
|
||||||
|
t.Skip("tx context canceled prior to first use")
|
||||||
|
}
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will trigger the *fakeConn.Prepare method which will take time
|
||||||
|
// performing the query. The ctxDriverPrepare func will check the context
|
||||||
|
// after this and close the rows and return an error.
|
||||||
|
_, err = tx.QueryContext(ctx, "WAIT|1s|SELECT|people|age,name|")
|
||||||
|
if err != context.DeadlineExceeded {
|
||||||
|
t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForFree(t, db, 5*time.Second, 1)
|
||||||
|
}
|
||||||
|
|
||||||
// TestUnsupportedOptions checks that the database fails when a driver that
|
// TestUnsupportedOptions checks that the database fails when a driver that
|
||||||
// doesn't implement ConnBeginTx is used with non-default options and an
|
// doesn't implement ConnBeginTx is used with non-default options and an
|
||||||
// un-cancellable context.
|
// un-cancellable context.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue