Skip to content

Skip the redundant checkout round-trips the pool paid on every borrow - #15

Merged
adhikjoshi merged 2 commits into
mainfrom
perf/checkout-fast-path
Aug 20, 2026
Merged

Skip the redundant checkout round-trips the pool paid on every borrow#15
adhikjoshi merged 2 commits into
mainfrom
perf/checkout-fast-path

Conversation

@adhikjoshi

@adhikjoshi adhikjoshi commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Problem

Datadog spans from production showed the pool's own overhead as the top SQL by count and total time: every checkout paid a SELECT 1 ping plus two session SETs, and every release paid the SETs again — 3–5 round-trips at ~10–20ms each from Cloud Run to the DB host. That is ~30–50ms of latency tax on every DB-using request in every app, and 162M SET statements on the MySQL server in 21 days.

Change

release() fully resets every connection before re-pooling and closes any whose reset fails, so pooled connections are clean by invariant. Checkout now:

  • runs a local dirty check (transaction counter + raw-PDO flag, no SQL, no lazy connect) and only pays the full reset for a genuinely dirty connection;
  • pings only connections idle past ping_after_idle (default 30s); freshly pooled/created connections skip it — a connection that died while idle is still recovered by Laravel's lost-connection retry through the pool's identity-preserving reconnector;
  • fresh connections get their session normalized once at creation (same SETs as the reset), so fresh and recycled connections are indistinguishable even when the server's global isolation/autocommit defaults differ.

Verification

  • E2E (real Octane + MySQL): 50 requests → session SETs 104 → 51 (exactly one reset per borrow, release-side), checkout pings gone.
  • 171 package tests green; new behaviors mutation-tested (dirty-check removed, ping removed, idle-order swapped, normalization dropped → each test goes red).
  • Adversarial review round completed; its findings (session-normalization gap, lazy-PDO resolution in the dirty check, doc claims) fixed and covered.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Datadog spans from production showed the pool's own overhead as the top
SQL by count AND total time: every checkout paid a SELECT 1 ping plus
two session SETs, and every release paid the two SETs again - three to
five ~10-20ms round-trips per borrow from Cloud Run to the database
host, ~30-50ms of latency tax on every DB-using request across all
apps (and 162M SET statements on the server in 21 days).

release() fully resets every connection before re-pooling and closes
any connection whose reset fails, so pooled connections are clean by
invariant. Checkout now:
- runs a LOCAL dirty check (transaction counter + PDO flag, no SQL) and
  only pays the full reset for a genuinely dirty connection;
- pings only connections idle past ping_after_idle (default 30s) -
  freshly pooled and freshly created connections skip it. A connection
  that died while idle is still recovered by the reconnector on first
  query.
…fix docs

- normalizeSession() runs the two session SETs once at connection
  creation for mysql/mariadb, so fresh and recycled connections are
  indistinguishable - without it, skipping the checkout reset would make
  isolation level and autocommit depend on whether a connection happened
  to be fresh whenever the server default differs.
- hasDirtyTransactionState() uses getRawPdo(): a never-connected lazy
  connection cannot be in a transaction, and the check must not trigger
  a connect.
- Correct two comments that overstated ping semantics.
@adhikjoshi
adhikjoshi merged commit 9df2103 into main Aug 20, 2026
2 checks passed
@adhikjoshi
adhikjoshi deleted the perf/checkout-fast-path branch August 20, 2026 12:42
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