The scheme of work is as follows: I send json to the controller (then I convert json into an object) -> then I send the object to the queue (I use doctrines as a transport) -> then the handler reads the message and saves the object to the database. Problem: when processing messages asynchronously, several duplicates of the object are saved in the database + the handler writes about errors in the connection (see screenshot).
Found a description of this problem on symfony casts, they suggest using the flush function when saving an object to the database, without first using the persist function. With this approach, saving to the database does not occur.
With a synchronous save, this error does not occur.
Does anyone know a solution to this problem?
ApiController:
/**
* @Route("/api/task")
* @param Request $request
* @param MessageBusInterface $messageBus
* @return Response
*/
public function createTask(Request $request, MessageBusInterface $messageBus): Response
{
try {
$serializer = SerializerBuilder::create()->build();
$this->inputCalculatorTaskDto = $serializer->deserialize($request->getContent(),
InputCalculatorTaskDto::class,
'json');
$this->inputCalculatorTaskDto
->setTaskGuid(Uuid::uuid4())
->setServiceName(ServicesListEnum::SERVICE_CALULATOR)
->setTaskName(TaskListEnum::CALCULATE_ORDER_PRICE)
->setCreatedAt(DateTime::createFromFormat('U.u', microtime(TRUE)));
$calculatorTaskMessage = new CalculatorTaskMessage($this->inputCalculatorTaskDto);
$messageBus->dispatch($calculatorTaskMessage);
$this->logger->info('done ' . $this->inputCalculatorTaskDto->getTaskGuid());
return new Response('done');
} catch (\Throwable $t) {
$this->logger->error($t->getMessage());
return new Response('error ' . $t->getMessage());
}
}
Message:
class CalculatorTaskMessage
{
/**
* @var InputCalculatorTaskDto
*/
private InputCalculatorTaskDto $inputCalculatorTaskDto;
public function __construct(InputCalculatorTaskDto $inputCalculatorTaskDto)
{
$this->inputCalculatorTaskDto = $inputCalculatorTaskDto;
}
/**
* @return InputCalculatorTaskDto
*/
public function getInputCalculatorTaskDto(): InputCalculatorTaskDto
{
return $this->inputCalculatorTaskDto;
}
}
MessageHandler
class CalculatorNewTaskCreatorHandler implements MessageHandlerInterface
{
public InputCalculatorTaskDtoToTaskEntityImpl $inputCalculatorTaskDtoToTaskEntity;
public EntityManagerInterface $entityManager;
public TasksRepository $tasksRepository;
private CalculatorTaskProducer $calculatorTaskProducer;
/**
* CalculatorTaskCreatorHandler constructor.
* @param EntityManagerInterface $entityManager
* @param TasksRepository $tasksRepository
* @param CalculatorTaskProducer $calculatorTaskProducer
*/
public function __construct(EntityManagerInterface $entityManager,
TasksRepository $tasksRepository,
CalculatorTaskProducer $calculatorTaskProducer)
{
$this->inputCalculatorTaskDtoToTaskEntity = new InputCalculatorTaskDtoToTaskEntityImpl();
$this->entityManager = $entityManager;
$this->tasksRepository = $tasksRepository;
$this->calculatorTaskProducer = $calculatorTaskProducer;
}
public function __invoke(CalculatorTaskMessage $calculatorTaskMessage)
{
$inputCalculatorTaskDto = $calculatorTaskMessage->getInputCalculatorTaskDto();
$tasks = $this->inputCalculatorTaskDtoToTaskEntity->map($inputCalculatorTaskDto);
$this->entityManager->persist($tasks);
$this->entityManager->flush();
$this->calculatorTaskProducer->sendMessage($inputCalculatorTaskDto);
}
}
