Drupal by default commits a transaction when its Transaction object goes out of scope, during the Transaction object destruction.
This is a pattern that deviates from normal PHP behaviour, where explicit commit is required and without explicit commit an uncompleted transaction is rolled back. Also, recently this behaviour caused problems when the destruction order of objects is not predictable (see #3405976: Transaction autocommit during shutdown relies on unreliable object destruction order (xdebug 3.3+ enabled)).
A new Transaction::commitOrRelease() method is added to the Transaction object, to mean explicit commit/savepoint release.
::commitOrRelease() indicates that the transaction control is returned to the parent level in a nested transaction scenario like Drupal's - e.g. a 'savepoint' transaction object returns control to its parent 'root' transaction (that can still be rolled back entirely if necessary); a 'root' transaction returns control to the database by committing (=persisting changed data) the db transaction, etc.
Before
try {
$transaction = $this->connection->startTransaction();
foreach ($this->insertValues as $insert_values) {
$stmt->execute($insert_values, $this->queryOptions);
...
}
}
catch (\Exception $e) {
if (isset($transaction)) {
// One of the INSERTs failed, rollback the whole batch.
$transaction->rollBack();
}
// Rethrow the exception for the calling code.
throw $e;
}
After
$transaction = $this->connection->startTransaction();
try {
foreach ($this->insertValues as $insert_values) {
$stmt->execute($insert_values, $this->queryOptions);
...
}
$transaction->commitOrRelease();
}
catch (\Exception $e) {
// One of the INSERTs failed, rollback the whole batch and rethrow the exception for the calling code.
$transaction->rollBack();
throw $e;
}