Skip to content

fix: clear transaction state when reconnect() replaces the socket - #10650

Open
KevinBlakeLopez wants to merge 2 commits into
codeigniter4:developfrom
KevinBlakeLopez:fix/10648-reconnect-clears-transaction
Open

KevinBlakeLopez wants to merge 2 commits into
codeigniter4:developfrom
KevinBlakeLopez:fix/10648-reconnect-clears-transaction

Conversation

@KevinBlakeLopez

@KevinBlakeLopez KevinBlakeLopez commented Oct 22, 2026 •

Copy link
Copy Markdown

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. transDepth and transStatus were left as they were, and ping() goes through simpleQuery(), which does not call handleTransStatus(). transComplete() then returned true while the pre-timeout insert was gone.

When the socket is replaced during a transaction, reconnect() now clears transDepth and marks the transaction failed. A later reconnect() while ping() is still true leaves the open transaction alone.

On develop (df8b96bee) the new live test failed before this change:

-    'depth' => 0,
-    'status' => false,
-    'completed' => false,
+    'depth' => 1,
+    'status' => true,
+    'completed' => true,
     'rows' => 0,

After the change, ReconnectDuringTransactionTest and PingTest pass (local MySQL 8.0.45, PHP 8.5.12, MySQLi).

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

Made with Cursor

@carson-codeigniter4 carson-codeigniter4 Bot added the bug Verified issues on the current code behavior or pull requests that will fix them label Oct 22, 2026
@carson-codeigniter4

Copy link
Copy Markdown

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
on the framework than you do. Please make it as painless for your contributions to be included as possible.

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]>
@KevinBlakeLopez
KevinBlakeLopez force-pushed the fix/10648-reconnect-clears-transaction branch from 2dc7af6 to 6369d6b Compare October 22, 2026 23:37
@paulbalandan
paulbalandan requested a balanced review from Copilot October 23, 2026 05:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

Comment thread system/Database/BaseConnection.php Outdated
Comment on lines +643 to +644
$this->transDepth = 0;
$this->transStatus = false;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 639 to +645
$this->close();
$this->initialize();

if ($lostTransaction) {
$this->transDepth = 0;
$this->transStatus = false;
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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]>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Verified issues on the current code behavior or pull requests that will fix them

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: reconnect() keeps the old transaction on a replaced MySQLi socket

2 participants