I’m facing a weird and stubborn situation. Been using Messenger component with Sendgrid in async://
mode with doctrine without issues, however the hosting platform where the project is being moved has an issue with cron; it can only execute every 30 minutes. That’s unacceptable for emails that need to go out asap, like transactional emails. Because of this, the messenger must be set in sync://
mode.
When the messenger is set to sync://
you cannot use async://
and vice-versa. At least I cannot understand how to. Been trying to create a custom router without success, and also tried enabling both sync://
and async://
in the configuration and then specificy X-Transport
header when sending emails.
The needed configuration must be like the following:
- Sync mode for “immediate” emails that need to go out
- Async mode for “mass” emails that need to go out in bulk, and the cron cannot execute forever.
On the async note after some digging (because supervisor is not available), I implemented this:
CronRunningEvent.php
<?php
namespace AppEvent;
use SymfonyComponentEventDispatcherEventSubscriberInterface;
use SymfonyComponentMessengerEventWorkerRunningEvent;
/**
* Class ExtractFailedEvent
* @package AppEvent
*/
class CronRunningEvent implements EventSubscriberInterface
{
public function onWorkerRunning(WorkerRunningEvent $event): void
{
if ($event->isWorkerIdle()) {
$event->getWorker()->stop();
}
}
/**
* @return array<string>
*/
public static function getSubscribedEvents()
{
return [
WorkerRunningEvent::class => 'onWorkerRunning',
];
}
}
Which works correctly after testing, in the sense of it stops when all emails are consumed from the database. Because of the cron limitations, the following was created to execute the messenger command:
cron.php
<?php
require __DIR__.'/vendor/autoload.php';
use SymfonyComponentProcessProcess;
use SymfonyComponentProcessExceptionProcessFailedException;
// Build the command
$command = 'php bin/console messenger:consume async';
// Execute the command
$process = new Process(explode(' ', $command));
$process->run();
// Check for errors
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
// Output
echo $process->getOutput();
Which again functions correctly after testing. Now all that is left is to have both modes working somehow. The current messenger config is as follows:
messenger.yaml
framework:
messenger:
transports:
# https://symfony.com/doc/current/messenger.html#handling-messages-synchronously
sync: 'sync://'
routing:
SymfonyComponentMailerMessengerSendEmailMessage: sync
SymfonyComponentNotifierMessageChatMessage: sync
SymfonyComponentNotifierMessageSmsMessage: sync
And sending emails pretty basic:
public function sendTestEmail($toEmail, $subject, $body)
{
try {
$email = new TemplatedEmail();
$email->from(new Address('...', '...'));
$email->to($toEmail);
$email->replyTo(new Address('...'));
$email->subject($subject);
$email->html($body);
$args = [
'category' => "...",
];
$email->getHeaders()->addTextHeader('X-SMTPAPI', json_encode($args));
//$email->getHeaders()->addTextHeader('X-Transport', 'sync');
$email->getHeaders()
// this non-standard header tells compliant autoresponders ("email holiday mode") to not
// reply to this message because it's an automated email
->addTextHeader('X-Auto-Response-Suppress', 'OOF, DR, RN, NRN, AutoReply');
$this->mailer->send($email);
} catch (Exception $ex) {
$this->logger->critical('Unable to send test email', [
'exception' => $ex->getMessage(),
]);
return false;
}
return true;
}
With all this being said, can someone please guide me on how to configure so that both sync://
and `async://“ can be used whenever needed? Try to explain to me in baby steps, I really don’t mind as long as I can fully understand what and how.
Thanks
user24640806 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.