diff --git a/lib.go b/lib.go index fbd1173..2d06516 100644 --- a/lib.go +++ b/lib.go @@ -31,6 +31,10 @@ type Stats struct { // Put is the number of put operations during the entire life cycle of the pool. Put int + // Drop is the number of items dropped without putting them back into the pool, during the entire life + // cycle of the pool. + Drop int + // Alloc is the number of allocations executed by the pool during the entire life cycle of the pool. Alloc int @@ -56,6 +60,9 @@ const ( // PutOperation is the type of events sent after a put operation. PutOperation + // DropOperation is the type of events sent after a drop operation. + DropOperation + // AllocateOperation is the type of events sent after an allocate operation. AllocateOperation @@ -70,7 +77,13 @@ const ( AllocateError // AllEvents can be used as a mask that includes all the event types. - AllEvents = GetOperation | PutOperation | AllocateOperation | LoadOperation | FreeOperation | AllocateError + AllEvents = GetOperation | + PutOperation | + DropOperation | + AllocateOperation | + LoadOperation | + FreeOperation | + AllocateError ) // Event values are sent by the pool after various operations, if a channel is provided to send the events to. @@ -156,6 +169,10 @@ func (et EventType) String() string { s = append(s, "put") } + if et&DropOperation != 0 { + s = append(s, "drop") + } + if et&AllocateOperation != 0 { s = append(s, "allocate") } @@ -187,11 +204,12 @@ func (ev Event) String() string { // String returns the string representation of a set of statistics about the pool. func (s Stats) String() string { return fmt.Sprintf( - "idle: %d, active: %d, get: %d, put: %d, alloc: %d, load: %d, free: %d", + "idle: %d, active: %d, get: %d, put: %d, drop: %d, alloc: %d, load: %d, free: %d", s.Idle, s.Active, s.Get, s.Put, + s.Drop, s.Alloc, s.Load, s.Free, @@ -272,6 +290,15 @@ func (p Pool[R]) Put(i R) { p.pool.put(i) } +// Drop indicates to the pool that an item received by Get was dropped and will not be put back into the pool. +// +// While it's not mandatory call it when item was dropped, it has a twofold purpose. Depending on the shrinking +// algorithm, it may work more consistently, if the algorithm is notified about the change in the active items. +// The other purpose is to allow the pool to reflect these cases in the events and the stats. +func (p Pool[R]) Drop() { + p.pool.drop() +} + // Load can be used to populate the pool with items that were not allocated as the result of the Get operation. // It can be useful in scenarios where prewarming or preparing for a sudden traffic spike is necessary. If // events were configured, it triggers a LoadOperation event. diff --git a/pool.go b/pool.go index 43c6c00..5020b6b 100644 --- a/pool.go +++ b/pool.go @@ -146,6 +146,32 @@ func (p pool[R]) put(r R) { } } +func (p pool[R]) drop() { + s := <-p.state + defer func() { + p.state <- s + }() + + var event EventType + event |= DropOperation + s.stats.Drop++ + if s.stats.Active > 0 { + s.stats.Active-- + } + + t, f := p.options.Algo.Target(s.stats) + if t < len(s.items) { + event |= FreeOperation + s = p.freeItems(s, t) + } + + s.stats.Idle = len(s.items) + p.sendEvent(event, s.stats) + if f > 0 { + s = p.forcedCheck(s, f) + } +} + func (p pool[R]) load(i []R) { s := <-p.state defer func() { diff --git a/pool_test.go b/pool_test.go index 30e3490..2c98e43 100644 --- a/pool_test.go +++ b/pool_test.go @@ -323,4 +323,23 @@ func TestPool(t *testing.T) { t.Fatal(s) } }) + + t.Run("drop", func(t *testing.T) { + alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil } + p := pool.Make(alloc, nil, pool.Options{}) + _, err := p.Get() + if err != nil { + t.Fatal(err) + } + + s := p.Stats() + e := pool.Stats{Alloc: 1, Get: 1, Active: 1} + if s != e { + t.Fatal(s) + } + + p.Drop() + s = p.Stats() + e = pool.Stats{Alloc: 1, Get: 1, Drop: 1} + }) }