Repository navigation
Fix database lease ownership, transaction cleanup and read extensions - #59
Conversation
Recognize physical transactions when deciding whether a database session can be released. Native PDO and SQL-started transactions now retain the session even when the framework transaction counter is zero. Inspect only open handles and preserve the public writer-only inTransaction behavior. Disconnect rolls back both distinct PDO handles, continues cleaning the reader when writer cleanup fails, and preserves cancellation precedence. Successful rollback invalidates remembered session settings; failed cleanup marks the physical state unknown before references are cleared. Extend the existing disconnect tests for reader rollback, shared and lazy handles, and cleanup failure ordering. These changes add no per-query ownership checks or database round trips.
Inspect the physical connection after release callbacks and logical lease detachment. Raw transactions can bypass framework counters, and callbacks can start another transaction during cleanup. Return the slot to the pool only after this final inspection. Discard immediately when a transaction remains or its state cannot be inspected, including when earlier cleanup was cancelled. Decide before logging so a failing logger cannot return a dirty session to idle. Reuse the existing discard path to roll back, close resources and release pool capacity exactly once. Preserve invalid-and-requeue behavior for other preparation failures when no transaction remains. Add regression coverage for whole-connection raw transactions, throwing loggers, custom-driver inspection failures and cancellation. Shared in-memory SQLite keeps its committed data after wrapper replacement.
Mark logical leases ended before terminal release or discard. A connection or retained builder that later needs another physical session now fails with an actionable exception instead of borrowing a slot whose original cleanup has already run. Check the terminal state only when acquiring a new slot. Early release and failed-acquisition cleanup continue to settle the physical wrapper directly, so normal reuse and reconnect remain available while the owning execution is active. Release and rollback callbacks retain access to their currently held session. Extend coroutine and non-coroutine lifecycle tests to reject later acquisition without consuming pool capacity. Cover terminal discard and raw transactions left by application code or release listeners, verifying rollback and preservation of committed SQLite data.
Classify every effective read configuration before enabling logical session leases. An extension selected by a single read record or a listed record must retain whole-connection ownership; rebuilding its result as a PDO lease bypassed the extension and could fail during construction. Keep name and top-level extension precedence, endpoint identity checks and write-side factory behavior unchanged. Normalize individual records only during pool construction, without selecting a read endpoint for the lifetime of the pool or adding per-query checks. Extend the existing extension tests with single and listed read records, checking returned object identity and early-release behavior. Extend session pinning coverage for native PDO, SQL BEGIN and distinct read-handle transactions across manual release and outgoing HTTP calls.
Response cancellation uses the connection and producer coroutine to stop active work when a connection closes. Remove the unused stream property and argument forwarding from the response bridge, HTTP server, both gRPC emission paths, WebSocket handshake and disconnect fixture. Keep cancellation of every active producer on the closed connection, isolation of sibling connections and identity-safe registration cleanup. Consolidate test rows that differed only in unused protocol IDs and identify producers directly in cleanup assertions. Clarify the existing HTTP/2 cancellation follow-up: introduce stream identity when integrating an actual released Swoole stream-cancel event. Existing connection-close handling and failed-write cleanup remain unchanged.
Explain that connections and builders belong to their resolving coroutine or task. Distinguish ordinary reuse across early release from acquisition after that execution finishes, and limit the new exception guarantee to connections that support early release. Document automatic pinning and terminal rollback for native PDO and SQL-started transactions, including the read connection. Retain explicit pinning guidance for other operations that require the same physical session. Describe the configuration supplied to named, top-level and read-record extensions. Include driver differences alongside database names and prefixes when explaining whole-connection ownership, without exposing internal cleanup machinery in application documentation.
Document the framework follow-up to the database ownership and response registration findings on PR #653. Record terminal lease ownership, effective read-extension selection, physical transaction cleanup and removal of unused stream identity, with the invariants needed to maintain each change. Include regression coverage, callback and cancellation ordering, performance boundaries and documentation requirements. Keep the framework follow-up separate from the paused AI package work and require its merged changes before package implementation resumes. Implementation was verified with failing regressions against the original source, focused tests, formatting, PHPStan, the full components suite, Testbench and package checks. MySQL, MariaDB and PostgreSQL shared and package-specific integration suites also passed. PR creation and replies to the original review remain pending.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoFix database lease ownership, transaction cleanup, and read extensions
AI Description
Diagram
High-Level Assessment
Files changed (19)
|
Code Review by Qodo🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)
Great, no issues found!Qodo reviewed your code and found no material issues that require reviewTip of the day💡 Did you know, you can keep summaries lean with Findings visible per group, which tucks the rest behind a View link |
|
A ConnectionEstablished listener can retain the published connection before throwing. Discarding only its physical wrapper left the logical lease able to borrow again without registered cleanup, permanently consuming a pool slot. Discard the logical owner when it exists, falling back to the borrowed wrapper if construction failed. Preserve cancellation precedence and recovery for failed reacquisition by an already registered owner. Keep the ownership failure message actionable for both finished executions and failed initial setup. Extend the existing listener failure and cancellation cases to prove that a retained failed owner cannot borrow, pool accounting remains empty, and fresh resolution succeeds. The assertions fail without the fix. Formatting, static analysis, and the affected database suites pass.
Distinguish terminal initial setup failures from recoverable acquisition failures within an existing registered lease. Record the retained-connection regression coverage and the remaining PR verification step so the plan matches the implemented lifecycle.
This fixes the database lifecycle issues raised in the review of #653 and removes unused stream-ID plumbing from response cancellation. Ordinary queries still benefit from early connection release and reuse. The changes close gaps in ownership, transaction cleanup, and read-driver extensions.
End logical connection ownership
A retained connection or query builder could borrow another pool slot after its owning coroutine had finished. The cleanup registered for that owner had already run, so nothing returned the new slot to the pool.
Logical leases now end when their coroutine or task releases or discards them. A connection whose initial setup fails is also ended, so a listener retaining it cannot later borrow a slot without registered cleanup. Attempting to borrow through an ended lease throws an exception explaining where to resolve the connection. This check runs only before a new borrow. Early release, reconnect, and recovery from a failed reacquisition still work within the owning execution. Release and rollback callbacks can still use the session while cleanup owns it.
Keep raw transactions on their sessions
Transactions started through PDO or SQL don't update the framework's transaction counter. Early release previously treated those sessions as idle, allowing another request to use a transaction it didn't start.
Pinning now checks the physical transaction state of both open PDO handles. This covers native and SQL-started transactions on the writer or reader without opening an unused handle. For example, the transaction below retains its session across the HTTP call:
The public
inTransaction()method keeps its existing writer-only behavior. Code using temporary tables, session locks, or other session-dependent operations still useswithPinnedSession()as documented.Settle unfinished transactions before reuse
Pool release now checks physical transaction state after callbacks finish and the logical lease detaches. This also catches a raw transaction opened by a release callback. If a transaction remains, or its state can't be inspected, the slot is discarded immediately so it doesn't sit idle holding transaction locks.
The discard decision is made before logging, so a failing logger can't return a dirty session to the pool. Cleanup still settles the slot when cancellation occurs. Other preparation failures retain their existing reconnect-before-reuse behavior when no transaction remains.
PDO disconnect now cleans both distinct handles and attempts reader cleanup even if writer cleanup fails. Cancellation retains precedence over ordinary errors. Shared in-memory SQLite keeps its committed data and schema after successful rollback and replacement of the pooled wrapper.
Honor effective read-driver extensions
An explicit
::readpool could select aDB::extenddriver from a read record, then incorrectly try to rebuild its result as a PDO lease. That bypassed the extension and could fail with an unsupported-driver error or a type error.Lease eligibility now checks every effective read record, including a single record and a list of records. These extensions retain their complete connection object. Named and top-level extension precedence, write-side construction, and database identity checks remain unchanged. Classification happens once when the pool is created.
Remove unused response stream IDs
Response cancellation tracks active producers by connection and coroutine. The stored stream ID wasn't used. This removes it and its argument forwarding from the response bridge, HTTP, gRPC, WebSocket, and test-fixture paths.
Connection-close cancellation still stops all opted-in producers on that connection, preserves other connections, and removes registrations after production. The existing HTTP/2 follow-up now states that stream identity will be added when a released Swoole stream-cancel event needs it.
Verification
The database regressions fail against the original source and pass with these changes. Coverage includes expired owners, raw transactions, read extensions, callback failures, cancellation, and cleanup of both PDO handles. Existing response tests verify that removing stream IDs preserves cancellation behavior.
Formatting, PHPStan, the full components suite, Testbench, and package-consumer checks passed. The MySQL, MariaDB, and PostgreSQL integration runs also passed, including their package-specific suites.
There are no new ownership checks on ordinary query execution or new database round trips for transaction inspection. The additional checks run at pool creation, acquisition, and release boundaries.
Summary by CodeRabbit
Note
Fix database lease ownership, transaction cleanup, and pooled connection discard
endedflag toConnectionLeaseso PDO resolution after release or discard throwsLogicExceptioninstead of borrowing a new pooled session; idle release no longer ends the leasePooledConnection.releasenow inspects for open physical transactions and discards the connection (with rollback) instead of reusing it; inspection failures also trigger discardConnection.hasPinnedSessionand the newhasPhysicalTransactionkeep the session pinned during raw PDO/SQL transactions, including on read connectionsDatabasePool.supportsSessionLeasesdisables session leases for configured extensions, including single-record and listed read-driver configsPdoConnection.disconnectDriverResourcesrolls back transactions on both distinct write and read PDOs before forgetting themResponseBridge::sendandResponseCancellation.registeracross HTTP, WebSocket, and gRPC servers; registration now uses only the connection and producer coroutine identifiersResponseBridge::send()that pass a stream ID (now removed) will break; extensions configured on read endpoints now change session-lease eligibility in DatabasePool.phpMacroscope summarized a3d1479.