Repository navigation
fix: clear transaction state when reconnect() replaces the socket - #10650
KevinBlakeLopez wants to merge 2 commits into
Conversation
|
Hi there, @KevinBlakeLopez! 👋 Thank you for sending this PR! We expect the following in all Pull Requests (PRs).
Important We expect all code changes or bug-fixes to be accompanied by one or more tests added to our test suite to prove the code works. If pull requests do not comply with the above, they will likely be closed. Since we are a team of volunteers, we don't have any more time to work See https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md |
The server rolls the open transaction back with the dead connection. Keeping transDepth and transStatus made transComplete() return true. Fixes codeigniter4#10648 Co-authored-by: Cursor <[email protected]>
2dc7af6 to
6369d6b
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Failed reconnection attempts bypass transaction invalidation, and OCI8 recovery leaves autocommit disabled.
2 open issues
What changed in this PR
Addresses #10648 by invalidating transaction state when reconnect() replaces a lost database connection. Reviewed under the assumed develop patch-release policy.
Changes:
- Clears transaction depth and marks interrupted transactions as failed.
- Adds MySQLi live tests for lost and healthy connections during transactions.
- Documents the behavior in the connection guide and changelog.
Validation used targeted source inspection; automated tests and the database-driver matrix remain for CI.
| File | Description |
|---|---|
user_guide_src/source/database/connecting.rst |
Explains transaction invalidation after reconnection. |
user_guide_src/source/changelogs/v4.7.6.rst |
Records the fix for #10648. |
tests/system/Database/Live/ReconnectDuringTransactionTest.php |
Tests interrupted and preserved MySQLi transactions. |
system/Database/BaseConnection.php |
Resets transaction state after replacing a connection. |
Review effort: Balanced
| $this->transDepth = 0; | ||
| $this->transStatus = false; |
There was a problem hiding this comment.
Fixed in 1a7668c. The reset now goes through a new abandonLostTransaction() hook, and OCI8\Connection overrides it to restore OCI_COMMIT_ON_SUCCESS before clearing the depth. ReconnectLostTransactionTest::testReconnectRestoresOci8CommitModeWhenTransactionIsLost covers it.
| $this->close(); | ||
| $this->initialize(); | ||
|
|
||
| if ($lostTransaction) { | ||
| $this->transDepth = 0; | ||
| $this->transStatus = false; | ||
| } |
There was a problem hiding this comment.
Fixed in 1a7668c. The lost transaction is now cleared after close() and before initialize(), so a reconnect that throws leaves transDepth at 0 and the status failed. ReconnectLostTransactionTest::testReconnectClearsTransactionStateWhenInitializeFails covers it. I also checked it against a live MySQL by locking the account during reconnect() and letting the next query() reconnect: transComplete() now returns false.
Same commit: the guide and changelog now say this applies only when ping() reports the connection lost. On PostgreSQL, pg_ping() re-establishes a broken connection itself and returns true, so reconnect() does not see the loss there.
reconnect() cleared transDepth and transStatus only after initialize() returned. When initialize() threw, the old transaction state survived, and a later query() that reconnected on its own let transComplete() report success. The state is now cleared before initialize() runs. OCI8 keeps its commit mode in PHP. Clearing the depth skipped _transRollback(), so commitMode stayed OCI_NO_AUTO_COMMIT and later writes were never committed. OCI8 now restores OCI_COMMIT_ON_SUCCESS when the transaction is abandoned. The guide and changelog now say this applies only when ping() reports the connection lost. On PostgreSQL, pg_ping() re-establishes a broken connection itself and returns true, so reconnect() does not clear the state there. Refs codeigniter4#10648 Co-Authored-By: Claude Opus 5.5 <[email protected]>

Description
Fixes #10648.
reconnect()closes a dead socket and opens a new one. The server has already rolled back the transaction that was open on the old socket.transDepthandtransStatuswere left as they were, andping()goes throughsimpleQuery(), which does not callhandleTransStatus().transComplete()then returned true while the pre-timeout insert was gone.When the socket is replaced during a transaction,
reconnect()now clearstransDepthand marks the transaction failed. A laterreconnect()whileping()is still true leaves the open transaction alone.On
develop(df8b96bee) the new live test failed before this change:After the change,
ReconnectDuringTransactionTestandPingTestpass (local MySQL 8.0.45, PHP 8.5.12, MySQLi).Checklist:
Made with Cursor