Package · cpool

API reference

Six types, all in the Flytachi\Winter\CPool namespace. Signatures match the source; for each item you get the arguments, the return value and any non-obvious semantics.

Overview

Type Kind Role
ConnectionFactory interface the driver adapter — the single extension point
ConnectionPool class the pool: borrow, release, evict
SingleConnection class the same meaning for a runtime without concurrency
PoolEntry DTO a borrowed connection and its timestamps
PoolPolicy value the settings
PoolException exception the package’s only exception

ConnectionFactory (interface)

php
public function create(): object;
public function validate(object $connection): bool;
public function close(object $connection): void;
Method Description
create() Open a connection. May throw — the pool wraps it in PoolException::connectFailed()
validate($c) A cheap liveness probe. false → the pool evicts and opens a new one. A thrown exception counts as death too
close($c) Close it. The pool ignores errors

In detail — in Writing an adapter.


ConnectionPool (final class)

php
new ConnectionPool(
  ConnectionFactory $factory,
  PoolPolicy        $policy = new PoolPolicy(),
  ?Closure          $clock = null,
)

$clock substitutes the time source, for tests; production code does not need it.

Method Returns Description
borrow() PoolEntry Take a live connection. Below the cap it creates one; at the cap it waits. Dead and expired ones are discarded and the next is tried, until connectionTimeout — the deadline for the whole borrow — runs out. Throws exhausted() (all busy), connectFailed() (could not open), unusable() (the window went on dead connections)
release($entry) void Return it to the pool. The timestamp is refreshed, the probe is not run
evict($entry) void Evict and close — for when the caller knows the connection is broken
stats() array ['total', 'idle', 'active', 'maximum']
close() void Close every connection and disarm the housekeeping timer
abandon() void Forget every connection without closing and disarm the timer — the fork-safe counterpart of close()

Releasing is mandatory

The pool does not track connections that were never returned: every leak permanently shrinks it by one. Wrap borrow() in try/finally, or build a facade that releases at the end of the unit of work.

evict() versus release()

release() puts the connection back into circulation as is. If you know it is broken — the driver reported a disconnect — returning it means handing a corpse to the next borrower. That is what evict() is for.


SingleConnection (final class)

php
new SingleConnection(
  ConnectionFactory $factory,
  PoolPolicy        $policy = new PoolPolicy(),
  ?Closure          $clock = null,
)
Method Returns Description
get() object The connection. Opens it on first call; probes it if it sat idle longer than aliveBypassWindow; reopens it once maxLifetime has passed
peek() ?object The current connection without creating or probing — for diagnostics with no side effects
evict() void Drop it; the next get() opens a new one
close() void Close it

For runtimes where the process serves one unit of work at a time. It honours the same lifetimes and probes, so a long-lived CLI worker does not wake up holding a socket the server closed hours ago.


PoolEntry (final class)

php
new PoolEntry(
  object  $resource,
  float   $createdAt,
  float   $lastUsedAt,
  ?float  $expiresAt,
)

What borrow() returns. The connection itself is in $entry->resource; the rest is what the pool uses to decide about probing and eviction. You never construct one by hand.


PoolPolicy (final readonly class)

php
new PoolPolicy(
  int   $maximumPoolSize = 10,
  float $connectionTimeout = 15.0,
  float $maxLifetime = 1800.0,
  float $aliveBypassWindow = 0.5,
  float $maxLifetimeJitter = 0.1,
  float $housekeepingInterval = 30.0,
  float $keepaliveTime = 120.0,
  float $idleTimeout = 600.0,
  int   $minimumIdle = 0,
)

PoolPolicy::default(): PoolPolicy
$policy->housekeepingEnabled(): bool

$keepaliveTime is the one parameter that does not read back as given: an unreachable value — one that reaches maxLifetime, or reaches idleTimeout with no warm floor — is dropped to 0.0 when the policy is constructed, so the property always shows what the housekeeper will actually do.

Each parameter is discussed on the Policy page.


PoolException (final class)

The package’s only exception, with three named constructors:

Constructor When
exhausted(float $timeout) No connection became free within the timeout — everything is busy
connectFailed(Throwable $previous) The adapter’s create() threw; the original is in getPrevious()
unusable(int $retired, float $timeout) The borrow’s window went on dead connections — the server is reachable but not serving

The three are deliberately distinct: “all busy” and “all broken” call for opposite reactions, and an operator told to raise maximumPoolSize while the database refuses to serve is being sent the wrong way. The number in unusable counts connections thrown away, not tries: a borrow keeps discarding what it finds until it reaches a live connection or runs out of time, and how many corpses it buried is the useful signal.

Next