aboutsummaryrefslogtreecommitdiff
path: root/src/database/sql
diff options
context:
space:
mode:
authorBryan C. Mills <bcmills@google.com>2022-01-13 13:57:47 -0500
committerBryan Mills <bcmills@google.com>2022-01-13 20:43:56 +0000
commit6891d07ee6a34f1c8d0326f3f7dd941bddf524f1 (patch)
tree1e8a2e9a5b86eecf2558c41776df33517795c05d /src/database/sql
parent3ff12a019f00bc81996c453e5cb4729a9278f65a (diff)
downloadgo-6891d07ee6a34f1c8d0326f3f7dd941bddf524f1.tar.xz
database/sql: consolidate test polling loops
Also eliminate some arbitrary deadlines and sleeps. Fixes #49958 Change-Id: I999b39a896e430e3bb93aa8b8c9444f28bbaa9d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/378395 Trust: Bryan Mills <bcmills@google.com> Run-TryBot: Bryan Mills <bcmills@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Daniel Theophanes <kardianos@gmail.com>
Diffstat (limited to 'src/database/sql')
-rw-r--r--src/database/sql/sql_test.go111
1 files changed, 61 insertions, 50 deletions
diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go
index 1bb9afc407..08ca1f5b9a 100644
--- a/src/database/sql/sql_test.go
+++ b/src/database/sql/sql_test.go
@@ -55,6 +55,10 @@ func init() {
}
}
+// pollDuration is an arbitrary interval to wait between checks when polling for
+// a condition to occur.
+const pollDuration = 5 * time.Millisecond
+
const fakeDBName = "foo"
var chrisBirthday = time.Unix(123456789, 0)
@@ -173,7 +177,7 @@ func closeDB(t testing.TB, db *DB) {
}
var numOpen int
- if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
+ if !waitCondition(t, func() bool {
numOpen = db.numOpenConns()
return numOpen == 0
}) {
@@ -197,16 +201,14 @@ func (db *DB) numDeps() int {
}
// Dependencies are closed via a goroutine, so this polls waiting for
-// numDeps to fall to want, waiting up to d.
-func (db *DB) numDepsPollUntil(want int, d time.Duration) int {
- deadline := time.Now().Add(d)
- for {
- n := db.numDeps()
- if n <= want || time.Now().After(deadline) {
- return n
- }
- time.Sleep(50 * time.Millisecond)
- }
+// numDeps to fall to want, waiting up to nearly the test's deadline.
+func (db *DB) numDepsPoll(t *testing.T, want int) int {
+ var n int
+ waitCondition(t, func() bool {
+ n = db.numDeps()
+ return n <= want
+ })
+ return n
}
func (db *DB) numFreeConns() int {
@@ -229,7 +231,7 @@ func (db *DB) clearAllConns(t *testing.T) {
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(0, time.Second); n > 0 {
+ if n := db.numDepsPoll(t, 0); n > 0 {
t.Errorf("number of dependencies = %d; expected 0", n)
db.dumpDeps(t)
}
@@ -321,7 +323,7 @@ func TestQueryContext(t *testing.T) {
for rows.Next() {
if index == 2 {
cancel()
- waitForRowsClose(t, rows, 5*time.Second)
+ waitForRowsClose(t, rows)
}
var r row
err = rows.Scan(&r.age, &r.name)
@@ -355,29 +357,43 @@ func TestQueryContext(t *testing.T) {
// And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection.
- waitForRowsClose(t, rows, 5*time.Second)
- waitForFree(t, db, 5*time.Second, 1)
+ waitForRowsClose(t, rows)
+ waitForFree(t, db, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
}
-func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
- deadline := time.Now().Add(waitFor)
- for time.Now().Before(deadline) {
+func waitCondition(t testing.TB, fn func() bool) bool {
+ timeout := 5 * time.Second
+
+ type deadliner interface {
+ Deadline() (time.Time, bool)
+ }
+ if td, ok := t.(deadliner); ok {
+ if deadline, ok := td.Deadline(); ok {
+ timeout = time.Until(deadline)
+ timeout = timeout * 19 / 20 // Give 5% headroom for cleanup and error-reporting.
+ }
+ }
+
+ deadline := time.Now().Add(timeout)
+ for {
if fn() {
return true
}
- time.Sleep(checkEvery)
+ if time.Until(deadline) < pollDuration {
+ return false
+ }
+ time.Sleep(pollDuration)
}
- return false
}
// waitForFree checks db.numFreeConns until either it equals want or
// the maxWait time elapses.
-func waitForFree(t *testing.T, db *DB, maxWait time.Duration, want int) {
+func waitForFree(t *testing.T, db *DB, want int) {
var numFree int
- if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
+ if !waitCondition(t, func() bool {
numFree = db.numFreeConns()
return numFree == want
}) {
@@ -385,8 +401,8 @@ func waitForFree(t *testing.T, db *DB, maxWait time.Duration, want int) {
}
}
-func waitForRowsClose(t *testing.T, rows *Rows, maxWait time.Duration) {
- if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
+func waitForRowsClose(t *testing.T, rows *Rows) {
+ if !waitCondition(t, func() bool {
rows.closemu.RLock()
defer rows.closemu.RUnlock()
return rows.closed
@@ -416,7 +432,7 @@ func TestQueryContextWait(t *testing.T) {
}
// Verify closed rows connection after error condition.
- waitForFree(t, db, 5*time.Second, 1)
+ waitForFree(t, db, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
// TODO(kardianos): if the context timeouts before the db.QueryContext
// executes this check may fail. After adjusting how the context
@@ -451,7 +467,7 @@ func TestTxContextWait(t *testing.T) {
t.Fatalf("expected QueryContext to error with context canceled but returned %v", err)
}
- waitForFree(t, db, 5*time.Second, 0)
+ waitForFree(t, db, 0)
}
// TestTxContextWaitNoDiscard is the same as TestTxContextWait, but should not discard
@@ -480,7 +496,7 @@ func TestTxContextWaitNoDiscard(t *testing.T) {
t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
}
- waitForFree(t, db, 5*time.Second, 1)
+ waitForFree(t, db, 1)
}
// TestUnsupportedOptions checks that the database fails when a driver that
@@ -565,7 +581,7 @@ func TestMultiResultSetQuery(t *testing.T) {
// And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection.
- waitForFree(t, db, 5*time.Second, 1)
+ waitForFree(t, db, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
@@ -2083,18 +2099,15 @@ func TestMaxOpenConns(t *testing.T) {
}
}()
}
- // Sleep for twice the expected length of time for the
- // batch of 50 queries above to finish before starting
- // the next round.
- time.Sleep(2 * sleepMillis * time.Millisecond)
+ // Wait for the batch of queries above to finish before starting the next round.
+ wg.Wait()
}
- wg.Wait()
if g, w := db.numFreeConns(), 10; g != w {
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(20, time.Second); n > 20 {
+ if n := db.numDepsPoll(t, 20); n > 20 {
t.Errorf("number of dependencies = %d; expected <= 20", n)
db.dumpDeps(t)
}
@@ -2119,7 +2132,7 @@ func TestMaxOpenConns(t *testing.T) {
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(10, time.Second); n > 10 {
+ if n := db.numDepsPoll(t, 10); n > 10 {
t.Errorf("number of dependencies = %d; expected <= 10", n)
db.dumpDeps(t)
}
@@ -2130,7 +2143,7 @@ func TestMaxOpenConns(t *testing.T) {
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(5, time.Second); n > 5 {
+ if n := db.numDepsPoll(t, 5); n > 5 {
t.Errorf("number of dependencies = %d; expected 0", n)
db.dumpDeps(t)
}
@@ -2141,7 +2154,7 @@ func TestMaxOpenConns(t *testing.T) {
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(5, time.Second); n > 5 {
+ if n := db.numDepsPoll(t, 5); n > 5 {
t.Errorf("number of dependencies = %d; expected 0", n)
db.dumpDeps(t)
}
@@ -2400,13 +2413,14 @@ func TestConnMaxLifetime(t *testing.T) {
tx2.Commit()
// Give connectionCleaner chance to run.
- for i := 0; i < 100 && closes != 1; i++ {
- time.Sleep(time.Millisecond)
+ waitCondition(t, func() bool {
driver.mu.Lock()
opens = driver.openCount - opens0
closes = driver.closeCount - closes0
driver.mu.Unlock()
- }
+
+ return closes == 1
+ })
if opens != 3 {
t.Errorf("opens = %d; want 3", opens)
@@ -2466,18 +2480,15 @@ func TestStmtCloseDeps(t *testing.T) {
}
}()
}
- // Sleep for twice the expected length of time for the
- // batch of 50 queries above to finish before starting
- // the next round.
- time.Sleep(2 * sleepMillis * time.Millisecond)
+ // Wait for the batch of queries above to finish before starting the next round.
+ wg.Wait()
}
- wg.Wait()
if g, w := db.numFreeConns(), 2; g != w {
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(4, time.Second); n > 4 {
+ if n := db.numDepsPoll(t, 4); n > 4 {
t.Errorf("number of dependencies = %d; expected <= 4", n)
db.dumpDeps(t)
}
@@ -2496,7 +2507,7 @@ func TestStmtCloseDeps(t *testing.T) {
db.dumpDeps(t)
}
- if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
+ if !waitCondition(t, func() bool {
return len(stmt.css) <= nquery
}) {
t.Errorf("len(stmt.css) = %d; want <= %d", len(stmt.css), nquery)
@@ -2510,7 +2521,7 @@ func TestStmtCloseDeps(t *testing.T) {
t.Errorf("free conns = %d; want %d", g, w)
}
- if n := db.numDepsPollUntil(2, time.Second); n > 2 {
+ if n := db.numDepsPoll(t, 2); n > 2 {
t.Errorf("number of dependencies = %d; expected <= 2", n)
db.dumpDeps(t)
}
@@ -2942,7 +2953,7 @@ func TestConnExpiresFreshOutOfPool(t *testing.T) {
if ct > 0 {
return
}
- time.Sleep(10 * time.Millisecond)
+ time.Sleep(pollDuration)
}
}()
@@ -3721,7 +3732,7 @@ func TestIssue18719(t *testing.T) {
// Wait for the context to cancel and tx to rollback.
for tx.isDone() == false {
- time.Sleep(3 * time.Millisecond)
+ time.Sleep(pollDuration)
}
}
defer func() { hookTxGrabConn = nil }()