From 109e4356b9eefaf01ff537c1e1394dbf5b141722 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Wed, 26 Dec 2018 14:35:23 -0600 Subject: [PATCH] Track acquire and slow acquire count --- pool.go | 47 +++++++++++++++++++++++++++++++---------------- pool_test.go | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 17 deletions(-) diff --git a/pool.go b/pool.go index 8688cfb..fdbf283 100644 --- a/pool.go +++ b/pool.go @@ -64,7 +64,8 @@ type Pool struct { destructor Destructor maxSize int - acquireCount uint64 + acquireCount int64 + slowAcquireCount int64 closed bool } @@ -103,6 +104,8 @@ type Stat struct { acquiredResources int idleResources int maxResources int + acquireCount int64 + slowAcquireCount int64 } // TotalResource returns the total number of resources in the pool. @@ -131,11 +134,24 @@ func (s *Stat) MaxResources() int { return s.maxResources } +// AcquireCount returns the number of successful acquires from the pool. +func (s *Stat) AcquireCount() int64 { + return s.acquireCount +} + +// SlowAcquireCount returns the number of successful acquires from the pool +// that waited for a resource to be released or constructed. +func (s *Stat) SlowAcquireCount() int64 { + return s.slowAcquireCount +} + // Stat returns the current pool statistics. func (p *Pool) Stat() *Stat { p.cond.L.Lock() s := &Stat{ - maxResources: p.maxSize, + maxResources: p.maxSize, + acquireCount: p.acquireCount, + slowAcquireCount: p.slowAcquireCount, } for _, res := range p.allResources { @@ -166,6 +182,7 @@ func (p *Pool) Acquire(ctx context.Context) (*Resource, error) { } } + slowAcquire := false p.cond.L.Lock() for { @@ -176,11 +193,19 @@ func (p *Pool) Acquire(ctx context.Context) (*Resource, error) { // If a resource is available now if len(p.idleResources) > 0 { - rw := p.lockedIdleAcquire() + res := p.idleResources[len(p.idleResources)-1] + p.idleResources = p.idleResources[:len(p.idleResources)-1] + res.status = resourceStatusAcquired + p.acquireCount += 1 + if slowAcquire { + p.slowAcquireCount += 1 + } p.cond.L.Unlock() - return rw, nil + return res, nil } + slowAcquire = true + // If there is room to create a resource do so if len(p.allResources) < p.maxSize { res := &Resource{pool: p, status: resourceStatusConstructing} @@ -199,6 +224,8 @@ func (p *Pool) Acquire(ctx context.Context) (*Resource, error) { res.value = value res.status = resourceStatusAcquired + p.acquireCount += 1 + p.slowAcquireCount += 1 p.cond.L.Unlock() return res, nil } @@ -230,18 +257,6 @@ func (p *Pool) Acquire(ctx context.Context) (*Resource, error) { } } -// lockedIdleAcquire gets the top resource from p.idleResources. p.cond.L -// must already be locked. len(p.idleResources) must be > 0. -func (p *Pool) lockedIdleAcquire() *Resource { - rw := p.idleResources[len(p.idleResources)-1] - p.idleResources = p.idleResources[:len(p.idleResources)-1] - if rw.status != resourceStatusIdle { - panic("BUG: non-idle resource gotten from idleResources") - } - rw.status = resourceStatusAcquired - return rw -} - // releaseAcquiredResource returns res to the the pool. func (p *Pool) releaseAcquiredResource(res *Resource) { p.cond.L.Lock() diff --git a/pool_test.go b/pool_test.go index 77578d2..d21cf0f 100644 --- a/pool_test.go +++ b/pool_test.go @@ -261,7 +261,7 @@ func TestPoolCloseBlocksUntilAllResourcesReleasedAndClosed(t *testing.T) { assert.Equal(t, len(resources), closeCalls.Value()) } -func TestPoolStat(t *testing.T) { +func TestPoolStatResources(t *testing.T) { startWaitChan := make(chan struct{}) waitingChan := make(chan struct{}) endWaitChan := make(chan struct{}) @@ -310,6 +310,46 @@ func TestPoolStat(t *testing.T) { close(endWaitChan) } +func TestPoolStatCounters(t *testing.T) { + createFunc, _ := createCreateResourceFunc() + pool := puddle.NewPool(createFunc, stubCloseRes, 1) + defer pool.Close() + + res, err := pool.Acquire(context.Background()) + require.NoError(t, err) + res.Release() + + stat := pool.Stat() + assert.Equal(t, int64(1), stat.AcquireCount()) + assert.Equal(t, int64(1), stat.SlowAcquireCount()) + + res, err = pool.Acquire(context.Background()) + require.NoError(t, err) + res.Release() + + stat = pool.Stat() + assert.Equal(t, int64(2), stat.AcquireCount()) + assert.Equal(t, int64(1), stat.SlowAcquireCount()) + + wg := &sync.WaitGroup{} + for i := 0; i < 2; i++ { + wg.Add(1) + go func() { + res, err = pool.Acquire(context.Background()) + require.NoError(t, err) + time.Sleep(50 * time.Millisecond) + res.Release() + wg.Done() + }() + } + + wg.Wait() + + stat = pool.Stat() + assert.Equal(t, int64(4), stat.AcquireCount()) + assert.Equal(t, int64(2), stat.SlowAcquireCount()) +} + func TestResourceDestroyRemovesResourceFromPool(t *testing.T) { createFunc, _ := createCreateResourceFunc() var closeCalls Counter