Laravel version:
8.x
PHP version:
8.0
MySQL version:
8.0
Server:
Larave Forge
I'm running a command on one of my websites
php artisan command:here
after a few minutes I get this status "Timed Out", is there anything that can be optimized here to prevent the time out?
subscriptions
table has 40,000
records and incomes
table has 8,000,000
. Every subscription has a maximum of 200
records in the incomes
table.
To explain what this command does, this is fixing (by inserting) the lacking of income of every ACTIVE
subscription, which will be identified based on the hour difference from the last income that has been inserted. Every subscription has a max of 200
records in the incomes
table, so if the script detected that a subscription
has already reached the 200
income records, it will update the status
to
COMPLETED
.
Subscription.php (model)
public function latestIncome()
{
return $this->hasOne(Income::class)->latestOfMany();
}
Income.php (model)
public function subscription()
{
return $this->belongsTo(Subscription::class);
}
namespace App\Console\Commands;
class SomeCommand extends Command
{
protected $signature = 'command:here';
public function handle()
{
ini_set('max_execution_time', 0);
foreach (Subscription::with('latestIncome')->withCount('income')->where('status', 'ACTIVE')->lazy() as $subscription) {
$count_earnings = $subscription->income_count;
$recent_bonus = $subscription->latestIncome;
if ($recent_bonus) {
if ($count_earnings < 200) {
$hour_difference = now()->diffInHours($recent_bonus->created_at);
if ($hour_difference > 1) {
$to_insert = 200 - $count_earnings;
$max = $hour_difference;
if ($hour_difference > $to_insert) {
$max = $to_insert;
}
for ($i = 0; $i < $max; $i ) {
$income = new Income;
$income->user_id = $subscription->user_id;
$income->subscription_id = $subscription->id;
$income->amount = (100 * 0.002) * 100;
$income->save();
}
if (($count_earnings $max) >= 200) {
$subscription->update(['status' => 'COMPLETED']);
}
Log::info('Fix for:'.$subscription->id.' | User:'.$subscription->user_id.' | Total:'.$max);
}
} else {
$subscription->update(['status' => 'COMPLETED']);
}
}
}
}
}
CodePudding user response:
You can define a time limit in the __construct function or in your index controller if you want a large time restriction.
public function __construct()
{
set_time_limit(8000000);
}
CodePudding user response:
You might gain some performance if you do your updates in bulk:
namespace App\Console\Commands;
class SomeCommand extends Command
{
protected $signature = 'command:here';
public function handle()
{
ini_set('max_execution_time', 0);
// Update everything you can before the loop
Subscription::has('income', '>=', 200)
->where('status', '!=' 'COMPLETED')
->update([ 'status' => 'COMPLETED' ]);
foreach (Subscription::with('latestIncome')->withCount('income')->where('status', 'ACTIVE')->lazy() as $subscription) {
$count_earnings = $subscription->income_count;
$recent_bonus = $subscription->latestIncome;
if ($recent_bonus) {
$hour_difference = now()->diffInHours($recent_bonus->created_at);
if ($hour_difference > 1) {
$to_insert = 200 - $count_earnings;
$max = $hour_difference;
if ($hour_difference > $to_insert) {
$max = $to_insert;
}
// Some performance gain here possibly
Income::insert(collect()->pad($max, [
'user_id' => $subscription->user_id,
'subscription_id' => $subscription->id,
'amount' => (100 * 0.002) * 100,
])->all());
Log::info('Fix for:'.$subscription->id.' | User:'.$subscription->user_id.' | Total:'.$max);
}
}
}
// Update anything that got pushed over that threshold
Subscription::has('income', '>=', 200)
->where('status', '!=' 'COMPLETED')
->update([ 'status' => 'COMPLETED' ]);
}
}