diff options
| author | Brad Fitzpatrick <bradfitz@golang.org> | 2024-03-16 18:29:06 -0700 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2024-03-18 23:40:44 +0000 |
| commit | 190d0d3e69b113bea0b6b604ba2f0beb62c08741 (patch) | |
| tree | 04d06f56ce711e71011956c6e6fb7733731b786a /src/database/sql/sql_test.go | |
| parent | 1ed85ee228023d766b37db056311929c00091c9f (diff) | |
| download | go-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.go | 104 |
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") + } + } +} |
