1
0

add function to drop items taken from but not returned to the pool

This commit is contained in:
Arpad Ryszka 2026-03-18 21:06:25 +01:00
parent 8cbcba8458
commit cde2ec1e6c
3 changed files with 74 additions and 2 deletions

31
lib.go
View File

@ -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.

26
pool.go
View File

@ -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() {

View File

@ -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}
})
}