Skip to content

Commit

Permalink
Fix nested transaction callbacks handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mpyw committed Sep 20, 2023
1 parent 1157523 commit c3ecd96
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 27 deletions.
22 changes: 2 additions & 20 deletions src/Illuminate/Database/Concerns/ManagesTransactions.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,7 @@ public function transaction(Closure $callback, $attempts = 1)
}

$this->transactions = max(0, $this->transactions - 1);

if ($this->afterCommitCallbacksShouldBeExecuted()) {
$this->transactionsManager?->commit($this->getName());
}
$this->transactionsManager?->commit($this->getName());
} catch (Throwable $e) {
$this->handleCommitTransactionException(
$e, $currentAttempt, $attempts
Expand Down Expand Up @@ -195,26 +192,11 @@ public function commit()
}

$this->transactions = max(0, $this->transactions - 1);

if ($this->afterCommitCallbacksShouldBeExecuted()) {
$this->transactionsManager?->commit($this->getName());
}
$this->transactionsManager?->commit($this->getName());

$this->fireConnectionEvent('committed');
}

/**
* Determine if after commit callbacks should be executed.
*
* @return bool
*/
protected function afterCommitCallbacksShouldBeExecuted()
{
return $this->transactions == 0 ||
($this->transactionsManager &&
$this->transactionsManager->callbackApplicableTransactions()->count() === 1);
}

/**
* Handle an exception encountered when committing a transaction.
*
Expand Down
50 changes: 43 additions & 7 deletions src/Illuminate/Database/DatabaseTransactionsManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,53 @@ public function rollback($connection, $level)
*/
public function commit($connection)
{
[$forThisConnection, $forOtherConnections] = $this->transactions->partition(
if ($this->afterCommitCallbacksShouldBeExecuted($connection)) {
[$forThisConnection, $forOtherConnections] = $this->transactions->partition(
fn ($transaction) => $transaction->connection == $connection
);

$this->transactions = $forOtherConnections->values();

$forThisConnection->map->executeCallbacks();

if ($this->transactions->isEmpty()) {
$this->callbacksShouldIgnore = null;
}

return;
}

$last = $this->transactions->last(
fn ($transaction) => $transaction->connection == $connection
);
$parent = $this->transactions->last(
fn ($transaction) => $transaction->connection == $connection && $last !== $transaction,
);

$this->transactions = $forOtherConnections->values();
$this->transactions = $this->transactions->reject(
fn ($transaction) => $transaction === $last,
)->values();

$forThisConnection->map->executeCallbacks();
foreach ($last?->getCallbacks() ?? [] as $callback) {
$parent?->addCallback($callback);
}

if ($this->transactions->isEmpty()) {
$this->callbacksShouldIgnore = null;
}
}

/**
* Determine if after commit callbacks should be executed.
*
* @param string $connection
* @return bool
*/
protected function afterCommitCallbacksShouldBeExecuted($connection)
{
return $this->callbackApplicableTransactions($connection)->count() === 1;
}

/**
* Register a transaction callback.
*
Expand Down Expand Up @@ -112,13 +146,15 @@ public function callbacksShouldIgnore(DatabaseTransactionRecord $transaction)
/**
* Get the transactions that are applicable to callbacks.
*
* @param string $connection
* @return \Illuminate\Support\Collection
*/
public function callbackApplicableTransactions()
public function callbackApplicableTransactions($connection)
{
return $this->transactions->reject(function ($transaction) {
return $transaction === $this->callbacksShouldIgnore;
})->values();
return $this->transactions->reject(
fn ($transaction) => $transaction->connection == $connection
&& $transaction === $this->callbacksShouldIgnore,
)->values();
}

/**
Expand Down

0 comments on commit c3ecd96

Please sign in to comment.