Our emails are failing to send using Laravel with a Redis Queue.
The code that triggers the error is this: ->onQueue('emails')
$job = (new SendNewEmail($sender, $recipients))->onQueue('emails');
$job_result = $this->dispatch($job);
In combination with this in the job:
use InteractsWithQueue;
Our error message is:
Feb 09 17:15:57 laravel: message repeated 7947 times: [ production.ERROR: exception 'Swift_TransportException' with message 'Expected response code 354 but got code "550", with message "550 5.7.0 Requested action not taken: too many emails per second "' in /home/laravel/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php:383 Stack trace: #0 /home/laravel/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php(281):
Our error only happens using Sendgrid and not Mailtrap, which spoofs emailing sending. I've talked with Sendgrid and the emails never touched their servers and their service was fully active when my error occurred. So, the error appears to be on my end.
Any thoughts?
You need to rate limit
emails
queue.The "official" way is to setup Redis queue driver. But it's hard and time consuming.
So I wrote custom queue worker mxl/laravel-queue-rate-limit that uses
Illuminate\Cache\RateLimiter
to rate limit job execution (the same one that used internally by Laravel to rate limit HTTP requests).In
config/queue.php
specify rate limit foremails
queue (for example 2 emails per second):And run worker for this queue:
I had this problem when working with mail trap. I was sending 10 mails in one second and it was treated as spam. I had to make delay between each job. Take look at solution (its Laravel - I have
system_jobs
table and usequeue=database
)Make a static function to check last job time, then add to it
self::DELAY_IN_SECONDS
- how many seconds you want to have between jobs:Then use it to make sending messages with delay (taking in to consideration last job in queue)
For debugging only!
If you don't expect more then 5 emails and don't have the option to change mailtrap, try:
Using sleep() is a really bad practice. In theory this code should only execute in test environment or in debug mode.
I finally figured out how to set up the entire Laravel app to throttle mail based on a config.
In the
boot()
function ofAppServiceProvider
,In
config/mail.php
, add this line:In your
.env
files, add a line like:The only problem is that it doesn't seem to affect mail sent via the
later()
function ifQUEUE_DRIVER=sync
.Maybe you should make sure it was really sent via Sendgrid and not mailtrap. Their hard rate limit seems currently to be 3k requests per second against 3 requests per second for mailtrap on free plan :)
I achieved this on Laravel v5.8 by setting the Authentication routes manually. The routes are located on the file
routes\web.php
. Below are the routes that needs to be added to that file:Explanation:
Auth::routes();
to let configure the authentication routes manually.email/resend
inside aRoute::group
with the middlewarethrottle:1,1
(the two numbers represents the max retry attempts and the time in minutes for those max retries)I also removed a line of code in the file
app\Http\Controllers\Auth\VerificationController.php
in the__construct
function.I removed this: