github.com/jackc/puddle@v1.3.0/pool_test.go (about)

     1  package puddle_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"math/rand"
     9  	"net"
    10  	"os"
    11  	"runtime"
    12  	"strconv"
    13  	"sync"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/jackc/puddle"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  type Counter struct {
    23  	mutex sync.Mutex
    24  	n     int
    25  }
    26  
    27  // Next increments the counter and returns the value
    28  func (c *Counter) Next() int {
    29  	c.mutex.Lock()
    30  	c.n += 1
    31  	n := c.n
    32  	c.mutex.Unlock()
    33  	return n
    34  }
    35  
    36  // Value returns the counter
    37  func (c *Counter) Value() int {
    38  	c.mutex.Lock()
    39  	n := c.n
    40  	c.mutex.Unlock()
    41  	return n
    42  }
    43  
    44  func createConstructor() (puddle.Constructor, *Counter) {
    45  	var c Counter
    46  	f := func(ctx context.Context) (interface{}, error) {
    47  		return c.Next(), nil
    48  	}
    49  	return f, &c
    50  }
    51  
    52  func createConstructorWithNotifierChan() (puddle.Constructor, *Counter, chan int) {
    53  	ch := make(chan int)
    54  	var c Counter
    55  	f := func(ctx context.Context) (interface{}, error) {
    56  		n := c.Next()
    57  
    58  		// Because the tests will not read from ch until after the create function f returns.
    59  		go func() { ch <- n }()
    60  
    61  		return n, nil
    62  	}
    63  	return f, &c, ch
    64  }
    65  
    66  func stubDestructor(interface{}) {}
    67  
    68  func waitForRead(ch chan int) bool {
    69  	select {
    70  	case <-ch:
    71  		return true
    72  	case <-time.NewTimer(time.Second).C:
    73  		return false
    74  	}
    75  }
    76  
    77  func TestNewPoolRequiresMaxSizeGreaterThan0(t *testing.T) {
    78  	constructor, _ := createConstructor()
    79  	assert.Panics(t, func() { puddle.NewPool(constructor, stubDestructor, -1) })
    80  	assert.Panics(t, func() { puddle.NewPool(constructor, stubDestructor, 0) })
    81  }
    82  
    83  func TestPoolAcquireCreatesResourceWhenNoneIdle(t *testing.T) {
    84  	constructor, _ := createConstructor()
    85  	pool := puddle.NewPool(constructor, stubDestructor, 10)
    86  	defer pool.Close()
    87  
    88  	res, err := pool.Acquire(context.Background())
    89  	require.NoError(t, err)
    90  	assert.Equal(t, 1, res.Value())
    91  	assert.WithinDuration(t, time.Now(), res.CreationTime(), time.Second)
    92  	res.Release()
    93  }
    94  
    95  func TestPoolAcquireDoesNotCreatesResourceWhenItWouldExceedMaxSize(t *testing.T) {
    96  	constructor, createCounter := createConstructor()
    97  	pool := puddle.NewPool(constructor, stubDestructor, 1)
    98  
    99  	wg := &sync.WaitGroup{}
   100  
   101  	for i := 0; i < 100; i++ {
   102  		wg.Add(1)
   103  		go func() {
   104  			for j := 0; j < 100; j++ {
   105  				res, err := pool.Acquire(context.Background())
   106  				assert.NoError(t, err)
   107  				assert.Equal(t, 1, res.Value())
   108  				res.Release()
   109  			}
   110  			wg.Done()
   111  		}()
   112  	}
   113  
   114  	wg.Wait()
   115  
   116  	assert.EqualValues(t, 1, createCounter.Value())
   117  	assert.EqualValues(t, 1, pool.Stat().TotalResources())
   118  }
   119  
   120  func TestPoolAcquireWithCancellableContext(t *testing.T) {
   121  	constructor, createCounter := createConstructor()
   122  	pool := puddle.NewPool(constructor, stubDestructor, 1)
   123  
   124  	wg := &sync.WaitGroup{}
   125  
   126  	for i := 0; i < 100; i++ {
   127  		wg.Add(1)
   128  		go func() {
   129  			for j := 0; j < 100; j++ {
   130  				ctx, cancel := context.WithCancel(context.Background())
   131  				res, err := pool.Acquire(ctx)
   132  				assert.NoError(t, err)
   133  				assert.Equal(t, 1, res.Value())
   134  				res.Release()
   135  				cancel()
   136  			}
   137  			wg.Done()
   138  		}()
   139  	}
   140  
   141  	wg.Wait()
   142  
   143  	assert.EqualValues(t, 1, createCounter.Value())
   144  	assert.EqualValues(t, 1, pool.Stat().TotalResources())
   145  }
   146  
   147  func TestPoolAcquireReturnsErrorFromFailedResourceCreate(t *testing.T) {
   148  	errCreateFailed := errors.New("create failed")
   149  	constructor := func(ctx context.Context) (interface{}, error) {
   150  		return nil, errCreateFailed
   151  	}
   152  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   153  
   154  	res, err := pool.Acquire(context.Background())
   155  	assert.Equal(t, errCreateFailed, err)
   156  	assert.Nil(t, res)
   157  }
   158  
   159  func TestPoolAcquireCreatesResourceRespectingContext(t *testing.T) {
   160  	var cancel func()
   161  	constructor := func(ctx context.Context) (interface{}, error) {
   162  		cancel()
   163  		// sleep to give a chance for the acquire to recognize it's cancelled
   164  		time.Sleep(10 * time.Millisecond)
   165  		return 1, nil
   166  	}
   167  	pool := puddle.NewPool(constructor, stubDestructor, 1)
   168  	defer pool.Close()
   169  
   170  	var ctx context.Context
   171  	ctx, cancel = context.WithCancel(context.Background())
   172  	defer cancel()
   173  	_, err := pool.Acquire(ctx)
   174  	assert.Equal(t, context.Canceled, err)
   175  
   176  	// wait for the constructor to sleep and then for the resource to be added back
   177  	// to the idle pool
   178  	time.Sleep(100 * time.Millisecond)
   179  
   180  	stat := pool.Stat()
   181  	assert.EqualValues(t, 1, stat.IdleResources())
   182  	assert.EqualValues(t, 1, stat.TotalResources())
   183  }
   184  
   185  func TestPoolAcquireReusesResources(t *testing.T) {
   186  	constructor, createCounter := createConstructor()
   187  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   188  
   189  	res, err := pool.Acquire(context.Background())
   190  	require.NoError(t, err)
   191  	assert.Equal(t, 1, res.Value())
   192  
   193  	res.Release()
   194  
   195  	res, err = pool.Acquire(context.Background())
   196  	require.NoError(t, err)
   197  	assert.Equal(t, 1, res.Value())
   198  
   199  	res.Release()
   200  
   201  	assert.Equal(t, 1, createCounter.Value())
   202  }
   203  
   204  func TestPoolTryAcquire(t *testing.T) {
   205  	constructor, createCounter := createConstructor()
   206  	pool := puddle.NewPool(constructor, stubDestructor, 1)
   207  
   208  	// Pool is initially empty so TryAcquire fails but starts construction of resource in the background.
   209  	res, err := pool.TryAcquire(context.Background())
   210  	require.EqualError(t, err, puddle.ErrNotAvailable.Error())
   211  	assert.Nil(t, res)
   212  
   213  	// Wait for background creation to complete.
   214  	time.Sleep(100 * time.Millisecond)
   215  
   216  	res, err = pool.TryAcquire(context.Background())
   217  	require.NoError(t, err)
   218  	assert.Equal(t, 1, res.Value())
   219  	defer res.Release()
   220  
   221  	res, err = pool.TryAcquire(context.Background())
   222  	require.EqualError(t, err, puddle.ErrNotAvailable.Error())
   223  	assert.Nil(t, res)
   224  
   225  	assert.Equal(t, 1, createCounter.Value())
   226  }
   227  
   228  func TestPoolTryAcquireReturnsErrorWhenPoolIsClosed(t *testing.T) {
   229  	constructor, _ := createConstructor()
   230  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   231  	pool.Close()
   232  
   233  	res, err := pool.TryAcquire(context.Background())
   234  	assert.Equal(t, puddle.ErrClosedPool, err)
   235  	assert.Nil(t, res)
   236  }
   237  
   238  func TestPoolTryAcquireWithFailedResourceCreate(t *testing.T) {
   239  	errCreateFailed := errors.New("create failed")
   240  	constructor := func(ctx context.Context) (interface{}, error) {
   241  		return nil, errCreateFailed
   242  	}
   243  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   244  
   245  	res, err := pool.TryAcquire(context.Background())
   246  	require.EqualError(t, err, puddle.ErrNotAvailable.Error())
   247  	assert.Nil(t, res)
   248  }
   249  
   250  func TestPoolAcquireNilContextDoesNotLeavePoolLocked(t *testing.T) {
   251  	constructor, createCounter := createConstructor()
   252  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   253  
   254  	assert.Panics(t, func() { pool.Acquire(nil) })
   255  
   256  	res, err := pool.Acquire(context.Background())
   257  	require.NoError(t, err)
   258  	assert.Equal(t, 1, res.Value())
   259  	res.Release()
   260  
   261  	assert.Equal(t, 1, createCounter.Value())
   262  }
   263  
   264  func TestPoolAcquireContextAlreadyCanceled(t *testing.T) {
   265  	constructor := func(ctx context.Context) (interface{}, error) {
   266  		panic("should never be called")
   267  	}
   268  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   269  
   270  	ctx, cancel := context.WithCancel(context.Background())
   271  	cancel()
   272  	res, err := pool.Acquire(ctx)
   273  	assert.Equal(t, context.Canceled, err)
   274  	assert.Nil(t, res)
   275  }
   276  
   277  func TestPoolAcquireContextCanceledDuringCreate(t *testing.T) {
   278  	ctx, cancel := context.WithCancel(context.Background())
   279  	time.AfterFunc(100*time.Millisecond, cancel)
   280  	timeoutChan := time.After(1 * time.Second)
   281  
   282  	var constructorCalls Counter
   283  	constructor := func(ctx context.Context) (interface{}, error) {
   284  		select {
   285  		case <-ctx.Done():
   286  			return nil, ctx.Err()
   287  		case <-timeoutChan:
   288  		}
   289  		return constructorCalls.Next(), nil
   290  	}
   291  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   292  
   293  	res, err := pool.Acquire(ctx)
   294  	assert.Equal(t, context.Canceled, err)
   295  	assert.Nil(t, res)
   296  }
   297  
   298  func TestPoolAcquireAllIdle(t *testing.T) {
   299  	constructor, _ := createConstructor()
   300  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   301  	defer pool.Close()
   302  
   303  	resources := make([]*puddle.Resource, 4)
   304  	var err error
   305  
   306  	resources[0], err = pool.Acquire(context.Background())
   307  	require.NoError(t, err)
   308  	resources[1], err = pool.Acquire(context.Background())
   309  	require.NoError(t, err)
   310  	resources[2], err = pool.Acquire(context.Background())
   311  	require.NoError(t, err)
   312  	resources[3], err = pool.Acquire(context.Background())
   313  	require.NoError(t, err)
   314  
   315  	assert.Len(t, pool.AcquireAllIdle(), 0)
   316  
   317  	resources[0].Release()
   318  	resources[3].Release()
   319  
   320  	assert.ElementsMatch(t, []*puddle.Resource{resources[0], resources[3]}, pool.AcquireAllIdle())
   321  
   322  	resources[0].Release()
   323  	resources[3].Release()
   324  	resources[1].Release()
   325  	resources[2].Release()
   326  
   327  	assert.ElementsMatch(t, resources, pool.AcquireAllIdle())
   328  
   329  	resources[0].Release()
   330  	resources[1].Release()
   331  	resources[2].Release()
   332  	resources[3].Release()
   333  }
   334  
   335  func TestPoolAcquireAllIdleWhenClosedIsNil(t *testing.T) {
   336  	constructor, _ := createConstructor()
   337  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   338  	pool.Close()
   339  	assert.Nil(t, pool.AcquireAllIdle())
   340  }
   341  
   342  func TestPoolCreateResource(t *testing.T) {
   343  	constructor, counter := createConstructor()
   344  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   345  	defer pool.Close()
   346  
   347  	var err error
   348  
   349  	err = pool.CreateResource(context.Background())
   350  	require.NoError(t, err)
   351  
   352  	stats := pool.Stat()
   353  	assert.EqualValues(t, 1, stats.IdleResources())
   354  
   355  	res, err := pool.Acquire(context.Background())
   356  	require.NoError(t, err)
   357  	assert.Equal(t, counter.Value(), res.Value())
   358  	assert.True(t, res.LastUsedNanotime() > 0, "should set LastUsedNanotime so that idle calculations can still work")
   359  	assert.Equal(t, 1, res.Value())
   360  	assert.WithinDuration(t, time.Now(), res.CreationTime(), time.Second)
   361  	res.Release()
   362  
   363  	assert.EqualValues(t, 0, pool.Stat().EmptyAcquireCount(), "should have been a warm resource")
   364  }
   365  
   366  func TestPoolCreateResourceReturnsErrorFromFailedResourceCreate(t *testing.T) {
   367  	errCreateFailed := errors.New("create failed")
   368  	constructor := func(ctx context.Context) (interface{}, error) {
   369  		return nil, errCreateFailed
   370  	}
   371  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   372  
   373  	err := pool.CreateResource(context.Background())
   374  	assert.Equal(t, errCreateFailed, err)
   375  }
   376  
   377  func TestPoolCreateResourceReturnsErrorWhenAlreadyClosed(t *testing.T) {
   378  	constructor, _ := createConstructor()
   379  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   380  	pool.Close()
   381  	err := pool.CreateResource(context.Background())
   382  	assert.Equal(t, puddle.ErrClosedPool, err)
   383  }
   384  
   385  func TestPoolCreateResourceReturnsErrorWhenClosedWhileCreatingResource(t *testing.T) {
   386  	// There is no way to guarantee the correct order of the pool being closed while the resource is being constructed.
   387  	// But these sleeps should make it extremely likely. (Ah, the lengths we go for 100% test coverage...)
   388  	constructor := func(ctx context.Context) (interface{}, error) {
   389  		time.Sleep(500 * time.Millisecond)
   390  		return "abc", nil
   391  	}
   392  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   393  
   394  	acquireErrChan := make(chan error)
   395  	go func() {
   396  		err := pool.CreateResource(context.Background())
   397  		acquireErrChan <- err
   398  	}()
   399  
   400  	time.Sleep(250 * time.Millisecond)
   401  	pool.Close()
   402  
   403  	err := <-acquireErrChan
   404  	assert.Equal(t, puddle.ErrClosedPool, err)
   405  }
   406  
   407  func TestPoolCloseClosesAllIdleResources(t *testing.T) {
   408  	constructor, _ := createConstructor()
   409  
   410  	var destructorCalls Counter
   411  	destructor := func(interface{}) {
   412  		destructorCalls.Next()
   413  	}
   414  
   415  	p := puddle.NewPool(constructor, destructor, 10)
   416  
   417  	resources := make([]*puddle.Resource, 4)
   418  	for i := range resources {
   419  		var err error
   420  		resources[i], err = p.Acquire(context.Background())
   421  		require.Nil(t, err)
   422  	}
   423  
   424  	for _, res := range resources {
   425  		res.Release()
   426  	}
   427  
   428  	p.Close()
   429  
   430  	assert.Equal(t, len(resources), destructorCalls.Value())
   431  }
   432  
   433  func TestPoolCloseBlocksUntilAllResourcesReleasedAndClosed(t *testing.T) {
   434  	constructor, _ := createConstructor()
   435  	var destructorCalls Counter
   436  	destructor := func(interface{}) {
   437  		destructorCalls.Next()
   438  	}
   439  
   440  	p := puddle.NewPool(constructor, destructor, 10)
   441  
   442  	resources := make([]*puddle.Resource, 4)
   443  	for i := range resources {
   444  		var err error
   445  		resources[i], err = p.Acquire(context.Background())
   446  		require.Nil(t, err)
   447  	}
   448  
   449  	for _, res := range resources {
   450  		go func(res *puddle.Resource) {
   451  			time.Sleep(100 * time.Millisecond)
   452  			res.Release()
   453  		}(res)
   454  	}
   455  
   456  	p.Close()
   457  	assert.Equal(t, len(resources), destructorCalls.Value())
   458  }
   459  
   460  func TestPoolCloseIsSafeToCallMultipleTimes(t *testing.T) {
   461  	constructor, _ := createConstructor()
   462  
   463  	p := puddle.NewPool(constructor, stubDestructor, 10)
   464  
   465  	p.Close()
   466  	p.Close()
   467  }
   468  
   469  func TestPoolStatResources(t *testing.T) {
   470  	startWaitChan := make(chan struct{})
   471  	waitingChan := make(chan struct{})
   472  	endWaitChan := make(chan struct{})
   473  
   474  	var constructorCalls Counter
   475  	constructor := func(ctx context.Context) (interface{}, error) {
   476  		select {
   477  		case <-startWaitChan:
   478  			close(waitingChan)
   479  			<-endWaitChan
   480  		default:
   481  		}
   482  
   483  		return constructorCalls.Next(), nil
   484  	}
   485  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   486  	defer pool.Close()
   487  
   488  	resAcquired, err := pool.Acquire(context.Background())
   489  	require.Nil(t, err)
   490  
   491  	close(startWaitChan)
   492  	go func() {
   493  		res, err := pool.Acquire(context.Background())
   494  		require.Nil(t, err)
   495  		res.Release()
   496  	}()
   497  	<-waitingChan
   498  	stat := pool.Stat()
   499  
   500  	assert.EqualValues(t, 2, stat.TotalResources())
   501  	assert.EqualValues(t, 1, stat.ConstructingResources())
   502  	assert.EqualValues(t, 1, stat.AcquiredResources())
   503  	assert.EqualValues(t, 0, stat.IdleResources())
   504  	assert.EqualValues(t, 10, stat.MaxResources())
   505  
   506  	resAcquired.Release()
   507  
   508  	stat = pool.Stat()
   509  	assert.EqualValues(t, 2, stat.TotalResources())
   510  	assert.EqualValues(t, 1, stat.ConstructingResources())
   511  	assert.EqualValues(t, 0, stat.AcquiredResources())
   512  	assert.EqualValues(t, 1, stat.IdleResources())
   513  	assert.EqualValues(t, 10, stat.MaxResources())
   514  
   515  	close(endWaitChan)
   516  }
   517  
   518  func TestPoolStatSuccessfulAcquireCounters(t *testing.T) {
   519  	constructor, _ := createConstructor()
   520  	sleepConstructor := func(ctx context.Context) (interface{}, error) {
   521  		// sleep to make sure we don't fail the AcquireDuration test
   522  		time.Sleep(time.Nanosecond)
   523  		return constructor(ctx)
   524  	}
   525  	pool := puddle.NewPool(sleepConstructor, stubDestructor, 1)
   526  	defer pool.Close()
   527  
   528  	res, err := pool.Acquire(context.Background())
   529  	require.NoError(t, err)
   530  	res.Release()
   531  
   532  	stat := pool.Stat()
   533  	assert.Equal(t, int64(1), stat.AcquireCount())
   534  	assert.Equal(t, int64(1), stat.EmptyAcquireCount())
   535  	assert.True(t, stat.AcquireDuration() > 0, "expected stat.AcquireDuration() > 0 but %v", stat.AcquireDuration())
   536  	lastAcquireDuration := stat.AcquireDuration()
   537  
   538  	res, err = pool.Acquire(context.Background())
   539  	require.NoError(t, err)
   540  	res.Release()
   541  
   542  	stat = pool.Stat()
   543  	assert.Equal(t, int64(2), stat.AcquireCount())
   544  	assert.Equal(t, int64(1), stat.EmptyAcquireCount())
   545  	assert.True(t, stat.AcquireDuration() > lastAcquireDuration)
   546  	lastAcquireDuration = stat.AcquireDuration()
   547  
   548  	wg := &sync.WaitGroup{}
   549  	for i := 0; i < 2; i++ {
   550  		wg.Add(1)
   551  		go func() {
   552  			res, err = pool.Acquire(context.Background())
   553  			require.NoError(t, err)
   554  			time.Sleep(50 * time.Millisecond)
   555  			res.Release()
   556  			wg.Done()
   557  		}()
   558  	}
   559  
   560  	wg.Wait()
   561  
   562  	stat = pool.Stat()
   563  	assert.Equal(t, int64(4), stat.AcquireCount())
   564  	assert.Equal(t, int64(2), stat.EmptyAcquireCount())
   565  	assert.True(t, stat.AcquireDuration() > lastAcquireDuration)
   566  	lastAcquireDuration = stat.AcquireDuration()
   567  }
   568  
   569  func TestPoolStatCanceledAcquireBeforeStart(t *testing.T) {
   570  	constructor, _ := createConstructor()
   571  	pool := puddle.NewPool(constructor, stubDestructor, 1)
   572  	defer pool.Close()
   573  
   574  	ctx, cancel := context.WithCancel(context.Background())
   575  	cancel()
   576  	_, err := pool.Acquire(ctx)
   577  	require.Equal(t, context.Canceled, err)
   578  
   579  	stat := pool.Stat()
   580  	assert.Equal(t, int64(0), stat.AcquireCount())
   581  	assert.Equal(t, int64(1), stat.CanceledAcquireCount())
   582  }
   583  
   584  func TestPoolStatCanceledAcquireDuringCreate(t *testing.T) {
   585  	constructor := func(ctx context.Context) (interface{}, error) {
   586  		<-ctx.Done()
   587  		return nil, ctx.Err()
   588  	}
   589  
   590  	pool := puddle.NewPool(constructor, stubDestructor, 1)
   591  	defer pool.Close()
   592  
   593  	ctx, cancel := context.WithCancel(context.Background())
   594  	time.AfterFunc(50*time.Millisecond, cancel)
   595  	_, err := pool.Acquire(ctx)
   596  	require.Equal(t, context.Canceled, err)
   597  
   598  	// sleep to give the constructor goroutine time to mark cancelled
   599  	time.Sleep(10 * time.Millisecond)
   600  
   601  	stat := pool.Stat()
   602  	assert.Equal(t, int64(0), stat.AcquireCount())
   603  	assert.Equal(t, int64(1), stat.CanceledAcquireCount())
   604  }
   605  
   606  func TestPoolStatCanceledAcquireDuringWait(t *testing.T) {
   607  	constructor, _ := createConstructor()
   608  	pool := puddle.NewPool(constructor, stubDestructor, 1)
   609  	defer pool.Close()
   610  
   611  	res, err := pool.Acquire(context.Background())
   612  	require.Nil(t, err)
   613  
   614  	ctx, cancel := context.WithCancel(context.Background())
   615  	time.AfterFunc(50*time.Millisecond, cancel)
   616  	_, err = pool.Acquire(ctx)
   617  	require.Equal(t, context.Canceled, err)
   618  
   619  	res.Release()
   620  
   621  	stat := pool.Stat()
   622  	assert.Equal(t, int64(1), stat.AcquireCount())
   623  	assert.Equal(t, int64(1), stat.CanceledAcquireCount())
   624  }
   625  
   626  func TestResourceHijackRemovesResourceFromPoolButDoesNotDestroy(t *testing.T) {
   627  	constructor, _ := createConstructor()
   628  	var destructorCalls Counter
   629  	destructor := func(interface{}) {
   630  		destructorCalls.Next()
   631  	}
   632  
   633  	pool := puddle.NewPool(constructor, destructor, 10)
   634  
   635  	res, err := pool.Acquire(context.Background())
   636  	require.NoError(t, err)
   637  	assert.Equal(t, 1, res.Value())
   638  
   639  	res.Hijack()
   640  
   641  	assert.EqualValues(t, 0, pool.Stat().TotalResources())
   642  	assert.EqualValues(t, 0, destructorCalls.Value())
   643  
   644  	// Can still call Value, CreationTime and IdleDuration
   645  	res.Value()
   646  	res.CreationTime()
   647  	res.IdleDuration()
   648  }
   649  
   650  func TestResourceDestroyRemovesResourceFromPool(t *testing.T) {
   651  	constructor, _ := createConstructor()
   652  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   653  
   654  	res, err := pool.Acquire(context.Background())
   655  	require.NoError(t, err)
   656  	assert.Equal(t, 1, res.Value())
   657  
   658  	assert.EqualValues(t, 1, pool.Stat().TotalResources())
   659  	res.Destroy()
   660  	for i := 0; i < 1000; i++ {
   661  		if pool.Stat().TotalResources() == 0 {
   662  			break
   663  		}
   664  		time.Sleep(time.Millisecond)
   665  	}
   666  
   667  	assert.EqualValues(t, 0, pool.Stat().TotalResources())
   668  }
   669  
   670  func TestResourceLastUsageTimeTracking(t *testing.T) {
   671  	constructor, _ := createConstructor()
   672  	pool := puddle.NewPool(constructor, stubDestructor, 1)
   673  
   674  	res, err := pool.Acquire(context.Background())
   675  	require.NoError(t, err)
   676  	t1 := res.LastUsedNanotime()
   677  	res.Release()
   678  
   679  	// Greater than zero after initial usage
   680  	res, err = pool.Acquire(context.Background())
   681  	require.NoError(t, err)
   682  	t2 := res.LastUsedNanotime()
   683  	d2 := res.IdleDuration()
   684  	assert.True(t, t2 > t1)
   685  	res.ReleaseUnused()
   686  
   687  	// ReleaseUnused does not update usage tracking
   688  	res, err = pool.Acquire(context.Background())
   689  	require.NoError(t, err)
   690  	t3 := res.LastUsedNanotime()
   691  	d3 := res.IdleDuration()
   692  	assert.EqualValues(t, t2, t3)
   693  	assert.True(t, d3 > d2)
   694  	res.Release()
   695  
   696  	// Release does update usage tracking
   697  	res, err = pool.Acquire(context.Background())
   698  	require.NoError(t, err)
   699  	t4 := res.LastUsedNanotime()
   700  	assert.True(t, t4 > t3)
   701  	res.Release()
   702  }
   703  
   704  func TestResourcePanicsOnUsageWhenNotAcquired(t *testing.T) {
   705  	constructor, _ := createConstructor()
   706  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   707  
   708  	res, err := pool.Acquire(context.Background())
   709  	require.NoError(t, err)
   710  	res.Release()
   711  
   712  	assert.PanicsWithValue(t, "tried to release resource that is not acquired", res.Release)
   713  	assert.PanicsWithValue(t, "tried to release resource that is not acquired", res.ReleaseUnused)
   714  	assert.PanicsWithValue(t, "tried to destroy resource that is not acquired", res.Destroy)
   715  	assert.PanicsWithValue(t, "tried to hijack resource that is not acquired", res.Hijack)
   716  	assert.PanicsWithValue(t, "tried to access resource that is not acquired or hijacked", func() { res.Value() })
   717  	assert.PanicsWithValue(t, "tried to access resource that is not acquired or hijacked", func() { res.CreationTime() })
   718  	assert.PanicsWithValue(t, "tried to access resource that is not acquired or hijacked", func() { res.LastUsedNanotime() })
   719  	assert.PanicsWithValue(t, "tried to access resource that is not acquired or hijacked", func() { res.IdleDuration() })
   720  }
   721  
   722  func TestPoolAcquireReturnsErrorWhenPoolIsClosed(t *testing.T) {
   723  	constructor, _ := createConstructor()
   724  	pool := puddle.NewPool(constructor, stubDestructor, 10)
   725  	pool.Close()
   726  
   727  	res, err := pool.Acquire(context.Background())
   728  	assert.Equal(t, puddle.ErrClosedPool, err)
   729  	assert.Nil(t, res)
   730  }
   731  
   732  func TestSignalIsSentWhenResourceFailedToCreate(t *testing.T) {
   733  	var c Counter
   734  	constructor := func(context.Context) (a interface{}, err error) {
   735  		if c.Next() == 2 {
   736  			return nil, errors.New("outage")
   737  		}
   738  		return 1, nil
   739  	}
   740  	destructor := func(value interface{}) {}
   741  
   742  	pool := puddle.NewPool(constructor, destructor, 1)
   743  
   744  	res1, err := pool.Acquire(context.Background())
   745  	require.NoError(t, err)
   746  
   747  	var wg sync.WaitGroup
   748  	for i := 0; i < 2; i++ {
   749  		wg.Add(1)
   750  		go func(name string) {
   751  			defer wg.Done()
   752  			_, _ = pool.Acquire(context.Background())
   753  		}(strconv.Itoa(i))
   754  	}
   755  
   756  	// ensure that both goroutines above are waiting for condition variable signal
   757  	time.Sleep(500 * time.Millisecond)
   758  	res1.Destroy()
   759  	wg.Wait()
   760  }
   761  
   762  func TestStress(t *testing.T) {
   763  	constructor, _ := createConstructor()
   764  	var destructorCalls Counter
   765  	destructor := func(interface{}) {
   766  		destructorCalls.Next()
   767  	}
   768  
   769  	poolSize := runtime.NumCPU()
   770  	if poolSize < 4 {
   771  		poolSize = 4
   772  	}
   773  
   774  	pool := puddle.NewPool(constructor, destructor, int32(poolSize))
   775  
   776  	finishChan := make(chan struct{})
   777  	wg := &sync.WaitGroup{}
   778  
   779  	releaseOrDestroyOrHijack := func(res *puddle.Resource) {
   780  		n := rand.Intn(100)
   781  		if n < 5 {
   782  			res.Hijack()
   783  			destructor(res)
   784  		} else if n < 10 {
   785  			res.Destroy()
   786  		} else {
   787  			res.Release()
   788  		}
   789  	}
   790  
   791  	actions := []func(){
   792  		// Acquire
   793  		func() {
   794  			res, err := pool.Acquire(context.Background())
   795  			if err != nil {
   796  				if err != puddle.ErrClosedPool {
   797  					assert.Failf(t, "stress acquire", "pool.Acquire returned unexpected err: %v", err)
   798  				}
   799  				return
   800  			}
   801  
   802  			time.Sleep(time.Duration(rand.Int63n(100)) * time.Millisecond)
   803  			releaseOrDestroyOrHijack(res)
   804  		},
   805  		// Acquire possibly canceled by context
   806  		func() {
   807  			ctx, cancel := context.WithTimeout(context.Background(), time.Duration(rand.Int63n(2000))*time.Nanosecond)
   808  			defer cancel()
   809  			res, err := pool.Acquire(ctx)
   810  			if err != nil {
   811  				if err != puddle.ErrClosedPool && err != context.Canceled && err != context.DeadlineExceeded {
   812  					assert.Failf(t, "stress acquire possibly canceled by context", "pool.Acquire returned unexpected err: %v", err)
   813  				}
   814  				return
   815  			}
   816  
   817  			time.Sleep(time.Duration(rand.Int63n(2000)) * time.Nanosecond)
   818  			releaseOrDestroyOrHijack(res)
   819  		},
   820  		// TryAcquire
   821  		func() {
   822  			res, err := pool.TryAcquire(context.Background())
   823  			if err != nil {
   824  				if err != puddle.ErrClosedPool && err != puddle.ErrNotAvailable {
   825  					assert.Failf(t, "stress TryAcquire", "pool.TryAcquire returned unexpected err: %v", err)
   826  				}
   827  				return
   828  			}
   829  
   830  			time.Sleep(time.Duration(rand.Int63n(100)) * time.Millisecond)
   831  			releaseOrDestroyOrHijack(res)
   832  		},
   833  		// AcquireAllIdle (though under heavy load this will almost certainly always get an empty slice)
   834  		func() {
   835  			resources := pool.AcquireAllIdle()
   836  			for _, res := range resources {
   837  				res.Release()
   838  			}
   839  		},
   840  	}
   841  
   842  	workerCount := int(poolSize) * 2
   843  
   844  	for i := 0; i < workerCount; i++ {
   845  		wg.Add(1)
   846  		go func() {
   847  			for {
   848  				select {
   849  				case <-finishChan:
   850  					wg.Done()
   851  					return
   852  				default:
   853  				}
   854  
   855  				actions[rand.Intn(len(actions))]()
   856  			}
   857  		}()
   858  	}
   859  
   860  	s := os.Getenv("STRESS_TEST_DURATION")
   861  	if s == "" {
   862  		s = "1s"
   863  	}
   864  	testDuration, err := time.ParseDuration(s)
   865  	require.Nil(t, err)
   866  	time.AfterFunc(testDuration, func() { close(finishChan) })
   867  	wg.Wait()
   868  
   869  	pool.Close()
   870  }
   871  
   872  func startAcceptOnceDummyServer(laddr string) {
   873  	ln, err := net.Listen("tcp", laddr)
   874  	if err != nil {
   875  		log.Fatalln("Listen:", err)
   876  	}
   877  
   878  	// Listen one time
   879  	go func() {
   880  		conn, err := ln.Accept()
   881  		if err != nil {
   882  			log.Fatalln("Accept:", err)
   883  		}
   884  
   885  		for {
   886  			buf := make([]byte, 1)
   887  			_, err := conn.Read(buf)
   888  			if err != nil {
   889  				return
   890  			}
   891  		}
   892  	}()
   893  
   894  }
   895  
   896  func ExamplePool() {
   897  	// Dummy server
   898  	laddr := "127.0.0.1:8080"
   899  	startAcceptOnceDummyServer(laddr)
   900  
   901  	// Pool creation
   902  	constructor := func(context.Context) (interface{}, error) {
   903  		return net.Dial("tcp", laddr)
   904  	}
   905  	destructor := func(value interface{}) {
   906  		value.(net.Conn).Close()
   907  	}
   908  	maxPoolSize := int32(10)
   909  
   910  	pool := puddle.NewPool(constructor, destructor, maxPoolSize)
   911  
   912  	// Use pool multiple times
   913  	for i := 0; i < 10; i++ {
   914  		// Acquire resource
   915  		res, err := pool.Acquire(context.Background())
   916  		if err != nil {
   917  			log.Fatalln("Acquire", err)
   918  		}
   919  
   920  		// Type-assert value and use
   921  		_, err = res.Value().(net.Conn).Write([]byte{1})
   922  		if err != nil {
   923  			log.Fatalln("Write", err)
   924  		}
   925  
   926  		// Release when done.
   927  		res.Release()
   928  	}
   929  
   930  	stats := pool.Stat()
   931  	pool.Close()
   932  
   933  	fmt.Println("Connections:", stats.TotalResources())
   934  	fmt.Println("Acquires:", stats.AcquireCount())
   935  	// Output:
   936  	// Connections: 1
   937  	// Acquires: 10
   938  }
   939  
   940  func BenchmarkPoolAcquireAndRelease(b *testing.B) {
   941  	benchmarks := []struct {
   942  		poolSize    int32
   943  		clientCount int
   944  		cancellable bool
   945  	}{
   946  		{8, 1, false},
   947  		{8, 2, false},
   948  		{8, 8, false},
   949  		{8, 32, false},
   950  		{8, 128, false},
   951  		{8, 512, false},
   952  		{8, 2048, false},
   953  		{8, 8192, false},
   954  
   955  		{64, 2, false},
   956  		{64, 8, false},
   957  		{64, 32, false},
   958  		{64, 128, false},
   959  		{64, 512, false},
   960  		{64, 2048, false},
   961  		{64, 8192, false},
   962  
   963  		{512, 2, false},
   964  		{512, 8, false},
   965  		{512, 32, false},
   966  		{512, 128, false},
   967  		{512, 512, false},
   968  		{512, 2048, false},
   969  		{512, 8192, false},
   970  
   971  		{8, 2, true},
   972  		{8, 8, true},
   973  		{8, 32, true},
   974  		{8, 128, true},
   975  		{8, 512, true},
   976  		{8, 2048, true},
   977  		{8, 8192, true},
   978  
   979  		{64, 2, true},
   980  		{64, 8, true},
   981  		{64, 32, true},
   982  		{64, 128, true},
   983  		{64, 512, true},
   984  		{64, 2048, true},
   985  		{64, 8192, true},
   986  
   987  		{512, 2, true},
   988  		{512, 8, true},
   989  		{512, 32, true},
   990  		{512, 128, true},
   991  		{512, 512, true},
   992  		{512, 2048, true},
   993  		{512, 8192, true},
   994  	}
   995  
   996  	for _, bm := range benchmarks {
   997  		name := fmt.Sprintf("PoolSize=%d/ClientCount=%d/Cancellable=%v", bm.poolSize, bm.clientCount, bm.cancellable)
   998  
   999  		b.Run(name, func(b *testing.B) {
  1000  			ctx := context.Background()
  1001  			cancel := func() {}
  1002  			if bm.cancellable {
  1003  				ctx, cancel = context.WithCancel(ctx)
  1004  			}
  1005  
  1006  			wg := &sync.WaitGroup{}
  1007  
  1008  			constructor, _ := createConstructor()
  1009  			pool := puddle.NewPool(constructor, stubDestructor, bm.poolSize)
  1010  
  1011  			for i := 0; i < bm.clientCount; i++ {
  1012  				wg.Add(1)
  1013  				go func() {
  1014  					defer wg.Done()
  1015  
  1016  					for j := 0; j < b.N; j++ {
  1017  						res, err := pool.Acquire(ctx)
  1018  						if err != nil {
  1019  							b.Fatal(err)
  1020  						}
  1021  						res.Release()
  1022  					}
  1023  				}()
  1024  			}
  1025  
  1026  			wg.Wait()
  1027  			cancel()
  1028  		})
  1029  	}
  1030  }