Hi everyone! I encountered a pretty weird issue today. I had to implement some Mail/Notification throttling today in order to respect our mail service provider's limit per second.
This is a very simplified test of what I have:
class TestMail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Test Mail',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'emails.test',
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
for ($i = 0; $i < 10; $i++) {
$mail = (new TestMail)->delay($i);
Mail::to('[email protected]')->send($mail);
}
Expected behavior : One mail is sent per second.
Actual behavior (queue output) :
2023-05-24 18:25:51 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:25:51 App\Mail\TestMail ......................... 33.32ms DONE
2023-05-24 18:25:55 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:25:55 App\Mail\TestMail ......................... 13.70ms DONE
2023-05-24 18:25:55 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:25:55 App\Mail\TestMail ......................... 22.25ms DONE
2023-05-24 18:25:55 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:25:55 App\Mail\TestMail ......................... 19.24ms DONE
2023-05-24 18:25:55 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:25:55 App\Mail\TestMail ......................... 14.25ms DONE
2023-05-24 18:25:58 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:25:58 App\Mail\TestMail ......................... 12.84ms DONE
2023-05-24 18:25:58 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:25:58 App\Mail\TestMail ......................... 46.36ms DONE
2023-05-24 18:25:59 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:25:59 App\Mail\TestMail ......................... 16.89ms DONE
2023-05-24 18:25:59 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:25:59 App\Mail\TestMail ......................... 25.09ms DONE
2023-05-24 18:26:02 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:26:02 App\Mail\TestMail ......................... 13.40ms DONE
As you can see there are sometimes a substential delay between mails, sometimes none at all.
However, if you increase the delay to 5 seconds ($i * 5), the behavior is somewhat ok, but you can still see some lack of precision (sometimes it's 3 seconds between, sometimes 6 seconds).
2023-05-24 18:29:10 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:29:10 App\Mail\TestMail ......................... 35.21ms DONE
2023-05-24 18:29:16 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:29:17 App\Mail\TestMail ......................... 22.50ms DONE
2023-05-24 18:29:20 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:29:20 App\Mail\TestMail ......................... 14.34ms DONE
2023-05-24 18:29:26 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:29:26 App\Mail\TestMail ......................... 21.55ms DONE
2023-05-24 18:29:33 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:29:33 App\Mail\TestMail ......................... 15.66ms DONE
2023-05-24 18:29:36 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:29:36 App\Mail\TestMail ......................... 16.54ms DONE
2023-05-24 18:29:42 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:29:42 App\Mail\TestMail ......................... 14.01ms DONE
2023-05-24 18:29:46 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:29:46 App\Mail\TestMail ......................... 17.23ms DONE
2023-05-24 18:29:52 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:29:52 App\Mail\TestMail ......................... 23.15ms DONE
2023-05-24 18:29:55 App\Mail\TestMail .............................. RUNNING
2023-05-24 18:29:55 App\Mail\TestMail ......................... 12.44ms DONE
Is it a precision limitation of the framework or am I missing something ?
P.S. The current "workaround" I have in place is this :
->delay(floor($i / $emailsPerSecond) * $delayBetweenBatches)
Basically, it will send X emails per seconds but will introduce a X second delay before the next "batch" starts. In this context, the "batch" I'm talking about is an actual Batch instance, it is just the raw concept of adding a delay after a certain amount of dispatched notifications. It's not perfect because I'm losing some performance by having to wait X seconds between each "batches", but it's what I settled with for now.