Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NoteService] implement deadlock retry strategy #2588

Merged
merged 1 commit into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ services:
CoreShop\Component\Pimcore\DataObject\NoteService:
arguments:
- '@event_dispatcher'
- '@monolog.logger.pimcore'

CoreShop\Component\Pimcore\DataObject\ObjectClonerInterface: '@CoreShop\Component\Pimcore\DataObject\ObjectCloner'
CoreShop\Component\Pimcore\DataObject\ObjectCloner: ~
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ public function log(Concrete $object, Event $event): void
$note->addData('workflow', 'text', $event->getWorkflowName());
$note->addData('transition', 'text', $transition->getName());

$this->noteService->storeNote($note);
try {
$this->noteService->storeNote($note);
}
catch (\Exception) {
//We simply ignore this, if the note cannot be saved, we don't want to break the workflow
}
}

private function getFrom(array $froms)
Expand Down
47 changes: 46 additions & 1 deletion src/CoreShop/Component/Pimcore/DataObject/NoteService.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,20 @@

namespace CoreShop\Component\Pimcore\DataObject;

use Doctrine\DBAL\Exception\RetryableException;
use Pimcore\Model\DataObject\Concrete;
use Pimcore\Model\Document;
use Pimcore\Model\Element\Note;
use Pimcore\Model\Tool\Email\Log;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

class NoteService implements NoteServiceInterface
{
public function __construct(
protected EventDispatcherInterface $eventDispatcher,
protected LoggerInterface $pimcoreLogger,
) {
}

Expand Down Expand Up @@ -88,7 +91,49 @@ public function storeNoteForEmail(Note $note, Document\Email $emailDocument): No

public function storeNote(Note $note, array $eventParams = []): Note
{
$note->save();
$maxRetries = 5;
for ($retries = 0; $retries < $maxRetries; $retries++) {
try {
$note->beginTransaction();
$note->save();
$note->commit();

//the transaction was successfully completed, so we cancel the loop here -> no restart required
break;
} catch (\Exception $e) {
try {
$note->rollBack();
} catch (\Exception $er) {
// PDO adapter throws exceptions if rollback fails
$this->pimcoreLogger->info((string)$er);
}

if ($e instanceof RetryableException) {
// we try to start the transaction $maxRetries times again (deadlocks, ...)
if ($retries < ($maxRetries - 1)) {
$run = $retries + 1;
$waitTime = random_int(1, 5) * 100000; // microseconds
$this->pimcoreLogger->warning(
'Unable to finish transaction ('.$run.". run) because of the following reason '".
$e->getMessage().
"'. --> Retrying in ".$waitTime.' microseconds ... ('.($run + 1).' of '.$maxRetries.')'
);

usleep($waitTime); // wait specified time until we restart the transaction
} else {
$this->pimcoreLogger->error(
'Finally giving up restarting the same transaction again and again, last message: '.$e->getMessage(
)
);

throw $e;
}
continue;
}

throw $e;
}
}

$this->eventDispatcher->dispatch(
new GenericEvent($note, $eventParams),
Expand Down
Loading