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

Ignored Resend rate limiting, Laravel failover configuration doesn't work #61

Open
MrMarci666 opened this issue Jun 27, 2024 · 7 comments

Comments

@MrMarci666
Copy link

Intro

Hello there,

We experienced many issues with this package, because it is simply ignoring the rate limitation of Resend.

By default, Resend has a rate limit of 2 requests per second for API calls. 1 This rate limit is designed to manage the load on their API and ensure fair usage across all customers.

However, Resend understands that some users may need higher rate limits for their specific use cases. If you find that the default rate limit is not sufficient for your needs, you have the option to request an increase. To request a rate limit increase, you should contact Resend support. Here's what you need to know:

The default rate limit can be increased for trusted senders upon request. 2. If you have specific requirements that exceed the default limit, you should reach out to Resend support to discuss your needs. 3. You can contact Resend support through their website at https://resend.com/contact to request a rate increase. It's worth noting that Resend provides response headers that help you monitor your current rate limit usage. These headers include:

  • ratelimit-limit: Maximum number of requests allowed within a window
  • ratelimit-remaining: How many requests you have left within the current window
  • ratelimit-reset: How many seconds until the limits are reset
  • retry-after: How many seconds you should wait before making a follow-up request These headers can help you manage your API requests and avoid hitting the rate limit.

If you do exceed the rate limit, you'll receive a 429 response error code. To prevent this, Resend recommends implementing strategies such as introducing a queue mechanism or reducing the number of concurrent requests per second. Remember, while requesting a rate limit increase is possible, it's also important to optimize your API usage to work efficiently within the provided limits when possible.

We are using a "Failover" configuration in Laravel 11.

'failover' => [
  'transport' => 'failover',
  'mailers' => [
    'resend',
    'smtp',
    'log',
  ],
],

Expected behaviour

I expect when I want to send out E-Mails using Resend mail driver in Laravel, it should work. If Resend has a built-in 2 requests per second rate limit, and also rate limit information response headers, the package should be prepared to read and use this information, and send out the mails via Resend correctly.

Also, if the requests fails, Laravel should use the next E-Mail sending method configured in the failover configuration. I am not sure that this is a Laravel issue or a Resend package issue, but it doesn't work right now, if the response is 429 from Resend, Laravel won't try to send out with the next configured E-Mail sender.

Issue description, screenshots

So we just released a webshop with around 15.000 users and wanted to send them a Password Change E-Mail. However, this resulted with a disaster that the Resend API responded with 429 in many cases, and Laravel didn't try to send it out with the failover SMTP server, it just threw an error... 👎
Screenshot 2024-06-27 at 10 06 42
Screenshot 2024-06-27 at 16 23 02

@jayanratna
Copy link
Collaborator

Hey @MrMarci666, apologies for getting on to this one so late.

I believe this could be caused by versions of the library prior to PR #58 or prior to v0.12.0. The underlying mailing system of Laravel depends on the TransportException type to "failover" to the next mailer.

If you upgrade to the latest version, this should be fixed. Let me know how it goes.

@MrMarci666
Copy link
Author

MrMarci666 commented Jul 10, 2024

@jayanratna Thanks for the reply. Sounds good! :)
Yes, I can confirm we were using v0.11.0 at the time, so that was solved.

But the main issue still remains I assume... That the package is basically ignoring a very core function of the API, the rate limitation.

@jayanratna
Copy link
Collaborator

That is something I am currently investigating. I'm wondering what the best way to expose the rate limit information would be.

Currently there is a functionality to expose the ID of the sent email with the X-Resend-Email-ID, perhaps it could be done the same way. Though that header is only available on successful requests 🤔

@MrMarci666
Copy link
Author

I see, cool. Hmmm let me summon also @floodx92. (We faced this issue in a project together)
What do you think @floodx92, what would be the best solution to this?

@elinardo10
Copy link

I'm having this same problem and I'm using version 0.14.0. Is there a solution yet?

@elinardo10
Copy link

I have a soluction for now.
1 create a job to send every emails and when call then define delay:
SendEmailJob::dispatch($event)->delay(now()->addSeconds(3))->onQueue('send_emails');

  1. Get error and filter in My job:
    public function handle()
    {
        try {
            Mail::to($this->recipient)->send(new SendNewsletter($this->data));
        } catch (TransportException $e) {
            if (str_contains($e->getMessage(), 'Too many requests')) {
                // Handle the specific "Too many requests" exception
                $this->release(10); // Delay the job for 10 seconds before retrying
            } else {
                // Rethrow the exception if it's not related to rate limiting
                throw $e;
            }
        }
    }

@flatcapco
Copy link

I have a soluction for now. 1 create a job to send every emails and when call then define delay: SendEmailJob::dispatch($event)->delay(now()->addSeconds(3))->onQueue('send_emails');

  1. Get error and filter in My job:
    public function handle()
    {
        try {
            Mail::to($this->recipient)->send(new SendNewsletter($this->data));
        } catch (TransportException $e) {
            if (str_contains($e->getMessage(), 'Too many requests')) {
                // Handle the specific "Too many requests" exception
                $this->release(10); // Delay the job for 10 seconds before retrying
            } else {
                // Rethrow the exception if it's not related to rate limiting
                throw $e;
            }
        }
    }

Did you ask resend to up your rate limit, they upped mine massively and then I just use a middleware restriction for 'resend-emails' thats massively under that limit (resend set it to 250/s and I'm using 50/s)

Could be as simple as that now and quicker than queuing up for so long?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants