Thanks @tray2 . Yeah, understanding how best to create locks in context of this particular piece of code is something that I need to look into a bit more. However, and unfortunately, I don't think that I can skip the locked records in a conflicting transaction because they have to be included in the logic within the transaction to achieve the necessary outcome. Currently, the operation in question uses a significant amount of data to calculate amounts and actions related to inventory adjustments, and I pre-load the necessary data up front with something like this:
$order->load([
'products' => function ($q) {
$q->withTrashed()
->with([
'inventories' => fn ($q) => $q->lockForUpdate(),
'variant' => fn($q) => $q->withSecondaryInventory(),
'shopProduct' => function ($q) {
$q->with(['inventory' => fn ($q) => $q->lockForUpdate()])
->withOpenInventory();
},
]);
}
]);
I use lockForUpdate because, if I understand how it works correctly, I don't want another inventory operation to read a value which will likely get updated in the current transaction until the first transaction is complete. Based on this small amount of info, does my understanding of how it works and the lock implementation seem reasonable?
Aside from that, I'm still confused as to why the transaction is dying when a deadlock occurrs instead of being gracefully retried, as the documentation suggests. Is my understanding of what transaction retries are for not correct? Is there a different or better way to handle retrying a transaction when a deadlock occurs? If just kind of feels like the Illuminate\Database\Concerns\ManagesTransactions::transaction() method needs some additional checking to ensure a valid transaction exists on retry, like:
20 public function transaction(Closure $callback, $attempts = 1)
21 {
//...
35 catch (Throwable $e) {
36 $this->handleTransactionException(
37 $e, $currentAttempt, $attempts
38 );
39
40 + if ($this->transactions > 0) {
41 + //when an exception is caught but allowed to retry,
42 + //ensure that a valid PDO transaction exists
43 + //and refresh it if not
44 + }
45
46 continue;
47 }
//...
70 }
I'd love any additional tips on good strategies for handling "intense" transactions. But, I'm mostly just trying to understand why the implemented transaction retry isn't working, or, more specifically, why my transaction is dying and therefore rendering the retry useless. Has anyone encountered this issue? Is there a better way to do this? Thanks!