Laravel 8 - Process millions of records in database - php

There are millions of records in a database table.
purchase
id (increments), device_id (int), app_id (int), status(boolean), receipt (string)
My framework laravel 8 and I use mysql
I have created a command
app/Console/Commands/ProcessPurchase.php
<?php
namespace App\Console\Commands;
use App\Models\Purchase;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Queue;
use App\Jobs\ProcessPurchaseJob;
class ProcessPurchase extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'process:purchase';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return int
*/
public function handle()
{
Log::info('process:purchase command start successfully.');
$purchases = Purchase::getUnequalizedPurchases(250);
if(count($purchases) > 0) {
foreach ($purchases as $purchase) {
//push job to the queue
Queue::push((new ProcessPurchaseJob($purchase, $purchase->os)));
}
} else {
Log::info('process:purchase command end successfully.');
}
}
}
and created a job
app/Console/Commands/ProcessPurchaseJob.php
<?php
namespace App\Jobs;
use App\Models\Purchase;
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 Illuminate\Support\Facades\Log;
class ProcessPurchaseJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $purchase, $os;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct(Purchase $purchase, string $os)
{
$this->purchase = $purchase;
$this->os = $os;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
//getting data from the third-party endpoint
if($purchaseData = getPurchaseDataFromMarket($this->purchase->receipt, $this->os)) {
$this->purchase->expire_date = $purchaseData['expire_date'];
$this->purchase->status = $purchaseData['status'];
try {
$this->purchase->save();
Log::info('purchase job runned successfully. receipt : ' . $this->purchase->receipt);
} catch (\Exception $exception) {
Log::error($exception->getMessage());
}
} else {
Log::error('purchase request error. receipt : ' . $this->purchase->receipt);
}
}
}
and added to app/http/Console/Kernel.php
$schedule->command('process:purchase')
->everyFiveMinutes();
to start cron, execute this command.
./vendor/bin/sail artisan schedule:work
to do the jobs in queue, execute this command
./vendor/bin/sail artisan queue:work --queue=high,default
I should send a request to third-party HTTP endpoint and update per every record.
This process take 1.5 secs about per a record. So, for 10.000.000 records, it takes 173.6 days.
I should can process all these data in a short time. (like 12 hours etc.)
What should i do ?

Related

Multiple Queue on Homestead is failing and Events don't get fired

I currently have a live app that i am making huge changes too so i am working locally using Homestead. I previously used One Queue but with changes and all that i had to run multiple queues. The previous default queues was meant to make rows in a database table and one of the recent ones was to send emails. then i needed another one that I need to make rows in another table as well...issue is sometimes it works and other times it fails and sometimes it only creates one row before failing. the most annoying thing is that it is supposed to fire an event per row created in the database but that does not even occur at all. I had done it effectively before and it never failed me even the default queue still works fine.
here is the controller:
<?php
namespace App\Http\Controllers;
use Redirect;
use App\Short;
use App\Wallet;
use \SplFixedArray;
use App\Jobs\ShortQueuer;
use Illuminate\Http\Request;
use App\Http\Requests\SlotRequest;
class ShortController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('short_term_goals');
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(SlotRequest $request)
{
$quota = 2;
$slotquota = request('slotamount') + $quota;
if ( auth()->user()->wallet->balance < $slotquota ) {
return Redirect::back()->with('low_balance', 'You do not have a sufficient wallet balance to reserve these SLOTS. Please Load Up Your Wallet');
} else {
// Getting SLOTS as objects of an array
$slotquantity = new SplFixedArray(request('slotamount'));
$slotquantity = $slotquantity->toArray();
$user = auth()->user();
ShortQueuer::dispatch($slotquantity, $user)->onQueue('shorts');
}
//Sorting Wallet Balance
$wallet = Wallet::where('user_id', auth()->user()->id)->first();
$wallet->balance = $wallet->balance - $slotquota;
$wallet->save();
//Returning View With Message
return Redirect::back()->with('reserved', 'Your Short Term Goals are Currently being met .');
}
/**
* Display the specified resource.
*
* #param \App\Short $short
* #return \Illuminate\Http\Response
*/
public function show(Short $short)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param \App\Short $short
* #return \Illuminate\Http\Response
*/
public function edit(Short $short)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param \App\Short $short
* #return \Illuminate\Http\Response
*/
public function update(Request $request, Short $short)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param \App\Short $short
* #return \Illuminate\Http\Response
*/
public function destroy(Short $short)
{
//
}
}
Here is the job
<?php
namespace App\Jobs;
use App\Short;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class ShortQueuer implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $slotquantity;
protected $user;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct(array $slotquantity, $user)
{
$this->slotquantity = $slotquantity;
$this->user = $user;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
// Inserting Rows in SLOTS Table
foreach ($this->slotquantity as $short) {
$short = new Short();
$short->user_id = $this->user->id;
$short->save();
//Slot Counting Event
event(new ShortCounter);
}
}
public $tries = 1;
public $timeout = 86400;
public $retryAfter = 87000;
}
here is the event
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class ShortCounter
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
Here is the listener
<?php
namespace App\Listeners;
use App\Short;
use App\Goal;
use App\Events\ShortCounter;
use Illuminate\Http\Request;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class GoalCreator
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param ShortCounter $event
* #return void
*/
public function handle(ShortCounter $event)
{
$shortcount = Short::all()->count();
if($shortcount == 800) {
$goalshort = Short::latest()->first();
$goal = new Goal();
$goal->gid = unique_random('goals', 'sgid', 8);
$goal->grc = unique_random('goals', 'sgrc', 12);
$goal->status = 0;
$goal->amount = 200;
$goal->user_id = $goalshort->user_id;
$goal->save();
Short::truncate();
}
}
}
I sort it out.....I went through the error logs and solved the errors one after the other. First I forgot to include the Event Class in the Job, then a column name in my database was wrong then I restarted my server.

Laravel 5.3 scheduler doesn't run automatically

I'm using Laravel 5.3.26 and cannot set the scheduler to run automatically although i have the cron job ready.
I' ve created a new command ligmena:update, below is the code:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use DB;
class ligmena extends Command {
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'ligmena:update';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Update';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct() {
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle() {
//
$today = strtotime("now");
$energa_symvolaia = DB::table('symvolaia')->where('eidos_kinisis', '1')->get();
foreach ($energa_symvolaia as $es) {
$imerominia_lixis = strtotime(str_replace("/", "-", $es->imerominia_lixis));
if ($today > $imerominia_lixis)
DB::table('symvolaia')->where('id', '<', $es->id)->update(['eidos_kinisis' => '4']);
}
}
}
?>
Below is the code of Kernel.php
<?php
namespace App\Console;
use DB;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel {
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
\App\Console\Commands\ligmena::class,
//
];
/**
* Define the application's command schedule.
*
* #param \Illuminate\Console\Scheduling\Schedule $schedule
* #return void
*/
protected function schedule(Schedule $schedule) {
// $schedule->command('inspire')
// ->hourly();
$schedule->command('ligmena:update')->everyMinute();
}
/**
* Register the Closure based commands for the application.
*
* #return void
*/
protected function commands() {
require base_path('routes/console.php');
}
}
?>
I've setup the cron job like this:
php /home/site.com/public_html/testdemo/artisan schedule:run >> /dev/null 2>&1
and it runs every minute.
If I run the command manually it works fine.
Any suggestions?
I 've solved the issue using raw mysql.
The cron job was running fine and the code was ok but it was not changing the DB.
After i changed to raw mysql it worked fine

Laravel Too many arguments, expected arguments "command" while scheduling

This should be straigh forward buti don't know why it is not working . I am creating a command in laravel to send birtday email reminders on a user's birtday .
Everything works fine and the schedule function is triggered but comes with an error
[Symfony\Component\Console\Exception\RuntimeException]
Too many arguments, expected arguments "command".
This is my command
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\User;
class SendBirthdayReminderEmail extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'email:birthday';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Email users a birthday Reminder message';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$users = User::whereMonth('dob', '=', date('m'))->whereDay('dob', '=', date('d'))->get();
foreach($users as $user) {
Mail::queue('emails.birthday', ['user' => $user], function ($mail) use ($user) {
$mail->to($user['email'])
->from('info#XXXXXX.com', 'Company')
->subject('Happy Birthday!');
});
}
$this->info('Birthday messages sent successfully!');
}
}
And this is my kernel.php file
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
Commands\SendBirthdayReminderEmail::class
];
/**
* Define the application's command schedule.
*
* #param \Illuminate\Console\Scheduling\Schedule $schedule
* #return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('email:birthday')->dailyAt('13:00')->timezone('Africa/Dar_es_Salaam');
}
/**
* Register the Closure based commands for the application.
*
* #return void
*/
protected function commands()
{
require base_path('routes/console.php');
}
}
Any help will be appreciated . Thanks :-)
I found a solution,
/opt/php70/bin/php /home/sitename/public_html/artisan schedule:run >/dev/null 2>&1
initially i had 1 after schedule:run method . As below
/opt/php70/bin/php /home/sitename/public_html/artisan schedule:run 1 >/dev/null 2>&1
Your code all looks good.
Have you tried simply as below
php artisan schedule:run
after reaching at your root folder path.

php artisan schedule:run don't send emails

I am trying to send emails from an scheduled laravel task, when I call the command from the application the email is sent, but when the command is called from the command line, it is executed but there is no email sent.
my command code is the next :
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
class SendEmails extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'emails:send';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
try {
Mail::send('emails.testmail', [ ], function ($m) {
$m->to('someona#gmail.com', 'Francisco Larios')->subject('Your Reminder!');
});
} catch (\Exception $e) {
throw new \Exception("Error Processing Request", 1);
}
}
}
the code in the kernel file is the next:
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
Commands\Inspire::class,
Commands\SendEmails::class,
];
/**
* Define the application's command schedule.
*
* #param \Illuminate\Console\Scheduling\Schedule $schedule
* #return void
*/
protected function schedule(Schedule $schedule)
//{
// $schedule->command('inspire')->everyMinute();
//}
}
the command I am running on the console is :
php artisan emails:send
the problem was the smtp server I was using to send the emails, I just change it for another smtp server in my mail.php connfiguration file an so, It works for both application and command line execution.

Laravel 5 Command not Returning Data

I'm trying to use commands so I can queue them because of a bunch of requests that will take 30-90 seconds to complete. The only issue is that the command is not returning data to my controller like I hoped it would, instead, all I'm getting is a convenient (sarcasm) "null". Here's some code snippets from my files, and thanks for any help!
HomeController
class HomeController extends Controller {
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Show the application dashboard to the user.
*
* #return Response
*/
public function index()
{
var_dump($this->dispatch(new \App\Commands\GetFeedCommand("http://alerts.weather.gov/cap/us.atom")));
}
GetFeedController
use App\Commands\Command;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldBeQueued;
use GuzzleHttp\Client;
class GetFeedCommand extends Command implements ShouldBeQueued {
use InteractsWithQueue, SerializesModels;
public $guzzle;
public $uri;
/**
* Create a new command instance.
*
* #return void
*/
public function __construct($uri)
{
$this->guzzle = new Client;
$this->uri = $uri;
}
}
GetFeedControllerHelper
use App\Commands\GetFeedCommand;
use Illuminate\Queue\InteractsWithQueue;
class GetFeedCommandHandler {
/**
* Create the command handler.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the command.
*
* #param GetFeedCommand $command
* #return void
*/
public function handle(GetFeedCommand $command)
{
return $command->guzzle->get($command->uri)->xml();
}
}
Any help would be greatly appreciated! Thanks!
That is correct.
If you queue the command - then when you run it nothing will happen instantly, so it returns null. Meanwhile the command will run in the background on your queue processing system separately.
If you need an immediate reply - then just run the command without the queue.

Categories