Signed-off-by: Andres Taylor <andres@planetscale.com>
This commit is contained in:
Andres Taylor 2021-09-30 08:09:45 +02:00
Родитель da34a47a87
Коммит 31820ccb7c
1 изменённых файлов: 102 добавлений и 279 удалений

Просмотреть файл

@ -21,6 +21,9 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"context"
"vitess.io/vitess/go/sync2"
@ -73,27 +76,13 @@ func TestOpen(t *testing.T) {
for i := 0; i < 5; i++ {
r, err := p.Get(ctx)
resources[i] = r
if err != nil {
t.Errorf("Unexpected error %v", err)
}
if p.Available() != int64(5-i-1) {
t.Errorf("expecting %d, received %d", 5-i-1, p.Available())
}
if p.WaitCount() != 0 {
t.Errorf("expecting 0, received %d", p.WaitCount())
}
if len(waitStarts) != 0 {
t.Errorf("expecting 0, received %d", len(waitStarts))
}
if p.WaitTime() != 0 {
t.Errorf("expecting 0, received %d", p.WaitTime())
}
if lastID.Get() != int64(i+1) {
t.Errorf("Expecting %d, received %d", i+1, lastID.Get())
}
if count.Get() != int64(i+1) {
t.Errorf("Expecting %d, received %d", i+1, count.Get())
}
require.NoError(t, err)
assert.EqualValues(t, 5-i-1, p.Available())
assert.Zero(t, p.WaitCount())
assert.Zero(t, len(waitStarts))
assert.Zero(t, p.WaitTime())
assert.EqualValues(t, i+1, lastID.Get())
assert.EqualValues(t, i+1, count.Get())
}
// Test that Get waits
@ -101,9 +90,7 @@ func TestOpen(t *testing.T) {
go func() {
for i := 0; i < 5; i++ {
r, err := p.Get(ctx)
if err != nil {
t.Errorf("Get failed: %v", err)
}
require.NoError(t, err)
resources[i] = r
}
for i := 0; i < 5; i++ {
@ -117,102 +104,62 @@ func TestOpen(t *testing.T) {
p.Put(resources[i])
}
<-ch
if p.WaitCount() != 5 {
t.Errorf("Expecting 5, received %d", p.WaitCount())
}
if int64(len(waitStarts)) != p.WaitCount() {
t.Errorf("expecting %d, received %d", p.WaitCount(), len(waitStarts))
}
assert.EqualValues(t, 5, p.WaitCount())
assert.Equal(t, 5, len(waitStarts))
// verify start times are monotonic increasing
for i := 1; i < len(waitStarts); i++ {
if waitStarts[i].Before(waitStarts[i-1]) {
t.Errorf("Expecting monotonic increasing start times")
}
}
if p.WaitTime() == 0 {
t.Errorf("Expecting non-zero")
}
if lastID.Get() != 5 {
t.Errorf("Expecting 5, received %d", lastID.Get())
}
assert.NotZero(t, p.WaitTime())
assert.EqualValues(t, 5, lastID.Get())
// Test Close resource
r, err := p.Get(ctx)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
require.NoError(t, err)
r.Close()
// A nil Put should cause the resource to be reopened.
p.Put(nil)
if count.Get() != 5 {
t.Errorf("Expecting 5, received %d", count.Get())
}
assert.EqualValues(t, 5, count.Get())
for i := 0; i < 5; i++ {
r, err := p.Get(ctx)
if err != nil {
t.Errorf("Get failed: %v", err)
}
require.NoError(t, err)
resources[i] = r
}
for i := 0; i < 5; i++ {
p.Put(resources[i])
}
if count.Get() != 5 {
t.Errorf("Expecting 5, received %d", count.Get())
}
if lastID.Get() != 6 {
t.Errorf("Expecting 6, received %d", lastID.Get())
}
assert.EqualValues(t, 5, count.Get())
assert.EqualValues(t, 6, lastID.Get())
// SetCapacity
p.SetCapacity(3)
if count.Get() != 3 {
t.Errorf("Expecting 3, received %d", count.Get())
}
if lastID.Get() != 6 {
t.Errorf("Expecting 6, received %d", lastID.Get())
}
if p.Capacity() != 3 {
t.Errorf("Expecting 3, received %d", p.Capacity())
}
if p.Available() != 3 {
t.Errorf("Expecting 3, received %d", p.Available())
}
assert.EqualValues(t, 3, count.Get())
assert.EqualValues(t, 6, lastID.Get())
assert.EqualValues(t, 3, p.Capacity())
assert.EqualValues(t, 3, p.Available())
p.SetCapacity(6)
if p.Capacity() != 6 {
t.Errorf("Expecting 6, received %d", p.Capacity())
}
if p.Available() != 6 {
t.Errorf("Expecting 6, received %d", p.Available())
}
assert.EqualValues(t, 6, p.Capacity())
assert.EqualValues(t, 6, p.Available())
for i := 0; i < 6; i++ {
r, err := p.Get(ctx)
if err != nil {
t.Errorf("Get failed: %v", err)
}
require.NoError(t, err)
resources[i] = r
}
for i := 0; i < 6; i++ {
p.Put(resources[i])
}
if count.Get() != 6 {
t.Errorf("Expecting 5, received %d", count.Get())
}
if lastID.Get() != 9 {
t.Errorf("Expecting 9, received %d", lastID.Get())
}
assert.EqualValues(t, 6, count.Get())
assert.EqualValues(t, 9, lastID.Get())
// Close
p.Close()
if p.Capacity() != 0 {
t.Errorf("Expecting 0, received %d", p.Capacity())
}
if p.Available() != 0 {
t.Errorf("Expecting 0, received %d", p.Available())
}
if count.Get() != 0 {
t.Errorf("Expecting 0, received %d", count.Get())
}
assert.EqualValues(t, 0, p.Capacity())
assert.EqualValues(t, 0, p.Available())
assert.EqualValues(t, 0, count.Get())
}
func TestPrefill(t *testing.T) {
@ -220,14 +167,10 @@ func TestPrefill(t *testing.T) {
count.Set(0)
p := NewResourcePool(PoolFactory, 5, 5, time.Second, 1, logWait, nil, 0)
defer p.Close()
if p.Active() != 5 {
t.Errorf("p.Active(): %d, want 5", p.Active())
}
assert.EqualValues(t, 5, p.Active())
p = NewResourcePool(FailFactory, 5, 5, time.Second, 1, logWait, nil, 0)
defer p.Close()
if p.Active() != 0 {
t.Errorf("p.Active(): %d, want 0", p.Active())
}
assert.EqualValues(t, 0, p.Active())
}
func TestPrefillTimeout(t *testing.T) {
@ -243,9 +186,7 @@ func TestPrefillTimeout(t *testing.T) {
if elapsed := time.Since(start); elapsed > 20*time.Millisecond {
t.Errorf("elapsed: %v, should be around 10ms", elapsed)
}
if p.Active() != 0 {
t.Errorf("p.Active(): %d, want 0", p.Active())
}
assert.Zero(t, p.Active())
}
func TestShrinking(t *testing.T) {
@ -259,9 +200,7 @@ func TestShrinking(t *testing.T) {
// Leave one empty slot in the pool
for i := 0; i < 4; i++ {
r, err := p.Get(ctx)
if err != nil {
t.Errorf("Get failed: %v", err)
}
require.NoError(t, err)
resources[i] = r
}
done := make(chan bool)
@ -289,28 +228,20 @@ func TestShrinking(t *testing.T) {
}
stats := p.StatsJSON()
expected = `{"Capacity": 3, "Available": 3, "Active": 3, "InUse": 0, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "Exhausted": 0}`
if stats != expected {
t.Errorf(`expecting '%s', received '%s'`, expected, stats)
}
if count.Get() != 3 {
t.Errorf("Expecting 3, received %d", count.Get())
}
assert.Equal(t, expected, stats)
assert.EqualValues(t, 3, count.Get())
// Ensure no deadlock if SetCapacity is called after we start
// waiting for a resource
var err error
for i := 0; i < 3; i++ {
resources[i], err = p.Get(ctx)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
require.NoError(t, err)
}
// This will wait because pool is empty
go func() {
r, err := p.Get(ctx)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
require.NoError(t, err)
p.Put(r)
done <- true
}()
@ -328,36 +259,22 @@ func TestShrinking(t *testing.T) {
}
<-done
<-done
if p.Capacity() != 2 {
t.Errorf("Expecting 2, received %d", p.Capacity())
}
if p.Available() != 2 {
t.Errorf("Expecting 2, received %d", p.Available())
}
if p.WaitCount() != 1 {
t.Errorf("Expecting 1, received %d", p.WaitCount())
}
if int64(len(waitStarts)) != p.WaitCount() {
t.Errorf("Expecting %d, received %d", p.WaitCount(), len(waitStarts))
}
if count.Get() != 2 {
t.Errorf("Expecting 2, received %d", count.Get())
}
assert.EqualValues(t, 2, p.Capacity())
assert.EqualValues(t, 2, p.Available())
assert.EqualValues(t, 1, p.WaitCount())
assert.EqualValues(t, p.WaitCount(), len(waitStarts))
assert.EqualValues(t, 2, count.Get())
// Test race condition of SetCapacity with itself
p.SetCapacity(3)
for i := 0; i < 3; i++ {
resources[i], err = p.Get(ctx)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
require.NoError(t, err)
}
// This will wait because pool is empty
go func() {
r, err := p.Get(ctx)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
require.NoError(t, err)
p.Put(r)
done <- true
}()
@ -384,12 +301,8 @@ func TestShrinking(t *testing.T) {
t.Errorf("Expecting error")
}
if p.Capacity() != 4 {
t.Errorf("Expecting 4, received %d", p.Capacity())
}
if p.Available() != 4 {
t.Errorf("Expecting 4, received %d", p.Available())
}
assert.EqualValues(t, 4, p.Capacity())
assert.EqualValues(t, 4, p.Available())
}
func TestClosing(t *testing.T) {
@ -400,9 +313,7 @@ func TestClosing(t *testing.T) {
var resources [10]Resource
for i := 0; i < 5; i++ {
r, err := p.Get(ctx)
if err != nil {
t.Errorf("Get failed: %v", err)
}
require.NoError(t, err)
resources[i] = r
}
ch := make(chan bool)
@ -415,9 +326,7 @@ func TestClosing(t *testing.T) {
time.Sleep(10 * time.Millisecond)
stats := p.StatsJSON()
expected := `{"Capacity": 0, "Available": 0, "Active": 5, "InUse": 5, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "Exhausted": 1}`
if stats != expected {
t.Errorf(`expecting '%s', received '%s'`, expected, stats)
}
assert.Equal(t, expected, stats)
// Put is allowed when closing
for i := 0; i < 5; i++ {
@ -429,15 +338,9 @@ func TestClosing(t *testing.T) {
stats = p.StatsJSON()
expected = `{"Capacity": 0, "Available": 0, "Active": 0, "InUse": 0, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "Exhausted": 1}`
if stats != expected {
t.Errorf(`expecting '%s', received '%s'`, expected, stats)
}
if lastID.Get() != 5 {
t.Errorf("Expecting 5, received %d", lastID.Get())
}
if count.Get() != 0 {
t.Errorf("Expecting 0, received %d", count.Get())
}
assert.Equal(t, expected, stats)
assert.EqualValues(t, 5, lastID.Get())
assert.EqualValues(t, 0, count.Get())
}
func TestReopen(t *testing.T) {
@ -451,18 +354,14 @@ func TestReopen(t *testing.T) {
var resources [10]Resource
for i := 0; i < 5; i++ {
r, err := p.Get(ctx)
if err != nil {
t.Errorf("Get failed: %v", err)
}
require.NoError(t, err)
resources[i] = r
}
time.Sleep(10 * time.Millisecond)
stats := p.StatsJSON()
expected := `{"Capacity": 5, "Available": 0, "Active": 5, "InUse": 5, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "Exhausted": 1}`
if stats != expected {
t.Errorf(`expecting '%s', received '%s'`, expected, stats)
}
assert.Equal(t, expected, stats)
time.Sleep(650 * time.Millisecond)
for i := 0; i < 5; i++ {
@ -471,16 +370,9 @@ func TestReopen(t *testing.T) {
time.Sleep(50 * time.Millisecond)
stats = p.StatsJSON()
expected = `{"Capacity": 5, "Available": 5, "Active": 0, "InUse": 0, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "Exhausted": 1}`
if stats != expected {
t.Errorf(`expecting '%s', received '%s', capacity '%d'`, expected, stats, p.Capacity())
}
if lastID.Get() != 5 {
t.Errorf("Expecting 5, received %d", lastID.Get())
}
if count.Get() != 0 {
t.Errorf("Expecting 5, received %d", count.Get())
}
assert.Equal(t, expected, stats)
assert.EqualValues(t, 5, lastID.Get())
assert.EqualValues(t, 0, count.Get())
}
func TestIdleTimeout(t *testing.T) {
@ -491,73 +383,38 @@ func TestIdleTimeout(t *testing.T) {
defer p.Close()
r, err := p.Get(ctx)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
if count.Get() != 1 {
t.Errorf("Expecting 1, received %d", count.Get())
}
if p.IdleClosed() != 0 {
t.Errorf("Expecting 0, received %d", p.IdleClosed())
}
p.Put(r)
if lastID.Get() != 1 {
t.Errorf("Expecting 1, received %d", lastID.Get())
}
if count.Get() != 1 {
t.Errorf("Expecting 1, received %d", count.Get())
}
if p.IdleClosed() != 0 {
t.Errorf("Expecting 0, received %d", p.IdleClosed())
}
time.Sleep(15 * time.Millisecond)
require.NoError(t, err)
assert.EqualValues(t, 1, count.Get())
assert.EqualValues(t, 0, p.IdleClosed())
p.Put(r)
assert.EqualValues(t, 1, lastID.Get())
assert.EqualValues(t, 1, count.Get())
assert.EqualValues(t, 0, p.IdleClosed())
time.Sleep(15 * time.Millisecond)
assert.EqualValues(t, 1, count.Get())
assert.EqualValues(t, 1, p.IdleClosed())
if count.Get() != 1 {
t.Errorf("Expecting 1, received %d", count.Get())
}
if p.IdleClosed() != 1 {
t.Errorf("Expecting 1, received %d", p.IdleClosed())
}
r, err = p.Get(ctx)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
if lastID.Get() != 2 {
t.Errorf("Expecting 2, received %d", lastID.Get())
}
if count.Get() != 1 {
t.Errorf("Expecting 1, received %d", count.Get())
}
if p.IdleClosed() != 1 {
t.Errorf("Expecting 1, received %d", p.IdleClosed())
}
require.NoError(t, err)
assert.EqualValues(t, 2, lastID.Get())
assert.EqualValues(t, 1, count.Get())
assert.EqualValues(t, 1, p.IdleClosed())
// sleep to let the idle closer run while all resources are in use
// then make sure things are still as we expect
time.Sleep(15 * time.Millisecond)
if lastID.Get() != 2 {
t.Errorf("Expecting 2, received %d", lastID.Get())
}
if count.Get() != 1 {
t.Errorf("Expecting 1, received %d", count.Get())
}
if p.IdleClosed() != 1 {
t.Errorf("Expecting 1, received %d", p.IdleClosed())
}
assert.EqualValues(t, 2, lastID.Get())
assert.EqualValues(t, 1, count.Get())
assert.EqualValues(t, 1, p.IdleClosed())
p.Put(r)
r, err = p.Get(ctx)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
if lastID.Get() != 2 {
t.Errorf("Expecting 2, received %d", lastID.Get())
}
if count.Get() != 1 {
t.Errorf("Expecting 1, received %d", count.Get())
}
if p.IdleClosed() != 1 {
t.Errorf("Expecting 1, received %d", p.IdleClosed())
}
require.NoError(t, err)
assert.EqualValues(t, 2, lastID.Get())
assert.EqualValues(t, 1, count.Get())
assert.EqualValues(t, 1, p.IdleClosed())
// the idle close thread wakes up every 1/100 of the idle time, so ensure
// the timeout change applies to newly added resources
@ -565,33 +422,19 @@ func TestIdleTimeout(t *testing.T) {
p.Put(r)
time.Sleep(15 * time.Millisecond)
if lastID.Get() != 2 {
t.Errorf("Expecting 2, received %d", lastID.Get())
}
if count.Get() != 1 {
t.Errorf("Expecting 1, received %d", count.Get())
}
if p.IdleClosed() != 1 {
t.Errorf("Expecting 1, received %d", p.IdleClosed())
}
assert.EqualValues(t, 2, lastID.Get())
assert.EqualValues(t, 1, count.Get())
assert.EqualValues(t, 1, p.IdleClosed())
// Get and Put to refresh timeUsed
r, err = p.Get(ctx)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
require.NoError(t, err)
p.Put(r)
p.SetIdleTimeout(10 * time.Millisecond)
time.Sleep(15 * time.Millisecond)
if lastID.Get() != 3 {
t.Errorf("Expecting 3, received %d", lastID.Get())
}
if count.Get() != 1 {
t.Errorf("Expecting 1, received %d", count.Get())
}
if p.IdleClosed() != 2 {
t.Errorf("Expecting 2, received %d", p.IdleClosed())
}
assert.EqualValues(t, 3, lastID.Get())
assert.EqualValues(t, 1, count.Get())
assert.EqualValues(t, 2, p.IdleClosed())
}
func TestIdleTimeoutCreateFail(t *testing.T) {
@ -601,18 +444,14 @@ func TestIdleTimeoutCreateFail(t *testing.T) {
p := NewResourcePool(PoolFactory, 1, 1, 10*time.Millisecond, 0, logWait, nil, 0)
defer p.Close()
r, err := p.Get(ctx)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
// Change the factory before putting back
// to prevent race with the idle closer, who will
// try to use it.
p.factory = FailFactory
p.Put(r)
time.Sleep(15 * time.Millisecond)
if p.Active() != 0 {
t.Errorf("p.Active(): %d, want 0", p.Active())
}
assert.Zero(t, p.Active())
}
func TestCreateFail(t *testing.T) {
@ -626,9 +465,7 @@ func TestCreateFail(t *testing.T) {
}
stats := p.StatsJSON()
expected := `{"Capacity": 5, "Available": 5, "Active": 0, "InUse": 0, "MaxCapacity": 5, "WaitCount": 0, "WaitTime": 0, "IdleTimeout": 1000000000, "IdleClosed": 0, "Exhausted": 0}`
if stats != expected {
t.Errorf(`expecting '%s', received '%s'`, expected, stats)
}
assert.Equal(t, expected, stats)
}
func TestCreateFailOnPut(t *testing.T) {
@ -638,14 +475,10 @@ func TestCreateFailOnPut(t *testing.T) {
p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait, nil, 0)
defer p.Close()
_, err := p.Get(ctx)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
p.factory = FailFactory
p.Put(nil)
if p.Active() != 0 {
t.Errorf("p.Active(): %d, want 0", p.Active())
}
assert.Zero(t, p.Active())
}
func TestSlowCreateFail(t *testing.T) {
@ -665,9 +498,7 @@ func TestSlowCreateFail(t *testing.T) {
for i := 0; i < 3; i++ {
<-ch
}
if p.Available() != 2 {
t.Errorf("Expecting 2, received %d", p.Available())
}
assert.EqualValues(t, 2, p.Available())
}
func TestTimeout(t *testing.T) {
@ -677,16 +508,11 @@ func TestTimeout(t *testing.T) {
p := NewResourcePool(PoolFactory, 1, 1, time.Second, 0, logWait, nil, 0)
defer p.Close()
r, err := p.Get(ctx)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
newctx, cancel := context.WithTimeout(ctx, 1*time.Millisecond)
_, err = p.Get(newctx)
cancel()
want := "resource pool timed out"
if err == nil || err.Error() != want {
t.Errorf("got %v, want %s", err, want)
}
assert.EqualError(t, err, "resource pool timed out")
p.Put(r)
}
@ -701,8 +527,5 @@ func TestExpired(t *testing.T) {
p.Put(r)
}
cancel()
want := "resource pool context already expired"
if err == nil || err.Error() != want {
t.Errorf("got %v, want %s", err, want)
}
assert.EqualError(t, err, "resource pool context already expired")
}