Discard Doctrine OCI8 Connections After a Rejected Commit

When Oracle rejects COMMIT because a deferred constraint fails, assume the unit of work was rolled back and the driver connection may no longer match Doctrine’s transaction state. Do not execute unrelated writes on that connection.

Last updated: November 6, 2026.

<?php
try {
    $connection->beginTransaction();
    saveInvoice($connection, $invoice);
    $connection->commit();
} catch (Throwable $error) {
    // A failed COMMIT can leave this OCI8 connection unsafe to reuse.
    $connection->close();
    throw $error;
}

// A later request or bounded retry must obtain a clean connection.

Retry only the complete idempotent business operation, never just the failed commit. A new connection restores a known driver execution mode and releases any state associated with the old session.

Why the wrapper state is not enough

Doctrine’s transaction documentation requires using the DBAL connection methods so its nesting state stays synchronized with the driver. A current Doctrine OCI8 report describes a rejected commit leaving the wrapper at nesting level zero while the driver continues executing statements with auto-commit disabled.

That mismatch is dangerous: the next write may remain invisible to other sessions, retain locks, and be committed by a later unrelated transaction. Checking only isTransactionActive() cannot prove that the physical connection has returned to a safe execution mode.

Fail closed and test with two sessions

PHP’s OCI8 execution-mode constants distinguish OCI_COMMIT_ON_SUCCESS from OCI_NO_AUTO_COMMIT. Applications normally should not reach into Doctrine’s driver to toggle those flags. Closing the failed connection is a simpler containment boundary until the installed DBAL release includes a verified correction.

Reproduce in a disposable schema using a deferred constraint. After the commit fails, confirm the application closes the connection. Then acquire a new connection, perform a sentinel write outside an explicit transaction, and verify from a second session that the row is immediately visible and no unexpected lock remains.

Log the Oracle error code, DBAL version, transaction identifier, and whether the connection was discarded; do not log sensitive bind values. Continue with Doctrine transaction recovery, database error inspection, and PHP database connection patterns.

Related Web Cheat Sheet guides

Sergey Kornilov

Sergey Kornilov