@@ -421,7 +421,6 @@ type DB struct {
421421 // It is closed during db.Close(). The close tells the connectionOpener
422422 // goroutine to exit.
423423 openerCh chan struct {}
424- resetterCh chan * driverConn
425424 closed bool
426425 dep map [finalCloser ]depSet
427426 lastPut map [* driverConn ]string // stacktrace of last conn's put; debug only
@@ -460,10 +459,10 @@ type driverConn struct {
460459
461460 sync.Mutex // guards following
462461 ci driver.Conn
462+ needReset bool // The connection session should be reset before use if true.
463463 closed bool
464464 finalClosed bool // ci.Close has been called
465465 openStmt map [* driverStmt ]bool
466- lastErr error // lastError captures the result of the session resetter.
467466
468467 // guarded by db.mu
469468 inUse bool
@@ -489,6 +488,36 @@ func (dc *driverConn) expired(timeout time.Duration) bool {
489488 return dc .createdAt .Add (timeout ).Before (nowFunc ())
490489}
491490
491+ // resetSession checks if the driver connection needs the
492+ // session to be reset and if required, resets it.
493+ func (dc * driverConn ) resetSession (ctx context.Context ) error {
494+ dc .Lock ()
495+ defer dc .Unlock ()
496+
497+ if ! dc .needReset {
498+ return nil
499+ }
500+ if cr , ok := dc .ci .(driver.SessionResetter ); ok {
501+ return cr .ResetSession (ctx )
502+ }
503+ return nil
504+ }
505+
506+ // validateConnection checks if the connection is valid and can
507+ // still be used. It also marks the session for reset if required.
508+ func (dc * driverConn ) validateConnection (needsReset bool ) bool {
509+ dc .Lock ()
510+ defer dc .Unlock ()
511+
512+ if needsReset {
513+ dc .needReset = true
514+ }
515+ if cv , ok := dc .ci .(driver.ConnectionValidator ); ok {
516+ return cv .ValidConnection ()
517+ }
518+ return true
519+ }
520+
492521// prepareLocked prepares the query on dc. When cg == nil the dc must keep track of
493522// the prepared statements in a pool.
494523func (dc * driverConn ) prepareLocked (ctx context.Context , cg stmtConnGrabber , query string ) (* driverStmt , error ) {
@@ -514,19 +543,6 @@ func (dc *driverConn) prepareLocked(ctx context.Context, cg stmtConnGrabber, que
514543 return ds , nil
515544}
516545
517- // resetSession resets the connection session and sets the lastErr
518- // that is checked before returning the connection to another query.
519- //
520- // resetSession assumes that the embedded mutex is locked when the connection
521- // was returned to the pool. This unlocks the mutex.
522- func (dc * driverConn ) resetSession (ctx context.Context ) {
523- defer dc .Unlock () // In case of panic.
524- if dc .closed { // Check if the database has been closed.
525- return
526- }
527- dc .lastErr = dc .ci .(driver.SessionResetter ).ResetSession (ctx )
528- }
529-
530546// the dc.db's Mutex is held.
531547func (dc * driverConn ) closeDBLocked () func () error {
532548 dc .Lock ()
@@ -716,14 +732,12 @@ func OpenDB(c driver.Connector) *DB {
716732 db := & DB {
717733 connector : c ,
718734 openerCh : make (chan struct {}, connectionRequestQueueSize ),
719- resetterCh : make (chan * driverConn , 50 ),
720735 lastPut : make (map [* driverConn ]string ),
721736 connRequests : make (map [uint64 ]chan connRequest ),
722737 stop : cancel ,
723738 }
724739
725740 go db .connectionOpener (ctx )
726- go db .connectionResetter (ctx )
727741
728742 return db
729743}
@@ -1118,23 +1132,6 @@ func (db *DB) connectionOpener(ctx context.Context) {
11181132 }
11191133}
11201134
1121- // connectionResetter runs in a separate goroutine to reset connections async
1122- // to exported API.
1123- func (db * DB ) connectionResetter (ctx context.Context ) {
1124- for {
1125- select {
1126- case <- ctx .Done ():
1127- close (db .resetterCh )
1128- for dc := range db .resetterCh {
1129- dc .Unlock ()
1130- }
1131- return
1132- case dc := <- db .resetterCh :
1133- dc .resetSession (ctx )
1134- }
1135- }
1136- }
1137-
11381135// Open one new connection
11391136func (db * DB ) openNewConnection (ctx context.Context ) {
11401137 // maybeOpenNewConnctions has already executed db.numOpen++ before it sent
@@ -1216,14 +1213,13 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn
12161213 conn .Close ()
12171214 return nil , driver .ErrBadConn
12181215 }
1219- // Lock around reading lastErr to ensure the session resetter finished.
1220- conn .Lock ()
1221- err := conn .lastErr
1222- conn .Unlock ()
1223- if err == driver .ErrBadConn {
1216+
1217+ // Reset the session if required.
1218+ if err := conn .resetSession (ctx ); err == driver .ErrBadConn {
12241219 conn .Close ()
12251220 return nil , driver .ErrBadConn
12261221 }
1222+
12271223 return conn , nil
12281224 }
12291225
@@ -1272,11 +1268,9 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn
12721268 if ret .conn == nil {
12731269 return nil , ret .err
12741270 }
1275- // Lock around reading lastErr to ensure the session resetter finished.
1276- ret .conn .Lock ()
1277- err := ret .conn .lastErr
1278- ret .conn .Unlock ()
1279- if err == driver .ErrBadConn {
1271+
1272+ // Reset the session if required.
1273+ if err := ret .conn .resetSession (ctx ); err == driver .ErrBadConn {
12801274 ret .conn .Close ()
12811275 return nil , driver .ErrBadConn
12821276 }
@@ -1337,6 +1331,11 @@ const debugGetPut = false
13371331// putConn adds a connection to the db's free pool.
13381332// err is optionally the last error that occurred on this connection.
13391333func (db * DB ) putConn (dc * driverConn , err error , resetSession bool ) {
1334+ if err != driver .ErrBadConn {
1335+ if ! dc .validateConnection (resetSession ) {
1336+ err = driver .ErrBadConn
1337+ }
1338+ }
13401339 db .mu .Lock ()
13411340 if ! dc .inUse {
13421341 if debugGetPut {
@@ -1368,41 +1367,13 @@ func (db *DB) putConn(dc *driverConn, err error, resetSession bool) {
13681367 if putConnHook != nil {
13691368 putConnHook (db , dc )
13701369 }
1371- if db .closed {
1372- // Connections do not need to be reset if they will be closed.
1373- // Prevents writing to resetterCh after the DB has closed.
1374- resetSession = false
1375- }
1376- if resetSession {
1377- if _ , resetSession = dc .ci .(driver.SessionResetter ); resetSession {
1378- // Lock the driverConn here so it isn't released until
1379- // the connection is reset.
1380- // The lock must be taken before the connection is put into
1381- // the pool to prevent it from being taken out before it is reset.
1382- dc .Lock ()
1383- }
1384- }
13851370 added := db .putConnDBLocked (dc , nil )
13861371 db .mu .Unlock ()
13871372
13881373 if ! added {
1389- if resetSession {
1390- dc .Unlock ()
1391- }
13921374 dc .Close ()
13931375 return
13941376 }
1395- if ! resetSession {
1396- return
1397- }
1398- select {
1399- default :
1400- // If the resetterCh is blocking then mark the connection
1401- // as bad and continue on.
1402- dc .lastErr = driver .ErrBadConn
1403- dc .Unlock ()
1404- case db .resetterCh <- dc :
1405- }
14061377}
14071378
14081379// Satisfy a connRequest or put the driverConn in the idle pool and return true
0 commit comments