aboutsummaryrefslogtreecommitdiff
path: root/src/database/sql/sql_test.go
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@golang.org>2024-03-16 18:29:06 -0700
committerGopher Robot <gobot@golang.org>2024-03-18 23:40:44 +0000
commit190d0d3e69b113bea0b6b604ba2f0beb62c08741 (patch)
tree04d06f56ce711e71011956c6e6fb7733731b786a /src/database/sql/sql_test.go
parent1ed85ee228023d766b37db056311929c00091c9f (diff)
downloadgo-190d0d3e69b113bea0b6b604ba2f0beb62c08741.tar.xz
database/sql: optimize connection request pool
This replaces a map used as a set with a slice. We were using a surprising amount of CPU in this code, making mapiters to pull out a random element of the map. Instead, just rand.IntN to pick a random element of the slice. It also adds a benchmark: │ before │ after │ │ sec/op │ sec/op vs base │ ConnRequestSet-8 1818.0n ± 0% 452.4n ± 0% -75.12% (p=0.000 n=10) (whether random is a good policy is a bigger question, but this optimizes the current policy without changing behavior) Updates #66361 Change-Id: I3d456a819cc720c2d18e1befffd2657e5f50f1e7 Reviewed-on: https://go-review.googlesource.com/c/go/+/572119 Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com> Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/database/sql/sql_test.go')
-rw-r--r--src/database/sql/sql_test.go104
1 files changed, 103 insertions, 1 deletions
diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go
index bf0ecc243f..e786ecbfab 100644
--- a/src/database/sql/sql_test.go
+++ b/src/database/sql/sql_test.go
@@ -14,6 +14,7 @@ import (
"math/rand"
"reflect"
"runtime"
+ "slices"
"strings"
"sync"
"sync/atomic"
@@ -2983,7 +2984,7 @@ func TestConnExpiresFreshOutOfPool(t *testing.T) {
return
}
db.mu.Lock()
- ct := len(db.connRequests)
+ ct := db.connRequests.Len()
db.mu.Unlock()
if ct > 0 {
return
@@ -4803,3 +4804,104 @@ func BenchmarkGrabConn(b *testing.B) {
release(nil)
}
}
+
+func TestConnRequestSet(t *testing.T) {
+ var s connRequestSet
+ wantLen := func(want int) {
+ t.Helper()
+ if got := s.Len(); got != want {
+ t.Errorf("Len = %d; want %d", got, want)
+ }
+ if want == 0 && !t.Failed() {
+ if _, ok := s.TakeRandom(); ok {
+ t.Fatalf("TakeRandom returned result when empty")
+ }
+ }
+ }
+ reset := func() { s = connRequestSet{} }
+
+ t.Run("add-delete", func(t *testing.T) {
+ reset()
+ wantLen(0)
+ dh := s.Add(nil)
+ wantLen(1)
+ if !s.Delete(dh) {
+ t.Fatal("failed to delete")
+ }
+ wantLen(0)
+ if s.Delete(dh) {
+ t.Error("delete worked twice")
+ }
+ wantLen(0)
+ })
+ t.Run("take-before-delete", func(t *testing.T) {
+ reset()
+ ch1 := make(chan connRequest)
+ dh := s.Add(ch1)
+ wantLen(1)
+ if got, ok := s.TakeRandom(); !ok || got != ch1 {
+ t.Fatalf("wrong take; ok=%v", ok)
+ }
+ wantLen(0)
+ if s.Delete(dh) {
+ t.Error("unexpected delete after take")
+ }
+ })
+ t.Run("get-take-many", func(t *testing.T) {
+ reset()
+ m := map[chan connRequest]bool{}
+ const N = 100
+ var inOrder, backOut []chan connRequest
+ for range N {
+ c := make(chan connRequest)
+ m[c] = true
+ s.Add(c)
+ inOrder = append(inOrder, c)
+ }
+ if s.Len() != N {
+ t.Fatalf("Len = %v; want %v", s.Len(), N)
+ }
+ for s.Len() > 0 {
+ c, ok := s.TakeRandom()
+ if !ok {
+ t.Fatal("failed to take when non-empty")
+ }
+ if !m[c] {
+ t.Fatal("returned item not in remaining set")
+ }
+ delete(m, c)
+ backOut = append(backOut, c)
+ }
+ if len(m) > 0 {
+ t.Error("items remain in expected map")
+ }
+ if slices.Equal(inOrder, backOut) { // N! chance of flaking; N=100 is fine
+ t.Error("wasn't random")
+ }
+ })
+}
+
+func BenchmarkConnRequestSet(b *testing.B) {
+ var s connRequestSet
+ for range b.N {
+ for range 16 {
+ s.Add(nil)
+ }
+ for range 8 {
+ if _, ok := s.TakeRandom(); !ok {
+ b.Fatal("want ok")
+ }
+ }
+ for range 8 {
+ s.Add(nil)
+ }
+ for range 16 {
+ if _, ok := s.TakeRandom(); !ok {
+ b.Fatal("want ok")
+ }
+ }
+ if _, ok := s.TakeRandom(); ok {
+ b.Fatal("unexpected ok")
+ }
+ }
+}