Let MySQL do the time logic when looking for tasks

This commit is contained in:
Dane Everitt 2020-10-25 15:06:54 -07:00
parent 23872b844a
commit 8c6327fd32
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
5 changed files with 24 additions and 168 deletions

View file

@ -1,62 +1,38 @@
<?php <?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Console\Commands\Schedule; namespace Pterodactyl\Console\Commands\Schedule;
use Cake\Chronos\Chronos;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Collection; use Pterodactyl\Models\Schedule;
use Pterodactyl\Services\Schedules\ProcessScheduleService; use Pterodactyl\Services\Schedules\ProcessScheduleService;
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
class ProcessRunnableCommand extends Command class ProcessRunnableCommand extends Command
{ {
/** /**
* @var string * @var string
*/ */
protected $description = 'Process schedules in the database and determine which are ready to run.'; protected $signature = 'p:schedule:process';
/**
* @var \Pterodactyl\Services\Schedules\ProcessScheduleService
*/
protected $processScheduleService;
/**
* @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface
*/
protected $repository;
/** /**
* @var string * @var string
*/ */
protected $signature = 'p:schedule:process'; protected $description = 'Process schedules in the database and determine which are ready to run.';
/**
* ProcessRunnableCommand constructor.
*
* @param \Pterodactyl\Services\Schedules\ProcessScheduleService $processScheduleService
* @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository
*/
public function __construct(ProcessScheduleService $processScheduleService, ScheduleRepositoryInterface $repository)
{
parent::__construct();
$this->processScheduleService = $processScheduleService;
$this->repository = $repository;
}
/** /**
* Handle command execution. * Handle command execution.
*
* @param \Pterodactyl\Services\Schedules\ProcessScheduleService $service
*
* @throws \Throwable
*/ */
public function handle() public function handle(ProcessScheduleService $service)
{ {
$schedules = $this->repository->getSchedulesToProcess(Chronos::now()->toAtomString()); $schedules = Schedule::query()->with('tasks')
->where('is_active', true)
->where('is_processing', false)
->whereRaw('next_run_at <= NOW()')
->get();
if ($schedules->count() < 1) { if ($schedules->count() < 1) {
$this->line('There are no scheduled tasks for servers that need to be run.'); $this->line('There are no scheduled tasks for servers that need to be run.');
@ -64,9 +40,9 @@ class ProcessRunnableCommand extends Command
} }
$bar = $this->output->createProgressBar(count($schedules)); $bar = $this->output->createProgressBar(count($schedules));
$schedules->each(function ($schedule) use ($bar) { foreach ($schedules as $schedule) {
if ($schedule->tasks instanceof Collection && count($schedule->tasks) > 0) { if ($schedule->tasks->isNotEmpty()) {
$this->processScheduleService->handle($schedule); $service->handle($schedule);
if ($this->input->isInteractive()) { if ($this->input->isInteractive()) {
$bar->clear(); $bar->clear();
@ -79,7 +55,7 @@ class ProcessRunnableCommand extends Command
$bar->advance(); $bar->advance();
$bar->display(); $bar->display();
}); }
$this->line(''); $this->line('');
} }

View file

@ -24,12 +24,4 @@ interface ScheduleRepositoryInterface extends RepositoryInterface
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function getScheduleWithTasks(int $schedule): Schedule; public function getScheduleWithTasks(int $schedule): Schedule;
/**
* Return all of the schedules that should be processed.
*
* @param string $timestamp
* @return \Illuminate\Support\Collection
*/
public function getSchedulesToProcess(string $timestamp): Collection;
} }

View file

@ -52,7 +52,12 @@ class Utilities
)->getNextRunDate()); )->getNextRunDate());
} }
public static function checked($name, $default) /**
* @param string $name
* @param mixed $default
* @return string
*/
public static function checked(string $name, $default)
{ {
$errors = session('errors'); $errors = session('errors');

View file

@ -47,19 +47,4 @@ class ScheduleRepository extends EloquentRepository implements ScheduleRepositor
throw new RecordNotFoundException; throw new RecordNotFoundException;
} }
} }
/**
* Return all of the schedules that should be processed.
*
* @param string $timestamp
* @return \Illuminate\Support\Collection
*/
public function getSchedulesToProcess(string $timestamp): Collection
{
return $this->getBuilder()->with('tasks')
->where('is_active', true)
->where('is_processing', false)
->where('next_run_at', '<=', $timestamp)
->get($this->getColumns());
}
} }

View file

@ -1,102 +0,0 @@
<?php
namespace Tests\Unit\Commands\Schedule;
use Mockery as m;
use Cake\Chronos\Chronos;
use Pterodactyl\Models\Task;
use Pterodactyl\Models\Schedule;
use Tests\Unit\Commands\CommandTestCase;
use Pterodactyl\Services\Schedules\ProcessScheduleService;
use Pterodactyl\Console\Commands\Schedule\ProcessRunnableCommand;
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
class ProcessRunnableCommandTest extends CommandTestCase
{
/**
* @var \Pterodactyl\Console\Commands\Schedule\ProcessRunnableCommand
*/
protected $command;
/**
* @var \Pterodactyl\Services\Schedules\ProcessScheduleService
*/
protected $processScheduleService;
/**
* @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface
*/
protected $repository;
/**
* Setup tests.
*/
public function setUp(): void
{
parent::setUp();
Chronos::setTestNow(Chronos::now());
$this->processScheduleService = m::mock(ProcessScheduleService::class);
$this->repository = m::mock(ScheduleRepositoryInterface::class);
$this->command = new ProcessRunnableCommand($this->processScheduleService, $this->repository);
}
/**
* Test that a schedule can be queued up correctly.
*/
public function testScheduleIsQueued()
{
$schedule = factory(Schedule::class)->make();
$schedule->tasks = collect([factory(Task::class)->make()]);
$this->repository->shouldReceive('getSchedulesToProcess')->with(Chronos::now()->toAtomString())->once()->andReturn(collect([$schedule]));
$this->processScheduleService->shouldReceive('handle')->with($schedule)->once()->andReturnNull();
$display = $this->runCommand($this->command);
$this->assertNotEmpty($display);
$this->assertStringContainsString(trans('command/messages.schedule.output_line', [
'schedule' => $schedule->name,
'hash' => $schedule->hashid,
]), $display);
}
/**
* If tasks is an empty collection, don't process it.
*/
public function testScheduleWithNoTasksIsNotProcessed()
{
$schedule = factory(Schedule::class)->make();
$schedule->tasks = collect([]);
$this->repository->shouldReceive('getSchedulesToProcess')->with(Chronos::now()->toAtomString())->once()->andReturn(collect([$schedule]));
$display = $this->runCommand($this->command);
$this->assertNotEmpty($display);
$this->assertStringNotContainsString(trans('command/messages.schedule.output_line', [
'schedule' => $schedule->name,
'hash' => $schedule->hashid,
]), $display);
}
/**
* If tasks isn't an instance of a collection, don't process it.
*/
public function testScheduleWithTasksObjectThatIsNotInstanceOfCollectionIsNotProcessed()
{
$schedule = factory(Schedule::class)->make(['tasks' => null]);
$this->repository->shouldReceive('getSchedulesToProcess')->with(Chronos::now()->toAtomString())->once()->andReturn(collect([$schedule]));
$display = $this->runCommand($this->command);
$this->assertNotEmpty($display);
$this->assertStringNotContainsString(trans('command/messages.schedule.output_line', [
'schedule' => $schedule->name,
'hash' => $schedule->hashid,
]), $display);
}
}