Laravel version 8.83.17.
I've been using laravel for years and this seems to be a weird problem only occured in one of my mac machine.
Queue driver is using local Redis. have set QUEUE_CONNECTION=redis. And connection is ok.
Then Run php artisan queue:work --queue=test
on one of the command window
class TestJob implements ShouldQueue {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct() {}
public function handle() {
Log::debug('TestJobRun');
}
}
Running TestJob::dispatch()->onQueue('test')
on tinker to dispatch a simple job TestJob. Nothing happend.
until I run TestJob::dispatchSync()
, suddenly the previous job fired together. with the logs 'TestJobRun'
[2023-09-14 19:09:15][a4kFc8yYeVg3agjsFnYP0K5BYbH9NtiD] Processing: App\Jobs\TestJob
[2023-09-14 19:09:15][a4kFc8yYeVg3agjsFnYP0K5BYbH9NtiD] Processed: App\Jobs\TestJob
I just dont know why. how to debug this?
I tried thanging queue, sleep, timeout settings for queue:work. Tried swiching to queue:listen
or supervisor ways. Nothing helped.
This is not related to the queue you're trying to use. It is related to trying to dispatch a job from tinker.
The dispatch()
method creates a PendingDispatch
object instance. The job is sent to the queue from the destructor of the PendingDispatch
instance, so the job will not be queued until this instance is destroyed.
When running commands in tinker, the result of the last command is kept in memory. Because the result of your last command was the PendingDispatch
instance, and it is being kept in memory, the destructor is not being called on it, and the job is not dispatching.
The next command you run will push the PendingDispatch
instance out of memory, call the destructor, and your job will be sent to the queue.
This is why your second call to TestJob::dispatchSync()
dispatched two jobs. The first one was the one from the PendingDispatch
instance finally being destroyed, and the second one was the one from the call to TestJob::dispatchSync()
(which does not use a PendingDispatch
instance).
In tinker, instead of using the ::dispatch()
method on the job, you can use the Bus::dispatch()
method directly.
Bus::dispatch((new TestJob())->onQueue('test'));
Or, you can append another command to your dispatch command so that the result of your command isn't the PendingDispatch
instance. Ex:
TestJob::dispatch()->onQueue('test');echo;
Now the result of your command will be the result of the appended echo;
instead of the PendingDispatch
instance returned from the distpatch()
. Therefore, the PendingDispatch
instance will immediately be destroyed and your job will dispatch.
NB: this is also noted in the Laravel documentation:
The
dispatch
helper function anddispatch
method on theDispatchable
class depends on garbage collection to place the job on the queue. Therefore, when using tinker, you should useBus::dispatch
orQueue::push
to dispatch jobs.