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

[8.x] Fix scheduler unique job #39197

Closed
wants to merge 2 commits into from
Closed

[8.x] Fix scheduler unique job #39197

wants to merge 2 commits into from

Conversation

ericsssan
Copy link

Hi maintainers

I discovered a bug where scheduling job that implemented ShouldBeUnique failed to enforce uniqueness.

I have included test case and possible fix.

Please review.

Thanks

@taylorotwell
Copy link
Member

@paras-malhotra do you have any thoughts on this?

@GrahamCampbell GrahamCampbell changed the title Fix scheduler unique job [8.x] Fix scheduler unique job Oct 14, 2021
@paras-malhotra
Copy link
Contributor

I dont believe unique jobs were ever meant for task scheduling. @ericsssan what is your use case here and can it not be solved using withoutOverlapping or onOneServer?

@ericsssan
Copy link
Author

ericsssan commented Oct 20, 2021

Hi

Use case:
I have a job called ProcessDelayedSMSToSendOut. This job runs every minute to query db to get a list of sms notifications and put them into Redis queue to be sent out. This job sometimes take more than a minute to complete. If it takes more than a minute the scheduler will dispatch another job overlapping the previous job which results in duplicate sms being sent out unintentionally. This happened to us. Some recipients received up to 5 identical sms at once and it went on for months.

Objective:
What we don't want to happen is to have multiple instance of the above mentioned job in the queue at any one time. We have limited resource on the redis server. Using ShouldBeUnqiue interface works for us. The problem is with the job method.

What works:

  • I can dispatch the job from a command class.
  • I can dispatch the job from schedule->call() callback.

What does not work:

  • Dispatch with schedule->job()

https://laravel.com/docs/8.x/scheduling#scheduling-queued-jobs

According to the doc , job method in Task Scheduling is a convenient method for call. But dispatching a unique job from scheduler within call method callback works as expected whereas dispatching with job method does not.

If you believe that sheduler is not meant to dispatch unique job then I think we can just put it in the doc to warn others about it.

I just think that dispatching a job should work as expected regardless of where you dispatch it. But the problem is we get a different dispatcher when we dispatch with schedule->job() and that makes it inconsistent.

Cheers
Eric

@taylorotwell
Copy link
Member

Do you have a good understanding of why your change fixes it? If so, can you explain?

@ericsssan
Copy link
Author

Hi @taylorotwell

Yes.

When dispatching a job elsewhere it goes through ShouldDispatch method in PendingDispatch class.

$uniqueJob::dispatch(); or dispatch(new UniqueJob);

But when using $schedule->job(), it does not go through PendingDispatch class.

$this->dispatchToQueue($job, $queue ?? $job->queue, $connection ?? $job->connection);

Here is the job method call dispatchToQueue method.

$this->getDispatcher()->dispatch(

Then goes on to call getDispatcher method and chain call dispatch method.

protected function getDispatcher()

And here, getDispatcher simply just resolve Dispatcher from a container and dispatch without going through PendingDispatch class.

I can think of 2 ways to fix this:

  1. in dispatchToQueue method, just use dispatch helper function to dispatch the job. But using helper function is probably a bad idea.
- $this->getDispatcher()->dispatch(
-           $job->onConnection($connection)->onQueue($queue)
-        );
+ dispatch($job)->onConnection($connection)->onQueue($queue);
  1. do exactly what the dispatch helper function does and be more explicit.

image

Please let me know if I missed out something.

Cheers
Eric

@taylorotwell
Copy link
Member

Replacing with #39302 because using Foundation classes from the console component isn't really allowed. Had to refactor some things into the Bus component.

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

Successfully merging this pull request may close these issues.

3 participants