@@ -421,7 +421,6 @@ type DB struct {
421
421
// It is closed during db.Close(). The close tells the connectionOpener
422
422
// goroutine to exit.
423
423
openerCh chan struct {}
424
- resetterCh chan * driverConn
425
424
closed bool
426
425
dep map [finalCloser ]depSet
427
426
lastPut map [* driverConn ]string // stacktrace of last conn's put; debug only
@@ -460,10 +459,10 @@ type driverConn struct {
460
459
461
460
sync.Mutex // guards following
462
461
ci driver.Conn
462
+ needReset bool // The connection session should be reset before use if true.
463
463
closed bool
464
464
finalClosed bool // ci.Close has been called
465
465
openStmt map [* driverStmt ]bool
466
- lastErr error // lastError captures the result of the session resetter.
467
466
468
467
// guarded by db.mu
469
468
inUse bool
@@ -489,6 +488,36 @@ func (dc *driverConn) expired(timeout time.Duration) bool {
489
488
return dc .createdAt .Add (timeout ).Before (nowFunc ())
490
489
}
491
490
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
+
492
521
// prepareLocked prepares the query on dc. When cg == nil the dc must keep track of
493
522
// the prepared statements in a pool.
494
523
func (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
514
543
return ds , nil
515
544
}
516
545
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
-
530
546
// the dc.db's Mutex is held.
531
547
func (dc * driverConn ) closeDBLocked () func () error {
532
548
dc .Lock ()
@@ -716,14 +732,12 @@ func OpenDB(c driver.Connector) *DB {
716
732
db := & DB {
717
733
connector : c ,
718
734
openerCh : make (chan struct {}, connectionRequestQueueSize ),
719
- resetterCh : make (chan * driverConn , 50 ),
720
735
lastPut : make (map [* driverConn ]string ),
721
736
connRequests : make (map [uint64 ]chan connRequest ),
722
737
stop : cancel ,
723
738
}
724
739
725
740
go db .connectionOpener (ctx )
726
- go db .connectionResetter (ctx )
727
741
728
742
return db
729
743
}
@@ -1118,23 +1132,6 @@ func (db *DB) connectionOpener(ctx context.Context) {
1118
1132
}
1119
1133
}
1120
1134
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
-
1138
1135
// Open one new connection
1139
1136
func (db * DB ) openNewConnection (ctx context.Context ) {
1140
1137
// maybeOpenNewConnctions has already executed db.numOpen++ before it sent
@@ -1216,14 +1213,13 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn
1216
1213
conn .Close ()
1217
1214
return nil , driver .ErrBadConn
1218
1215
}
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 {
1224
1219
conn .Close ()
1225
1220
return nil , driver .ErrBadConn
1226
1221
}
1222
+
1227
1223
return conn , nil
1228
1224
}
1229
1225
@@ -1272,11 +1268,9 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn
1272
1268
if ret .conn == nil {
1273
1269
return nil , ret .err
1274
1270
}
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 {
1280
1274
ret .conn .Close ()
1281
1275
return nil , driver .ErrBadConn
1282
1276
}
@@ -1337,6 +1331,11 @@ const debugGetPut = false
1337
1331
// putConn adds a connection to the db's free pool.
1338
1332
// err is optionally the last error that occurred on this connection.
1339
1333
func (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
+ }
1340
1339
db .mu .Lock ()
1341
1340
if ! dc .inUse {
1342
1341
if debugGetPut {
@@ -1368,41 +1367,13 @@ func (db *DB) putConn(dc *driverConn, err error, resetSession bool) {
1368
1367
if putConnHook != nil {
1369
1368
putConnHook (db , dc )
1370
1369
}
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
- }
1385
1370
added := db .putConnDBLocked (dc , nil )
1386
1371
db .mu .Unlock ()
1387
1372
1388
1373
if ! added {
1389
- if resetSession {
1390
- dc .Unlock ()
1391
- }
1392
1374
dc .Close ()
1393
1375
return
1394
1376
}
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
- }
1406
1377
}
1407
1378
1408
1379
// Satisfy a connRequest or put the driverConn in the idle pool and return true
0 commit comments