I have the script below which is used to send messages (sms, email & whatsapp) to some 300k customers. Things where working fine before but for some days now the script is bugging (it freezes at some point in the loop. I can see this with the progress bar which doesn't move further). I have checked laravel.log and there is no particular error message. The script itself does not output any error message.
Below is the code:
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
AccountsCstm::where('authentification_c', 1)->chunk(10000, function ($customer_details) {
$bar = $this->output->createProgressBar(count($customer_details));
$bar->start();
foreach ($customer_details as $customer_detail) {
// Get customer bills and send through the appropriate channel. He could have multiple bills
if (isset($customer_detail->account->sic_code)) {
$bills = TbBillsInfos::where('contract_number', $customer_detail->account->sic_code)->where('bill_status', 'UNPAID')->get();
if (isset($bills)){
foreach ($bills as $bill) {
// Send messages.
if ($customer_detail->is_whatsapp_c) {
$message = $this->bill_message($customer_detail, $bill, 'WhatsApp');
// Send the message
$this->send_whatsapp_message($customer_detail->phone_4_c, $message, $customer_detail->contract_number_c);
// Record the message in database
$this->save_message('WhatsApp', $customer_detail->phone_4_c, null, $message, $customer_detail->account->id);
} elseif ($customer_detail->is_sms_c) {
$message = $this->bill_message($customer_detail, $bill, 'SMS');
// Send the message
$this->send_sms($customer_detail->phone_4_c, $message);
// Record the message in database
$this->save_message('SMS', $customer_detail->phone_4_c, null, $message, $customer_detail->account->id);
} elseif ($customer_detail->is_mail_c) {
$message = $this->bill_message($customer_detail, $bill, 'Email');
// Send the message
$this->send_email($customer_detail, $bill, $message);
// Record the message in database
$this->save_message('Email', null, $customer_detail->mail_c, $message, $customer_detail->account->id);
}
}
}
}
$bar->advance();
}
$bar->finish();
});
// Delete all record from the tb_bills_infos table
// Set the auto-increment value to 0
TbBillsInfos::truncate();
Log::info('send:unpaid-bill-messages: Command executed successfully');
}
Please can someone point out what can be the issue with the above script? Why is it bugging and not completing execution ? Why is it freezing at some point in the loop?
See for example the command since April 21, where as before it will complete execution in about 15 minutes or less
I have equally checked RAM usage on the server and only 1GB out of 8GB is being used. So the server is stable
Related
I am unable to find in the documentation of how to handle the notification (what to do if notification is sent successfully or not)
NotificationController
public function AskForLevelUp()
{
$admin = User::where('is_admin', 1)->first();
$sendNotification = Notification::send($admin, new LevelUpNotification(Auth::user()->id));
if ($sendNotification->success()) // **HOW TO DO THIS CORRECTLY**
{
return back()->with('success', 'Your request has been submitted, please wait for admin confirmation.');
}
else
{
return back()->with('failure', 'Something went wrong, your request is not submitted. Please try again later.');
}
}
I've also read that send() has void return value here. Does anyone know how to handle this?
I want to email a potentially large number of clients, so I am using Batches and pushing each email send as a batched job, like this:
public function __construct(RunReport $runReport, User $run_by) {
$this->runReport = $runReport;
$this->run_by = $run_by;
}
public function handle()
{
$company_detail = CompanyDetail::first();
$jobs = $this->runReport->runReportReportees
->map(fn(RunReportReportee $runReportReportee) => new EmailStatementJob($runReportReportee, $company_detail))
->toArray();
$batch = Bus
::batch($jobs)
->then(function(Batch $batch) {
// All completed
$completed = ($batch->totalJobs - $batch->failedJobs);
$message = "foo";
$type = "bar";
$this->run_by->notify(new GenericNotification($message, $type, 'envelope'));
})
->allowFailures()
->name('Batch name here trust me')
->dispatch();
return $batch->id;
}
However the notify line causes an error Serialization of 'Doctrine\DBAL\Driver\PDOConnection' is not allowed.
How can I notify the user that initiated the batch when the batch is finished, with the results of the included email send attempts? Alternatively, how else should I email a few hundred clients and notify the user of the results?
I figured it out eventually with help - I was using $this in the ->then() callback which was my first mistake. Then instead of the notification in the job, I have instead stored the user ID and passed it to the then() callback like this
->then(function(Batch $batch) use ($run_by_id) { ... }
In the callback instead of notifying the user, I call an event
event(new HcpStatementsBatchFinishedEvent($batch, $id));
The event simply stores the information
public function __construct(Batch $batch, int $run_by_sysid)
{
$this->run_by = User::find($run_by_id);
$this->batch = $batch;
}
And the listener for the event builds the message and notifies the user.
In laravel 7, I setup cronjobs that runs base from different users specific timezone.
Here's the exact code:
foreach (User::role('admin')->get() as $user) {
/* with queue job */
$schedule->command('weekly-survey:send')->timezone($user->timezone)->weekly()->wednesdays()->at('8:00');
$schedule->command('update:surveys-completed')->timezone($user->timezone)->daily();
$schedule->command('trial:reminder')->timezone($user->timezone)->dailyAt('7:45');
$schedule->command('trial:ends')->timezone($user->timezone)->dailyAt('23:00');
$schedule->command('subscribe:customer')->timezone($user->timezone)->dailyAt('23:45');
$schedule->command('update:user-invoice')->timezone($user->timezone)->everyMinute();
$schedule->command('employees:update-status')->timezone($user->timezone)->everyMinute();
$schedule->command('subscription:update-quantity')->timezone($user->timezone)->daily();
$schedule->command('update:freeze-account')->timezone($user->timezone)->dailyAt('22:45');
$schedule->command('send:upcoming-survey-notification')->timezone($user->timezone)->weeklyOn(1, '8:00');
$schedule->command('surveys:end-soon')->timezone($user->timezone)->dailyAt('8:00');
/* end with queue job */
/* without queue */
$schedule->command('amazon:get-send-qouta')->timezone($user->timezone)->dailyAt('23:55');
$schedule->command('amazon:get-statistics')->timezone($user->timezone)->dailyAt('23:55');
$schedule->command('update:customer-success-table')->timezone($user->timezone)->everyMinute();
$schedule->command('csm:prev-month-active')->timezone($user->timezone)->lastDayOfMonth('23:59');
$schedule->command('update:monthly-earning')->timezone($user->timezone)->lastDayOfMonth('23:59');
$schedule->command('update:subscription-status')->timezone($user->timezone)->everyMinute();
$schedule->command('retrieve:past-due-subscription')->timezone($user->timezone)->dailyAt('23:59');
$user = new User();
$user->accountNotificationsSchedule($schedule);
$schedule->command('horizon:snapshot')->timezone($user->timezone)->everyFiveMinutes();
/* end without queue */
\Log::info("Cron entry successfully executed!");
}
Do this code safe to run? First I loop through all users where has admin role and then pass user time zone to each of the command. I'm sure that this code will infinitely run for as long as the server is alive. I just want to have an alternative safe approach.
Here's what I implemented and it works.
public function handle(TimezoneRepository $timezoneRepository)
{
// get user's unique timezone
$timezones = $timezoneRepository->getUniqueTimezones();
foreach ($timezones as $timezone) {
$date = Carbon::now($timezone);
// send only on Wednesday at 8:00 am
if ($date->isWednesday() && $date->hour == 8 && $date->minute == 0) {
Survey::SendSurvey();
}
}
}
I'm running a laravel websocket and have a connection over wss.
I am running commands on the server, and the commands are logged in a file. Each line is also sent over a websocket to the front-end so I can view it. Each laravel-command has it's own file and broadcast-channel.
Commandlogger:
class CommandLogger implements Logger {
public $commandname = '';
public $broadcast = false;
public function __construct($commandname, $broadcast = false) {
$this->commandname = Str::camel(Str::slug($commandname));
$this->broadcast = $broadcast;
}
function log($message) {
$message = Carbon::now()->format('Y-m-d H:i:s').": ".$message;
file_put_contents(storage_path("logs/commands/$this->commandname.log"), $message.PHP_EOL , FILE_APPEND | LOCK_EX);
if($this->broadcast) {
event(new CommandLogCreated($message, $this->commandname));
}
}
}
In Vue.js I listen with Echo(which implements pusherjs):
Echo.private('logs.commands.' + command)
.listen('.command-log:created', event => {
this.log[command] += event.message + "\n";
let splitLog = this.log[command].split("\n");
let splitLength = splitLog.length;
if(splitLength > 200) {
splitLog = splitLog.slice(splitLength - 200, splitLength);
}
this.log[command] = splitLog.join('\n');
this.trigger++;
})
.error(error => {
console.log(error);
});
The issue I'm experiencing only happens when the command is sending a lot of messages to echo.
At a normal rate, with some pauses between the messages, echo does the ping-pong and the connection remains receiving messages.
At higher message rates, is seems echo is not sending the ping-pong and my socket silently stops receiving data. After it stops receiving it starts ping-ponging as if nothing happend. No disconnect has occured on both server and client.
Websocket messages(notice it stops at 205000, gives no error and resumes ping-pong):
Actual command output(at 230000 and still running):
If I refresh the page, I will receive messages again.
I've updated the websockers:serve command (vendor/beyondcode/laravel-websockets/src/Console/Commands/StartServer.php) directly and disabled the pongtracker:
protected function configurePongTracker()
{
//$this->loop->addPeriodicTimer(10, function () {
// $this->laravel
// ->make(ChannelManager::class)
// ->removeObsoleteConnections();
//});
}
Then I rebooted the websocket and tried again. This time, no matter how fast I was sending messages in, echo keeps receiving messages.
TL;DR:
My conclusion is that echo should ping-pong in between receiving messages from the server, because currently it seems to fail at doing so, and the websocket cleans the connection eventually without disconnecting. How can I either force echo to do the ping-pong, or make sure the server does not clean the connection without running the risk of having runaway connections?
Update 1:
I've been diving a little more into the Startserver.php file and found this:
public function removeObsoleteConnections(): PromiseInterface
{
if (! $this->lock()->acquire()) {
return Helpers::createFulfilledPromise(false);
}
$this->getLocalConnections()->then(function ($connections) {
foreach ($connections as $connection) {
$differenceInSeconds = $connection->lastPongedAt->diffInSeconds(Carbon::now());
if ($differenceInSeconds > 120) {
$this->unsubscribeFromAllChannels($connection);
}
}
});
return Helpers::createFulfilledPromise(
$this->lock()->forceRelease()
);
}
So that explains why I stop receiving messages, but there is no disconnect. It just unsubscribes the channels I'm listening to silently(can't see this on front-end) and keeps the connection alive.
I've also created an issue on github(laravel/echo) because I do think this is unwanted behaviour. I'm just not sure if the issue lies within echo, or within pusher js.
I have been through this. check your channel name and type, and make sure you have access. Also, check that there are no other js files interfere with your app.js file like what happened to me. check this laracasts question out
How to remove already enqueued messages in joomla 2.5?
Thanks in advance!
I tried
$jAp = JFactory::getApplication();
$messagesexist = $jAp->getMessageQueue();
But the above code is showing all the enqued messages.
From that I want to remove particular message.
Try this,
$session = JFactory::getSession();
$sessionQueue = $session->get('application.queue');
if (count($sessionQueue)) {
$session->set('application.queue', null);
}
Hope its works..
To remove a particular message you will have to work through the array returned by getMessageQueue().
So something like this:
// Get the message queue
$messages = JFactory::getApplication()->getMessageQueue();
// If we have messages
if (is_array($messages) && count($messages))
{
// Check each message for the one we want
foreach ($messages as $message)
{
// do your checks here
}
}