Laravel Jobs are not processed when triggered by a Redis subscribed message - laravel

I've some mistake with a job that I try to dispatch when my Redis subscribe command receive a message.
I launch the Redis subscribe inside an "artisan console" command :
Redis::subscribe(['channel'], function ($message) {
dipatch((new MyJob($message)
->onQueue('default')
->onConnection('redis'));
}
Job is created and I can see it on my Laravel Horizon dashboard. But it's never processed... "handle" function is never called and the job stay in "pending" tab on Horizon.
But when I dispatch it from a tinker session, that's work fine!
Maybe I have to call another artisan command to launch the job outside the redis subscribe function, but hope there is a better solution...
Any ideas?

Solution :
create another connection on the database.php file for your Redis database (same params as the default one, just change the name, eg name it "subscribe"), so after that change, the code must look like this :
$redis = Redis::connection('subscribe');
$redis->subscribe(['channel'], function () {});

I had the same problem, The job will never be dispatched on Redis queue. When I used another queue driver (database or beanstalkd) it worked fine.

Related

dynamic smtp not working with queueble mail in laravel

trying to implement dynamic SMTP with laravel it works fine without queue mail but when adding queue to mail it takes default mail SMTP details
dispatch(function () use ($details, $email, $name, $tutorEmail, $brandName, $ownerEmail, $createdBy, $mailSubject) {
Mail::send('mail.mailSchedule', $details, function ($m) use ($email, $name, $tutorEmail, $brandName, $ownerEmail, $createdBy, $mailSubject) {
$m->to($email, $name);
$m->from($ownerEmail, $brandName);
$m->subject($mailSubject);
});
})->delay(now()->addSeconds());
Dynamic configuration in mail is not compatible with queue jobs because the configuration settings, such as the server address and port, may change while the job is in the queue. When the job is pulled from the queue and executed, the configuration settings may no longer be valid, resulting in the job failing to send the email.
The solution of this problem is to pass the email configuration settings as an argument when the job is pushed to the queue, so that the job has access to the correct settings at the time of execution.

Laravel job executing real time without queue being started

Running on Laravel 5.8
I am creating a large number of Jobs which I believe should be executed once the queue has been initiated.
My issue is that the jobs get executed then and there when I haven't even started the queue.
They are not even being inserted in to the jobs table created by the migration.
Below are the settings and the piece of code I believe is relevant. Please let me know if more info is needed.
On a fresh installation:
php artisan queue:table
php artisan migrate
.env file
QUEUE_CONNECTION=database
Created the queuedtask
class FulfillmentTask implements ShouldQueue{
//code here
}
Controller
use App\Jobs\FulfillmentTask;
//rest of the class here
public function somefunction(Request $request){
//some code here
//read csv file
foreach ($fileContents as $row){
FulfillmentTask::dispatch($orderId, $client, $request->sendEmail)->onQueue('database');
}
}
Issue is the FulfillmentTask is executed without the queue:work command being given in the terminal.
Any idea on why this is happening?
"database" is a queue connection. Pls dispatch your job to that connection.
FulfillmentTask::dispatch($orderId, $client, $request->sendEmail)->onConnection('database');
And that seems to be the default connection so you just dispatch the job.
FulfillmentTask::dispatch($orderId, $client, $request->sendEmail);

What is the right way to send bulk mail with Laravel via AWS SES?

I've SES approved sending rate of 500 emails / second. When I try to send bulk email via Laravel using SES API, the actual mail sending rate is very slow (about ~100 per minute).
Here's an overview of how I do it -
...
Users::latest()->chunk(100, function($users) use($newsletter) {
Notification::send($users, new SendNewsLetter($newsletter)); // queued
})
My guess was that I'd send about 100 mails in one shot, however, Horizon shows that the queue which I'm using will have long wait time (of several seconds).
Can someone inform me what is the right way to send bulk emails using SES and Laravel?
First, i recommand you change the .env file setting for QUEUE_DRIVER=sync to QUEUE_DRIVER=database.
sending Email action
$users = Users::latest()->take(100)->get();
Mail::queue('send', ['users' => $users], function($m) use ($users) {
foreach ($users as $user) {
$m->to($user->email)->subject('YOUR SUBJECT GOES HERE');
}
});
As next step you need to create a Queue table in the database using
the following command before clicking on the route:
PHP artisan queue: table
PHP artisan migrate
Before starting the project you need to run a listener to listen to
the Queue Request. But you're gonna introduce with the new method as
listen is high CPU usage. It's better to use a daemon. So Run the
following command:
PHP artisan queue:work --daemon --tries=3
Source

laravel event dispatch from controller

Alright so I have been trying to set up a spreadsheet application with concurrent editing. I went down the laravel echo, redis, sockets route. (Any advice to just use pusher will be summarily dismissed). Now for the most part I have this working and I can call my event from tinker and see the data I want flow through redis to sockets and get picked up by my frontend. However the call simply does not work when placed in a controller; its like its just being ignored. For simplicity sake I removed all code from the controller function except for event(new SalesPricingReportEdit($request)); I can use this exact code in tinker storing the exact source of the request from dev tools {"change":[[0,"future contract price",null,750]],"customer_id":101443,"item_id":"MOFT0602-1-550"} and my ws responds on multiple instances of the page across different computers with the data attached to the WS. I can post any code anyone needs to help just let me know what you want to see.
EDIT(resolved): Alright so now that I am mostly finished with this project I have my write up I figured Id post it for posterity this is a process guide that lead me to successfully using laravel events with redis and socket-io via echo.
Register your new Event in laravel go to app/Providers/EventServiceProvider.php and put in the fully namespaced path for your Event that doesnt exist yet. Using the default namespace will save you a headache in implemntation but you can use a non-default
if you wish to gouge your own eyes out while you attempt to route the events on your frontend.
protected $listen = [ 'App\Events\EventName.php' => [
//you would put a handler here but for my case i was only dealing with an
//Event generated by server and handled by the client(browser)
//so it wasn't needed. KISS
],
Once you save that you just use the artisan generate event command and it will read the listener array and scaffold you up an event.
note: While we are in the providers folder lets just note the broadcastServiceProvider and its reference to the routes/channel file this isn't very important for me now but its good to remember
In your app/Events/EventName.php we will be configuring our event. Most of this is standard but there will be a few gotchas I dont want to forget about.
your class should by default implement ShouldBroadcast ... I changed this to shouldBroadcastNow as well as setting my default QUE_DRIVER=sync in global .env and config to sync .. this just avoids an extra overhead of queing the jobs..probably dumb in the long term but, for now queing is a bit overkill.
continue in app/Events/EventName.php
Next notice your construct function. To pass data through an event you need to declare a public variable in the class and set the value for that inside your constructor. By Default laravel will/should/suppposed to pass any public variable in the
class with the event.
public $change;
public function __construct($request){
$this->change = json_decode($request, true);
}
Next in your broadcastOn() function you want to return a new channel...essencially
return new [Type]Channel([channel-name]);
make sure you have all the facades you need in your header.
the broadcastWith() function will let you manipulate the data sent with the event.
I did not use the broadcastAs() function but, it is supposed to allow you to change the Event Name rather than using the default class name.
Now we are going to go to our controller file where we plan to actually fire the event from app/Http/Controllers/xxxxxxxxxController.php
use the syntax
event(new EventName($data))
when you want the event to get kicked off. this will not work as we have not set up redis or sockets but to test this is all in working order go to your .env and change the BROADCAST_DRIVER=log. once done wipe cache and do whatever it is you need to do to get the controller function to run(either by using tinker or by a frontend path you plan on using with the event). you should then be able to see the storage/app/logs/laravel.log contain the information from your event.
Go ahead and install redis on your server and use basic laravel configuration in .env and in config/database.php the only thing to change is to add in the .env BROADCAST_DRIVER=redis.
To avoid some complications go ahead and make sure your CACHE_DRIVER to file.
I have not yet set up multi connections with redis to let redis work both as broadcast and cache; ik its possible but I havent done it. so for now cache_driver can go away.
Also I tried to set this up using PHPredis but I admitted defeat and just used predis. Anyone that can figure this out I will venmo 50 bucks.
Finally to test that you have done this correctly go into
redis-cli > PING ...resp: PONG
Also you can use redis-cli monitor to verify that event data is indeed being pushed to redis.
Now we will start setup with npm we are going install laravel-echo-server, I will also get the pm2 package to act as my daemonizer for echo server.bash it up with
` #!/var/www/repoting/ bash
laravel-echo-server start`
then just run pm2 start socket.sh
when testing you might want to run laravel-echo-server start straight from the cli because the output stream will let you know when it observed an event broadcast in redis.
we will also need the
npm --save socket.io-client
or websockets don't work.
Lets go to resources/assets/js/bootstrap.js
import Echo from "laravel-echo";
window.io = require('socket.io-client');
if (typeof io !== 'undefined') {
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':6001',
});
}
This is initializing our websocket connection.
Now in our blade file we are going to subscribe to the channel and listen for events. Make sure to include the :6001/socket.io/socket.io.js file or your callbacks will fail
Why are we not doing this in the bootstrap.js you ask? because part of the channel name I am using is being passed by the controller with the view so to subscribe to the right channel I need something like
channel-name-{{$customer_id}}.
What you say I should have properly set up vue and done things the way they were designed to be done in vue....go home your drunk.
So now in resources/views/xxxxx.blade.php
I am using this in conjunction with handsontables so I am going to insert my listener in the afterRender hook.
Echo.channel('EventName-{{$customer_id}}')
.listen('EventName',function(data){
console.log(data);
});
any confusion here about what goes in the channel vs what goes in the listen clause can think of it as using the channelName and then the eventName by default the class name. There seems like in the past to have been some issues
with the class name being namespaced properly but I did not experience any problems.
this should all lead to seeing a console.log with your event data on any page subscribed to the the channel. But for prosperity sake here is the testing order to help verify each step along the way.
Troubleshooting steps
Is the event configured properly in laravel? Change the BROADCAST_DRIVER=log and verify you get a log entry when you run your controller.
Is the data actually being pushed to redis? with the right channel, etc? open redis-cli montior while you trigger an event and you should see feedback like "PUBLISH" "channel-name" "message"
Is echo server/socketio-server registering the event? look at your terminal that is running the laravel-echo-server start command you should see the event propagate from redis to echo there
Is your bootstrap.js file giving you an open web socket? go to dev-tools and check the network->ws if you see an entry click it and then on messages; make sure you see sent and recieved data. If you dont something is wrong in bootstrap.js
Is your clientside js retrieving data from the websocket event? if not you probably forgot to include you socket.io.js file.
Next Steps
-Implement whisper channels to notify the page of who is currently using the page as well as to unsubscribe the from the echo channel and remove any unneeded listeners
-pass handsontable selected data through the whisper channel and set those cells to readonly where the user is different from the current user
I ended up just adding the redis facade and calling Redis::publish(). My guess here is that I couldn't call an event without having a listener setup. I don't need a listener I just needed the event to fire and push it to redis. the real confusing part is why this works without a hitch in tinker and not in a controller is still a mystery but, I solved the immediate problem so this just gets chalked up to an edge case where I implemented something differently than it was probably designed to be.

Laravel event not being Broadcast

I am working on a chat/message feature for an app. When a User sends a message, I want a specific Event to be triggered and to be broadcasted. I am then wanting Laravel Echo to listen on the event's broadcast channel to an event via Laravel Echo, using the pusher driver.
I am wanting the Laravel Echo implementation to be in a Vue component and to retrieve the data that was broadcasted via the event. Currently, I can't get an event to broadcast to either a private channel or just a channel.
Here is what I have done:
1) Added the config to .env.
2) Added config to bootstrap.js :
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'mykey',
encypted: false
});
3) Added auth check in App\Providers\BroadcastServiceProvider (just returning true if User is signed in for now):
Broadcast::channel('chat', function ($user) {
// verify that user is recipient of message
return \Auth::check();
});
4) Added App\Providers\BroadcastServiceProvider::class, to App/config/app.php.
5) Created the Event App\Events\UserSentMessage, which broadcasts on a PrivateChannel named chat. I have also tried to broadcast to a Channel. I checked and the event is being fired from a call to the event() helper in a controller, it just seems to not be broadcast. I have also tried using the broadcast method in the controller mentioned.
6) Added the following within the mounted Vue life cycle hook in a valid Vue component (component is rendering data etc as expected, just not working with Laravel Echo atm):
Echo.private('chat').listen('UserSentMessage', (data) => {
console.log(data);
}, (e) => {
console.log(e);
});
Have also tried:
Echo.channel('chat').listen('UserSentMessage', (data) => {
console.log(data);
}, (e) => {
console.log(e);
});
The Vue component is successfully subscribing to the channel specified. The pusher debug console recognized that it is being subscribed to, but the Event, when triggered never gets broadcasted.
Seems to be that there is a problem with broadcasting the event. I have also tried using the broadcast() helper, rather than the event() helper.
Also, when subscribing to the event with Laravel Echo via Echo.private(). It seems to prefix the channel name with private, so I have also tried broadcasting (in the event) to a channel named private-chat.
I can recommend You to check Your .env file and make sure BROADCAST_DRIVER is not log (try pusher) and also don't forget to keep running queue listener:
php artisan queue:listen
UPD.: if You look for more advanced solution, read about laravel horizon
the event will not be broadcasted immediately, it will be send to the queue, only when you listen or work on the queue, the event will be consumed. If you want execute jobs immediately, set QUEUE_DRIVER=sync . see: laravel queue
In my case, my event class file is generated by "make:event", it does have broadcastOn method... But have no implements ShouldBroadcast~
It causes the laravel-echo-server output nothing~
Laravel Framework 8.13.0
I have upvoted Num8er's reply but i also found some other issues so maybe they can help you too.
1) Remove all 'private-' from the beginning of your channel names. These get handled behind the scenes by the various libraries (eg socket.io/laravel-echo). If you look at any debug output you will see 'private-' prepended to the front of the channel names but you DO NOT need to add these.
2) If you're using redis then make sure the REDIS_PREFIX is set to an empty string in your .env file. By default it's set to something like 'laravel_database' and this messes up the channel names.
REDIS_PREFIX=
3) Make sure you've got your laravel-echo server running (and redis/pusher). Also you need to make sure you have the queue worker running:
php artisan queue:work
4) It's worth enabling debugging output on your Echo Server as this will tell you useful info about traffic (eg channel names) so modify the laravel-echo-server.json file (in your project root dir) so that devMode is set to true:
"devMode": true,
#materliu: that was a good hint. In my case i did not create a queue with the working directory set to the project i am working on. Using circus. The event was triggered in code but no job listener was available. As soon as i run "php artisan queue:listen" suddenly all previous broadcasts have been processed.
Usually its difficult to figure out which component of a complex system does not work.

Resources