Skip to content

Fix: Pool 'db-5c52aaf50617332d2292619e5a36b490.fra.appwrite.center' is empty (size 24, active 0, idle 0) - #34

Closed
claudear wants to merge 1 commit into
mainfrom
fix/pool-creation-slot-leak-on-throwable
Closed

Fix: Pool 'db-5c52aaf50617332d2292619e5a36b490.fra.appwrite.center' is empty (size 24, active 0, idle 0)#34
claudear wants to merge 1 commit into
mainfrom
fix/pool-creation-slot-leak-on-throwable

Conversation

@claudear

Copy link
Copy Markdown
Contributor

Resolves CLOUD-3NVF (sentry).

PR opened automatically by claudear from pushed branch fix/pool-creation-slot-leak-on-throwable.

… Throwable

pop() reserves a capacity slot (connectionsCreated++) before creating a
connection, then decrements it on failure. The catch only handled
\Exception, so a \Throwable that is not an \Exception (e.g. a \TypeError
from the init callback) propagated without releasing the reserved slot.

Each such failure permanently shrank the pool's usable capacity. Once the
leaked count reached `size`, pop() would refuse to create new connections
and every request failed with "Pool '...' is empty (size N, active 0,
idle 0)" even though nothing was actually in use.

Catch \Throwable so the reserved slot is always released, matching the
\Throwable handling already used in destroyConnection().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@claudear

Copy link
Copy Markdown
Contributor Author

Fix Confidence: 75/100

@github-actions

Copy link
Copy Markdown

Thanks for contributing! This repository is a read-only mirror; development for this library happens in packages/pools in the utopia-php monorepo. Please open this pull request there instead.

@github-actions github-actions Bot closed this Jul 23, 2026
@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown

Greptile Summary

Fixes pool-capacity leakage when connection creation throws a non-Exception Throwable.

  • Broadens connection-creation cleanup in Pool::pop() to catch all Throwable instances and release the reserved slot.
  • Adds regression coverage proving repeated TypeError failures leave pool capacity available and no connections active.

Confidence Score: 5/5

The PR appears safe to merge, with the connection slot cleanup correctly extended to non-Exception failures.

The changed catch preserves the existing failure path while ensuring the slot reserved before connection creation is decremented for TypeError and other Throwable failures, and the regression test directly validates the repaired accounting.

Important Files Changed

Filename Overview
src/Pools/Pool.php Correctly broadens failure cleanup so every connection-creation failure releases its reserved capacity slot.
tests/Pools/Scopes/PoolTestScope.php Adds a focused regression test that fails under the previous slot-leaking behavior and verifies capacity and active-state accounting.

Reviews (1): Last reviewed commit: "fix(pools): release reserved slot when c..." | Re-trigger Greptile

loks0n added a commit to utopia-php/monorepo that referenced this pull request Jul 27, 2026
pop() reserves capacity by incrementing connectionsCreated before the
connection exists, then releases it in a catch. The catch was scoped to
\Exception, so anything else escaping createConnection() kept the slot.
The init callback is caller supplied, so an Error is entirely reachable:
a TypeError from the callback, or a failure inside an adapter.

Each leaked slot is permanent for the life of the process, and they
accumulate one failed pop at a time. Once connectionsCreated reaches
size the pool refuses to create anything and reports itself empty while
nothing is actually in use — "Pool 'x' is empty (size 24, active 0,
idle 0)". Only a restart recovers it, which is why an upstream outage
that should degrade into slow acquisition instead ratchets a process
into permanent failure that outlives the outage.

Catch \Throwable, and move the release into a finally guarded by a
$reserved flag so the reservation is balanced on every path out of the
block, not only the ones enumerated in a catch. destroyConnection()
already used \Throwable for the same reason a few lines below.

The added test drains a size-2 pool with five pops against an init
callback that throws a TypeError. Before this change count() drops to 0
with nothing handed out; after it stays at 2.

Reported previously as utopia-php/pools#34, which was auto-closed
because that repository is a read-only mirror.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant