resourcepool

package
v0.53.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package resourcepool implements the ADR-032 keyed pool of leasable, refcounted, LRU-capped, idle-evicted resources. CacheManager is the first consumer; the database and messaging managers adopt it in follow-up PRs.

A pool hands each borrower a lease (via GetOrCreate) plus an idempotent ReleaseFunc. A resource evicted (LRU, idle, or explicit Remove) while a lease is outstanding is detached immediately but its Closer runs only once the final lease is released, so an in-use resource is never closed under an active caller (the #606 race). The Closer is ALWAYS invoked outside the pool lock.

Index

Constants

This section is empty.

Variables

View Source
var ErrPoolClosed = errors.New("resourcepool: pool closed")

ErrPoolClosed is returned by GetOrCreate after Close has been called. Callers can errors.Is(err, ErrPoolClosed) to distinguish "pool is gone" from a per-resource creation failure.

Functions

This section is empty.

Types

type Closer

type Closer[V any] func(v V) error

Closer releases the underlying resource. It is always invoked OUTSIDE the pool lock and exactly once per resource. A non-nil return increments the pool's error counter on the lifecycle paths that track close failures (final lease release, idle cleanup, and Close); the create-time eviction and closed-pool cleanup paths discard it.

type EntrySnapshot

type EntrySnapshot struct {
	Key      string
	LastUsed time.Time
}

EntrySnapshot is a point-in-time view of one pooled entry's identity and idle age, for managers that surface per-resource detail in Stats (e.g. DbManager's "connections" array).

type Pool

type Pool[V any] struct {
	// contains filtered or unexported fields
}

Pool is a keyed pool of leasable, refcounted, LRU-capped, idle-evicted resources. The zero value is not usable; construct with New.

func New

func New[V any](maxSize int, idleTTL time.Duration, closer Closer[V]) *Pool[V]

New creates a pool with the given capacity, idle timeout, and Closer. The Closer is required; a nil Closer panics on the first close. maxSize <= 0 means unlimited; idleTTL <= 0 disables idle cleanup.

func (*Pool[V]) Close

func (p *Pool[V]) Close() error

Close shuts down all pooled resources, stops the cleanup loop, and makes subsequent GetOrCreate calls return ErrPoolClosed. It is idempotent and returns EVERY close error joined via errors.Join (nil if none) — so consumers whose Close contract aggregates all failures (e.g. DbManager) can surface them all, while errors.Is still matches any individual error. Every close failure is also counted.

func (*Pool[V]) Closed

func (p *Pool[V]) Closed() bool

Closed reports whether Close has been called.

func (*Pool[V]) GetOrCreate

func (p *Pool[V]) GetOrCreate(ctx context.Context, key string, create func(context.Context) (V, error)) (V, ReleaseFunc, error)

GetOrCreate returns the resource for key plus a ReleaseFunc the caller must invoke when finished with it for the current unit of work (typically deferred). It creates the resource via create on first use, collapsing concurrent creates for the same key through singleflight. Returns ErrPoolClosed if Close has been called. On error the returned ReleaseFunc is nil — check err first.

func (*Pool[V]) Remove

func (p *Pool[V]) Remove(key string) (v V, shouldClose bool)

Remove detaches the entry for key from the pool. If it exists and is unleased, it marks the entry closed and returns (value, true) for the caller to close OUTSIDE the pool. If it is still leased, the close is deferred to the final lease release and Remove returns (zero, false). A missing key returns (zero, false).

func (*Pool[V]) Size

func (p *Pool[V]) Size() int

Size returns the current number of active entries.

func (*Pool[V]) Snapshot

func (p *Pool[V]) Snapshot() []EntrySnapshot

Snapshot returns a point-in-time snapshot of every live entry (key + last-used), in unspecified order. Observability/Stats surfaces only — it takes no lease and does not touch LRU.

func (*Pool[V]) StartCleanup

func (p *Pool[V]) StartCleanup(interval time.Duration)

StartCleanup starts the idle-cleanup loop at the given interval. It is a no-op when the pool has no idle timeout, the interval is non-positive, the pool is closed, or a loop is already running (idempotent).

func (*Pool[V]) Stats

func (p *Pool[V]) Stats() PoolStats

Stats returns a point-in-time snapshot of the pool's counters.

func (*Pool[V]) StopCleanup

func (p *Pool[V]) StopCleanup()

StopCleanup stops a running idle-cleanup loop. It is idempotent: calling it when no loop is running is a no-op.

type PoolStats

type PoolStats struct {
	Size         int           // Current number of active entries
	MaxSize      int           // Maximum allowed active entries (0 = unlimited)
	TotalCreated int           // Total entries created since the pool started
	Evictions    int           // Total evictions due to LRU policy
	IdleCleanups int           // Total removals due to idle timeout
	Errors       int           // Total create failures and tracked close failures
	IdleTTL      time.Duration // Idle timeout duration
}

PoolStats is a point-in-time snapshot of the pool's counters. Consumers adapt it into their own stat shape (cache's typed ManagerStats, database/messaging's map[string]any).

type ReleaseFunc

type ReleaseFunc func()

ReleaseFunc releases a lease obtained from GetOrCreate. Callers must invoke it (typically deferred) when finished with the resource for the current unit of work. It is idempotent and does NOT close the shared resource; it signals this borrower is done, so a resource evicted while leased is closed only once its last lease is released. See ADR-032.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL