Documentation
¶
Overview ¶
Package connectionpool is a pure-Go (no cgo) reimplementation of Ruby's connection_pool gem (v2, MRI 4.0.5) — a generic, thread-safe pool of reusable connections. It mirrors the gem's observable behaviour — lazy creation up to a fixed size, a timeout-bounded checkout that raises ConnectionPool::TimeoutError on exhaustion, per-caller reentrant checkout, Shutdown/Reload disposal, and the method-delegating Wrapper — without any Ruby runtime. It is the connection_pool backend for go-embedded-ruby but is a standalone, reusable module.
Connections are opaque ¶
A "connection" is any value the Factory returns; the pool never inspects it. Redis clients, database handles, or plain Go structs all work. The pool only tracks ownership and lifetime.
Reentrancy — the caller-key seam ¶
The gem stores the checked-out connection in thread-local storage, so a nested `with` on the same thread reuses the same connection (and a depth counter decides when it truly returns to the pool). Go has no thread-locals, so the host identifies the logical caller with a CallerKey: two checkouts sharing a key nest exactly as two nested `with` blocks on one Ruby thread do. The go-embedded-ruby binding supplies the current Ruby thread/fiber as the key.
Index ¶
- type CallerKey
- type ConnectionPool
- func (p *ConnectionPool) Available() int
- func (p *ConnectionPool) Checkin(key CallerKey) error
- func (p *ConnectionPool) Checkout(key CallerKey, timeout time.Duration) (any, error)
- func (p *ConnectionPool) Reload(block func(conn any))
- func (p *ConnectionPool) Shutdown(block func(conn any))
- func (p *ConnectionPool) Size() int
- func (p *ConnectionPool) With(key CallerKey, fn func(conn any) (any, error)) (any, error)
- type Dispatch
- type Error
- type Factory
- type KeyFunc
- type PoolShuttingDownError
- type TimedStack
- type TimeoutError
- type Wrapper
- func (w *Wrapper) Call(name string, args ...any) (any, error)
- func (w *Wrapper) PoolAvailable() int
- func (w *Wrapper) PoolShutdown(block func(conn any))
- func (w *Wrapper) PoolSize() int
- func (w *Wrapper) RespondTo(name string, respond func(conn any) bool) (bool, error)
- func (w *Wrapper) With(fn func(conn any) (any, error)) (any, error)
- func (w *Wrapper) WrappedPool() *ConnectionPool
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CallerKey ¶
type CallerKey any
CallerKey identifies the logical caller (a Ruby thread or fiber) for reentrant checkout. Any comparable value works. Two Checkout calls with an equal key nest — the second reuses the first's connection — and the connection returns to the pool only when the outermost Checkin runs. It stands in for the gem's per-thread storage.
type ConnectionPool ¶
type ConnectionPool struct {
// contains filtered or unexported fields
}
ConnectionPool is a thread-safe pool of reusable connections. The zero value is not usable; construct it with New.
func New ¶
func New(size int, timeout time.Duration, factory Factory) *ConnectionPool
New builds a pool that lazily creates at most size connections through factory and blocks up to timeout in Checkout waiting for a free one. It mirrors ConnectionPool.new(size:, timeout:, &factory).
func (*ConnectionPool) Available ¶
func (p *ConnectionPool) Available() int
Available reports how many connections can be checked out right now without waiting (idle plus not-yet-created). Mirrors ConnectionPool#available.
func (*ConnectionPool) Checkin ¶
func (p *ConnectionPool) Checkin(key CallerKey) error
Checkin returns key's connection to the pool. On a nested checkout it only unwinds one level of the reentrancy counter; the connection returns to the pool when the outermost Checkin runs. It returns an *Error if key holds no connection. Mirrors ConnectionPool#checkin.
func (*ConnectionPool) Checkout ¶
Checkout returns a connection for key, blocking up to timeout. If key already holds a connection (a nested checkout on the same caller), it returns that same connection and deepens the reentrancy counter instead of taking another from the pool. Mirrors ConnectionPool#checkout.
func (*ConnectionPool) Reload ¶
func (p *ConnectionPool) Reload(block func(conn any))
Reload disposes every idle connection through block and resets the pool for reuse, so it will create fresh connections on demand again. Mirrors ConnectionPool#reload.
func (*ConnectionPool) Shutdown ¶
func (p *ConnectionPool) Shutdown(block func(conn any))
Shutdown disposes every idle connection through block and marks the pool shut down, so later checkouts fail and connections still checked out are disposed when returned. block must not be nil. Mirrors ConnectionPool#shutdown.
func (*ConnectionPool) Size ¶
func (p *ConnectionPool) Size() int
Size reports the maximum number of connections the pool will create. Mirrors ConnectionPool#size.
func (*ConnectionPool) With ¶
With checks out a connection for key, runs fn with it, and checks it back in even if fn panics — the faithful analogue of ConnectionPool#with's ensure. It returns fn's result, or the *TimeoutError / *PoolShuttingDownError from an unsuccessful checkout (in which case fn does not run).
type Dispatch ¶
Dispatch invokes method name with args on a connection and returns the result. It is the seam through which Wrapper delegates arbitrary calls to a pooled connection: the go-embedded-ruby binding plugs in Ruby's send, so a Wrapper behaves like the connection itself. Mirrors the connection.send(...) inside ConnectionPool::Wrapper#method_missing.
type Error ¶
type Error struct{ Msg string }
Error is the base error type for the pool, mirroring ConnectionPool::Error (a RuntimeError subclass in the gem). Checkin on a caller that holds no connection raises it.
type Factory ¶
type Factory func() any
Factory creates a new connection. It mirrors the block passed to ConnectionPool.new / TimedStack.new: it is invoked lazily, at most Size times over the life of the stack, the first time a caller needs a connection that is not already parked in the stack.
type KeyFunc ¶
type KeyFunc func() CallerKey
KeyFunc yields the CallerKey of the current logical caller. Wrapper calls it per delegation, exactly as the gem reads Thread.current inside pool.with.
type PoolShuttingDownError ¶
type PoolShuttingDownError struct{}
PoolShuttingDownError mirrors ConnectionPool::PoolShuttingDownError. It is returned by Checkout when the pool has been shut down (a ConnectionPool::Error subclass in the gem).
func (*PoolShuttingDownError) Error ¶
func (e *PoolShuttingDownError) Error() string
type TimedStack ¶
type TimedStack struct {
// contains filtered or unexported fields
}
TimedStack is a bounded, lazily-populated stack of connections with a timeout-bounded Pop. It is a faithful port of ConnectionPool::TimedStack: at most max connections are ever created (created on demand by the factory), Pop blocks up to the timeout waiting for a free connection and returns a *TimeoutError on exhaustion, and Shutdown disposes every connection and makes further Pops return a *PoolShuttingDownError.
func NewTimedStack ¶
func NewTimedStack(size int, factory Factory) *TimedStack
NewTimedStack builds a stack that will create at most size connections through factory. A size of zero means the stack never creates a connection, so every Pop that finds the stack empty times out — matching the gem.
func (*TimedStack) Length ¶
func (s *TimedStack) Length() int
Length reports how many connections could still be handed out without waiting: the parked ones plus those not yet created. Mirrors TimedStack#length, which backs ConnectionPool#available.
func (*TimedStack) Max ¶
func (s *TimedStack) Max() int
Max reports the maximum number of connections the stack will create. Mirrors ConnectionPool#size.
func (*TimedStack) Pop ¶
func (s *TimedStack) Pop(timeout time.Duration) (any, error)
Pop returns a connection, blocking up to timeout for one to become available. It returns a parked connection if any, otherwise creates a fresh one while the stack is below capacity, otherwise waits. It returns a *PoolShuttingDownError if the stack is shutting down and a *TimeoutError once the deadline passes. Mirrors TimedStack#pop.
func (*TimedStack) Push ¶
func (s *TimedStack) Push(obj any)
Push returns obj to the stack, or hands it to the shutdown block if the stack is shutting down, then wakes a waiter. Mirrors TimedStack#push.
func (*TimedStack) Shutdown ¶
func (s *TimedStack) Shutdown(block func(conn any), reload bool)
Shutdown disposes every parked connection through block and marks the stack as shutting down so waiters wake and future Pops fail. When reload is true it instead resets the stack for reuse (created count cleared, not shutting down), mirroring TimedStack#shutdown(reload:). block must not be nil.
type TimeoutError ¶
type TimeoutError struct{ Msg string }
TimeoutError mirrors ConnectionPool::TimeoutError (a Timeout::Error subclass in the gem). It is returned by Checkout when no connection becomes available within the timeout.
func (*TimeoutError) Error ¶
func (e *TimeoutError) Error() string
type Wrapper ¶
type Wrapper struct {
// contains filtered or unexported fields
}
Wrapper delegates method calls to a connection borrowed from a pool for the duration of each call. It is a faithful port of ConnectionPool::Wrapper: every delegated call checks a connection out, invokes the method, and checks it back in. Because delegation runs through With, nested delegated calls on the same caller reuse one connection, so a Wrapper is safe to treat as if it were a single connection.
func NewWrapper ¶
func NewWrapper(pool *ConnectionPool, keyFn KeyFunc, dispatch Dispatch) *Wrapper
NewWrapper wraps pool so that Call delegates to a pooled connection. keyFn supplies the current caller key (for reentrancy) and dispatch performs the method invocation. Mirrors ConnectionPool::Wrapper.new(pool:).
func Wrap ¶
func Wrap(size int, timeout time.Duration, factory Factory, keyFn KeyFunc, dispatch Dispatch) *Wrapper
Wrap builds a fresh pool and wraps it in one call, mirroring ConnectionPool.wrap(size:, timeout:) { factory }.
func (*Wrapper) Call ¶
Call borrows a connection and invokes method name with args on it, returning the method's result. It is the analogue of ConnectionPool::Wrapper#method_missing: any call the connection understands can be made straight on the Wrapper. It returns the checkout error (a *TimeoutError / *PoolShuttingDownError) if no connection is available.
func (*Wrapper) PoolAvailable ¶
PoolAvailable reports the underlying pool's availability. Mirrors ConnectionPool::Wrapper#pool_available.
func (*Wrapper) PoolShutdown ¶
PoolShutdown disposes the underlying pool through block. Mirrors ConnectionPool::Wrapper#pool_shutdown.
func (*Wrapper) PoolSize ¶
PoolSize reports the underlying pool's size. Mirrors ConnectionPool::Wrapper#pool_size.
func (*Wrapper) RespondTo ¶
RespondTo reports whether a delegated call to name would resolve: either it is one of the Wrapper's own methods, or a borrowed connection responds to it (probed through respond). Mirrors ConnectionPool::Wrapper#respond_to?.
func (*Wrapper) With ¶
With runs fn with a borrowed connection, exactly like the pool's With but using the Wrapper's caller-key source. Mirrors ConnectionPool::Wrapper#with.
func (*Wrapper) WrappedPool ¶
func (w *Wrapper) WrappedPool() *ConnectionPool
WrappedPool returns the underlying pool. Mirrors ConnectionPool::Wrapper#wrapped_pool.
