Add separate cron runner that doesn't use proc_open (#1405)

* Add alternative to using the artisan schedule runner

* StyleCI fixes

* Add additional cron time periods

* Style fixes

* Typo

* Update the web cron to use the new system

* Write out JSON for which tasks were run

* Rename cron.php to just cron
This commit is contained in:
Nabeel S
2022-02-11 16:24:06 -05:00
committed by GitHub
parent 607ea6f5e5
commit 78fd8367a1
20 changed files with 337 additions and 35 deletions

View File

@@ -22,7 +22,11 @@ class ProcessQueue extends Command
'--stop-when-empty' => null, '--stop-when-empty' => null,
]); ]);
Log::info(Artisan::output()); $jobOutput = trim(Artisan::output());
if (!empty($jobOutput)) {
Log::info($jobOutput);
}
///** @var App\Support\WorkCommand $queueWorker */ ///** @var App\Support\WorkCommand $queueWorker */
//$queueWorker = new App\Support\WorkCommand(app('queue.worker'), app('cache.store')); //$queueWorker = new App\Support\WorkCommand(app('queue.worker'), app('cache.store'));

90
app/Console/Cron.php Normal file
View File

@@ -0,0 +1,90 @@
<?php
/**
* This runs any of the cron tasks that are set to run according to the Laravel schedule
*/
namespace App\Console;
use App\Console\Cron\FifteenMinute;
use App\Console\Cron\FiveMinute;
use App\Console\Cron\Hourly;
use App\Console\Cron\JobQueue;
use App\Console\Cron\Monthly;
use App\Console\Cron\Nightly;
use App\Console\Cron\ThirtyMinute;
use App\Console\Cron\Weekly;
use App\Contracts\Command;
use Illuminate\Console\Scheduling\Schedule;
class Cron
{
/** @var Schedule */
private $scheduler;
/**
* @var string[] The cron tasks which get called/run
*/
private $cronTasks = [
JobQueue::class,
FiveMinute::class,
FifteenMinute::class,
ThirtyMinute::class,
Hourly::class,
Nightly::class,
Weekly::class,
Monthly::class,
];
/**
* @var array Stores the instantiated cron tasks
*/
private $cronRunners = [];
/**
* @param Schedule $scheduler
*/
public function __construct(Schedule $scheduler)
{
$this->scheduler = $scheduler;
foreach ($this->cronTasks as $task) {
/** @var Command $cronTask */
$cronTask = app($task);
$signature = $cronTask->getSignature();
if (empty($signature)) {
continue;
}
$this->cronRunners[$signature] = $cronTask;
}
}
/**
* Try to figure out which commands are supposed to run right now
*
* @return array string of tasks that were run
*/
public function run(): array
{
$events = $this->scheduler->dueEvents(app());
if (empty($events)) {
return [];
}
$run = [];
/** @var \Illuminate\Console\Scheduling\Event $event */
foreach ($events as $event) {
foreach ($this->cronRunners as $signature => $task) {
if (!str_contains($event->command, $signature)) {
continue;
}
$task->callEvent();
$run[] = $signature;
}
}
return $run;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Console\Cron;
use App\Contracts\CronCommand;
use App\Events\CronFifteenMinute;
/**
* The actual cron tasks are in app/Cron
*/
class FifteenMinute extends CronCommand
{
protected $signature = 'cron:fifteen';
protected $description = 'Run the 15 minute cron tasks';
protected $schedule;
public function handle(): void
{
$this->callEvent();
}
public function callEvent()
{
event(new CronFifteenMinute());
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Console\Cron;
use App\Contracts\CronCommand;
use App\Events\CronFiveMinute;
/**
* This just calls the CronNightly event, so all of the
* listeners, etc can just be called to run those tasks
*
* The actual cron tasks are in app/Cron
*/
class FiveMinute extends CronCommand
{
protected $signature = 'cron:five';
protected $description = 'Run the 5 minute cron tasks';
protected $schedule;
public function handle(): void
{
$this->callEvent();
}
public function callEvent()
{
event(new CronFiveMinute());
}
}

View File

@@ -2,14 +2,14 @@
namespace App\Console\Cron; namespace App\Console\Cron;
use App\Contracts\Command; use App\Contracts\CronCommand;
use App\Events\CronHourly; use App\Events\CronHourly;
/** /**
* This just calls the CronHourly event, so all of the * This just calls the CronHourly event, so all of the
* listeners, etc can just be called to run those tasks * listeners, etc can just be called to run those tasks
*/ */
class Hourly extends Command class Hourly extends CronCommand
{ {
protected $signature = 'cron:hourly'; protected $signature = 'cron:hourly';
protected $description = 'Run the hourly cron tasks'; protected $description = 'Run the hourly cron tasks';
@@ -17,7 +17,11 @@ class Hourly extends Command
public function handle(): void public function handle(): void
{ {
$this->redirectLoggingToFile('cron'); $this->callEvent();
}
public function callEvent()
{
event(new CronHourly()); event(new CronHourly());
} }
} }

View File

@@ -2,14 +2,14 @@
namespace App\Console\Cron; namespace App\Console\Cron;
use App\Contracts\Command; use App\Contracts\CronCommand;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
/** /**
* This just calls the CronHourly event, so all of the * This just calls the CronHourly event, so all of the
* listeners, etc can just be called to run those tasks * listeners, etc can just be called to run those tasks
*/ */
class JobQueue extends Command class JobQueue extends CronCommand
{ {
protected $signature = 'cron:queue'; protected $signature = 'cron:queue';
protected $description = 'Run the cron queue tasks'; protected $description = 'Run the cron queue tasks';
@@ -17,9 +17,16 @@ class JobQueue extends Command
public function handle(): void public function handle(): void
{ {
$this->redirectLoggingToFile('cron'); $this->callEvent();
Artisan::call('queue:cron');
$this->info(Artisan::output()); $queueOutput = trim(Artisan::output());
if (!empty($queueOutput)) {
$this->info($queueOutput);
}
}
public function callEvent()
{
Artisan::call('queue:cron');
} }
} }

View File

@@ -2,7 +2,7 @@
namespace App\Console\Cron; namespace App\Console\Cron;
use App\Contracts\Command; use App\Contracts\CronCommand;
use App\Events\CronMonthly; use App\Events\CronMonthly;
/** /**
@@ -11,7 +11,7 @@ use App\Events\CronMonthly;
* *
* The actual cron tasks are in app/Cron * The actual cron tasks are in app/Cron
*/ */
class Monthly extends Command class Monthly extends CronCommand
{ {
protected $signature = 'cron:monthly'; protected $signature = 'cron:monthly';
protected $description = 'Run the monthly cron tasks'; protected $description = 'Run the monthly cron tasks';
@@ -19,7 +19,11 @@ class Monthly extends Command
public function handle(): void public function handle(): void
{ {
$this->redirectLoggingToFile('cron'); $this->callEvent();
}
public function callEvent()
{
event(new CronMonthly()); event(new CronMonthly());
} }
} }

View File

@@ -2,7 +2,7 @@
namespace App\Console\Cron; namespace App\Console\Cron;
use App\Contracts\Command; use App\Contracts\CronCommand;
use App\Events\CronNightly; use App\Events\CronNightly;
/** /**
@@ -11,7 +11,7 @@ use App\Events\CronNightly;
* *
* The actual cron tasks are in app/Cron * The actual cron tasks are in app/Cron
*/ */
class Nightly extends Command class Nightly extends CronCommand
{ {
protected $signature = 'cron:nightly'; protected $signature = 'cron:nightly';
protected $description = 'Run the nightly cron tasks'; protected $description = 'Run the nightly cron tasks';
@@ -19,7 +19,11 @@ class Nightly extends Command
public function handle(): void public function handle(): void
{ {
$this->redirectLoggingToFile('cron'); $this->callEvent();
}
public function callEvent()
{
event(new CronNightly()); event(new CronNightly());
} }
} }

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Console\Cron;
use App\Contracts\CronCommand;
use App\Events\CronThirtyMinute;
/**
* The actual cron tasks are in app/Cron
*/
class ThirtyMinute extends CronCommand
{
protected $signature = 'cron:thirty';
protected $description = 'Run the 30 minute cron tasks';
protected $schedule;
public function handle(): void
{
$this->callEvent();
}
public function callEvent()
{
event(new CronThirtyMinute());
}
}

View File

@@ -2,7 +2,7 @@
namespace App\Console\Cron; namespace App\Console\Cron;
use App\Contracts\Command; use App\Contracts\CronCommand;
use App\Events\CronWeekly; use App\Events\CronWeekly;
/** /**
@@ -11,7 +11,7 @@ use App\Events\CronWeekly;
* *
* The actual cron tasks are in app/Cron * The actual cron tasks are in app/Cron
*/ */
class Weekly extends Command class Weekly extends CronCommand
{ {
protected $signature = 'cron:weekly'; protected $signature = 'cron:weekly';
protected $description = 'Run the weekly cron tasks'; protected $description = 'Run the weekly cron tasks';
@@ -19,7 +19,11 @@ class Weekly extends Command
public function handle(): void public function handle(): void
{ {
$this->redirectLoggingToFile('cron'); $this->callEvent();
}
public function callEvent()
{
event(new CronWeekly()); event(new CronWeekly());
} }
} }

View File

@@ -2,10 +2,13 @@
namespace App\Console; namespace App\Console;
use App\Console\Cron\FifteenMinute;
use App\Console\Cron\FiveMinute;
use App\Console\Cron\Hourly; use App\Console\Cron\Hourly;
use App\Console\Cron\JobQueue; use App\Console\Cron\JobQueue;
use App\Console\Cron\Monthly; use App\Console\Cron\Monthly;
use App\Console\Cron\Nightly; use App\Console\Cron\Nightly;
use App\Console\Cron\ThirtyMinute;
use App\Console\Cron\Weekly; use App\Console\Cron\Weekly;
use App\Services\CronService; use App\Services\CronService;
use Illuminate\Console\Scheduling\Schedule; use Illuminate\Console\Scheduling\Schedule;
@@ -33,10 +36,16 @@ class Kernel extends ConsoleKernel
->withoutOverlapping(); ->withoutOverlapping();
} }
/*
* NOTE: IF MORE TASKS ARE ADDED, THEY ALSO MUST BE ADDED TO THE CRON.PHP
*/
$schedule->command(FiveMinute::class)->everyFiveMinutes();
$schedule->command(FifteenMinute::class)->everyFifteenMinutes();
$schedule->command(ThirtyMinute::class)->everyThirtyMinutes();
$schedule->command(Nightly::class)->dailyAt('01:00'); $schedule->command(Nightly::class)->dailyAt('01:00');
$schedule->command(Hourly::class)->hourly();
$schedule->command(Weekly::class)->weeklyOn(0); $schedule->command(Weekly::class)->weeklyOn(0);
$schedule->command(Monthly::class)->monthlyOn(1); $schedule->command(Monthly::class)->monthlyOn(1);
$schedule->command(Hourly::class)->hourly();
// When spatie-backups runs // When spatie-backups runs
/*if (config('backup.backup.enabled', false) === true) { /*if (config('backup.backup.enabled', false) === true) {

View File

@@ -29,6 +29,16 @@ abstract class Command extends \Illuminate\Console\Command
}*/ }*/
} }
/**
* Return the signature of the command
*
* @return string
*/
public function getSignature(): string
{
return $this->signature;
}
/** /**
* Splice the logger and replace the active handlers with the handlers from the * Splice the logger and replace the active handlers with the handlers from the
* a stack in config/logging.php * a stack in config/logging.php

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Contracts;
abstract class CronCommand extends Command
{
/**
* @return mixed
*/
abstract public function callEvent();
/**
* Adjust the logging depending on where we're running from
*/
public function __construct()
{
parent::__construct();
$this->redirectLoggingToFile('cron');
}
}

View File

@@ -20,7 +20,7 @@ class ClearExpiredSimbrief extends Listener
} }
/** /**
* @param \App\Events\CronNightly $event * @param CronHourly $event
*/ */
public function handle(CronHourly $event): void public function handle(CronHourly $event): void
{ {

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Events;
use App\Contracts\Event;
class CronFifteenMinute extends Event
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Events;
use App\Contracts\Event;
class CronFiveMinute extends Event
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Events;
use App\Contracts\Event;
class CronThirtyMinute extends Event
{
}

View File

@@ -2,10 +2,10 @@
namespace App\Http\Controllers\Api; namespace App\Http\Controllers\Api;
use App\Console\Cron;
use App\Contracts\Controller; use App\Contracts\Controller;
use App\Exceptions\CronInvalid; use App\Exceptions\CronInvalid;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
class MaintenanceController extends Controller class MaintenanceController extends Controller
{ {
@@ -24,12 +24,12 @@ class MaintenanceController extends Controller
throw new CronInvalid(); throw new CronInvalid();
} }
$output = ''; $cron = app(Cron::class);
Artisan::call('schedule:run'); $run = $cron->run();
$output .= trim(Artisan::output());
return response([ return response()->json([
'content' => $output, 'count' => count($run),
'tasks' => $run,
]); ]);
} }
} }

View File

@@ -12,9 +12,12 @@ use App\Cron\Nightly\PilotLeave;
use App\Cron\Nightly\RecalculateBalances; use App\Cron\Nightly\RecalculateBalances;
use App\Cron\Nightly\RecalculateStats; use App\Cron\Nightly\RecalculateStats;
use App\Cron\Nightly\SetActiveFlights; use App\Cron\Nightly\SetActiveFlights;
use App\Events\CronFifteenMinute;
use App\Events\CronFiveMinute;
use App\Events\CronHourly; use App\Events\CronHourly;
use App\Events\CronMonthly; use App\Events\CronMonthly;
use App\Events\CronNightly; use App\Events\CronNightly;
use App\Events\CronThirtyMinute;
use App\Events\CronWeekly; use App\Events\CronWeekly;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
@@ -24,6 +27,15 @@ use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvi
class CronServiceProvider extends ServiceProvider class CronServiceProvider extends ServiceProvider
{ {
protected $listen = [ protected $listen = [
CronFiveMinute::class => [],
CronFifteenMinute::class => [],
CronThirtyMinute::class => [],
CronHourly::class => [
DeletePireps::class,
RemoveExpiredBids::class,
RemoveExpiredLiveFlights::class,
ClearExpiredSimbrief::class,
],
CronNightly::class => [ CronNightly::class => [
ApplyExpenses::class, ApplyExpenses::class,
RecalculateBalances::class, RecalculateBalances::class,
@@ -32,19 +44,10 @@ class CronServiceProvider extends ServiceProvider
RecalculateStats::class, RecalculateStats::class,
NewVersionCheck::class, NewVersionCheck::class,
], ],
CronWeekly::class => [ CronWeekly::class => [
], ],
CronMonthly::class => [ CronMonthly::class => [
\App\Cron\Monthly\ApplyExpenses::class, \App\Cron\Monthly\ApplyExpenses::class,
], ],
CronHourly::class => [
DeletePireps::class,
RemoveExpiredBids::class,
RemoveExpiredLiveFlights::class,
ClearExpiredSimbrief::class,
],
]; ];
} }

35
bin/cron Executable file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/env php
<?php
/**
* THIS CRON FILE IS A BACKUP METHOD IF YOUR SERVER DOES NOT HAVE PROC_OPEN ENABLED
* YOU SHOULD TRY USING THE DEFAULT INSTRUCTIONS OF USING ARTISAN SCHEDULE:RUN
*/
define('LARAVEL_START', microtime(true));
use App\Console\Cron;
use App\Console\Kernel;
require __DIR__.'/../vendor/autoload.php';
/** @var Application $app */
$app = require_once __DIR__.'/../bootstrap/app.php';
/** @var Kernel $kernel */
$kernel = $app->make(Kernel::class);
// Run a null artisan thing just so Laravel internals can be setup properly
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArrayInput([
'command' => 'version',
]),
new Symfony\Component\Console\Output\NullOutput()
);
/** @var Cron $cron */
$cron = app(Cron::class);
$run = $cron->run();
echo json_encode([
'count' => count($run),
'tasks' => $run,
]);