1

My application is hosted on a shared hosting platform that limits to 200 emails per hour.
My application is running database connection driver and I have 3000 jobs in the jobs table.
I would like to throttle this queue to only send 1 email every 30 secs or 1 minute to ensure I don't run issues with my hosting.

Research: I have tried delay from this tutorial, rate limiting from this question with no response, delayed jobs from this laravel documention but nothing works.

Question: Is there a way to throttle the queue in database queue connection like they do it in redis queue connection i.e

// Allow only 1 email every 30 seconds
Redis::throttle('any_key')->allow(1)->every(30)->then(function () {
    Mail::to($this->email)->send(new NotificationEmail($this->data) );
    Log::info('Emailed order ' . $this->email);
}, function () {
    // Could not obtain lock; this job will be re-queued
    return $this->release(2);
});

My implementation: Delays only for the first job then sends the others with no delay

public function sendEmailNotification($email,$data)
{
    //Send email to user and to admin
    $email_job = (new ProcessEmailNotificationJob($email,$data))->delay(now()->addSeconds(30));
    
    if($this->dispatch($email_job)){
        return true;
    }
    else{
        return false;
    }
}

**ENV File: **

BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=database
SESSION_DRIVER=file
SESSION_LIFETIME=120

MEMCACHED_HOST=127.0.0.1
Lefty
  • 1,192
  • 13
  • 18
  • 1
    Make a command that only dispatches one job when it's ran. In `App/Console/Kernel.php` register that command and schedule it for every 30 secs or 1 minute. That's how we've set it up for running one job every x seconds. – geertjanknapen Jul 07 '22 at 10:52
  • Does this answer your question? [How do I time delay a job in Laravel 5.2?](https://stackoverflow.com/questions/39582935/how-do-i-time-delay-a-job-in-laravel-5-2) – Bhimani Rutvik Jul 07 '22 at 11:01
  • @BhimaniRutvik This tutorial suggests exactly what I've used. This solution only delays the first email then sends the rest without delay as explained in my post – Lefty Jul 07 '22 at 11:59
  • @geertjanknapen thank you, but that command will have to be called by a cron job every 30 seconds for it to fire and my shared host has a minimum of 5 minutes. Each email might be sent every 5 minutes. – Lefty Jul 07 '22 at 12:01
  • @Lefty Okay, well depending on the use case, you should consider if 5 minutes works for you. Otherwise you could look at the answer below and maybe that helps. – geertjanknapen Jul 07 '22 at 12:38

2 Answers2

0

did you run php artisan queue:listen, if yes check my below code maybe it will help

inside your controller:

$mail = (
    [
       'data' => $EmailData,
       'userName' => $userData->first_name,
       'userMail' => $userData->email,
       'subject' => $subject
    ]);
                    
SendMailJob::dispatch($mail)
    ->delay(now()->addSeconds($waitSec));
$waitSec += 30; //seconds interval

SendMailJob class

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Mail\ClientRegistrationNotification;
use Mail;
class SendMailJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $bulkMail, $mail;
    public function __construct($Mail)
    {
        $this->mail=$Mail;
    }

    public function handle()
    {
        try  {
                Mail::to($this->mail['userMail'])
                     ->queue(new ClientRegistrationNotification($this->mail['data'], $this->mail['userName'], $this->mail['userMail'], $this->mail['subject']));

             } catch (\Throwable $exception) {
                $this->fail();
             }
    }
}
Lordwiz
  • 13
  • 1
  • 3
0

I'm not sure if this is the best option but the only solution that worked for me was sleeping the queue for 30 seconds after dispatch i.e

Dispatch Method

public function sendEmailNotification($email,$data)
{
    //Dispatch all 3000 with no delay
    if(ProcessEmailNotificationJob::dispatch($email,$data)){
        return true;
    }
    else{
        return false;
    }
}

Sleep Queue Sender

class ProcessEmailNotificationJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $email;
    private $data;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($email,$data)
    {
        //
        $this->email = $email;
        $this->data = $data;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        //Send the Mail and sleep for 30 Seconds before sending the next one
        sleep(30);
        Mail::to($this->email)->send(new NotificationEmail($this->data) );

    }
}
Lefty
  • 1,192
  • 13
  • 18