Transactions
Drupal also supports transactions, including a transparent fallback for databases that do not support transactions. However, transactions can be quite tricky if you try to run two transactions simultaneously. Behavior in such cases also depends on the database.
A similar issue exists with nested locks in C / C++. If code has already acquired lock A and tries to acquire lock A again, the code will block. If you write code that checks whether it already holds the lock and avoids acquiring it again, you can prevent deadlocks, but you might release the lock prematurely.
In SQL, we have the same problem. If your code is already in a transaction, starting a new transaction has the unexpected and unfortunate effect of committing the current transaction and starting a new one.
Java solves the nesting problem with its locking system by implementing support for a nesting structure similar to what we are testing below. Java allows marking functions as “synchronized,” which makes the function wait to acquire the lock before executing and release it when no longer needed. If one synchronized function calls another within the same class, Java tracks the nested locking. The outer function acquires the lock, the inner function performs no locking operation, and the lock is released when the outer function returns.
Although we cannot declare functions as “transactional” in PHP, we can emulate Java’s nesting logic using objects with constructors and destructors. The function simply calls “$transaction = $connection->startTransaction()” as the first (or nearly first) operation to make itself transactional. If one transactional function calls another, our transaction abstraction layer nests them without executing actual transactional operations (as seen by the database) inside inner nested levels.
To start a new transaction, simply call $transaction = $connection->startTransaction();
in your code. The transaction will remain open as long as the $transaction
variable stays in scope. When $transaction
is destroyed, the transaction will be committed. If your transaction is nested within another, Drupal will track each transaction and commit the outermost transaction only when the last transaction object goes out of scope, meaning all associated queries have successfully completed.
You must assign the return value of $connection->startTransaction();
to a variable, as shown in the example. If you call the method without assigning the return value to a variable, your transaction will be committed immediately, making it useless.
Rolling back a transaction is managed through the connection object ($connection->rollBack()
) in Drupal 8, but it is generally better to use the transaction wrapper method ($transaction->rollBack()
). The reason for using the transaction’s rollBack()
method is that it rolls back the transaction based on its name, whereas $connection->rollBack()
defaults to the name drupal_transaction
and may produce unintended results with nested transactions.
Example:
function my_transaction_function() { // The transaction opens here. $transaction = $connection->startTransaction(); try { $id = $connection->insert('example') ->fields([ 'field1' => 'mystring', 'field2' => 5, ]) ->execute(); my_other_function($id); return $id; } catch (Exception $e) { $transaction->rollBack(); watchdog_exception('my_type', $e); } // You can let $transaction go out of scope here and the transaction will // automatically be committed if it wasn't already rolled back. // However, if you have more work to do, you will want to commit the transaction // yourself, like this: $transaction->commit(); // More code here that is outside of the transaction. } function my_other_function($id) { // The transaction is still open here. if ($id % 2 == 0) { $connection->update('example') ->condition('id', $id) ->fields(['field2' => 10]) ->execute(); } }
Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.