Scheduler: VITEC tasks instead of separate crontab lines
New command vitec:scheduler-setup creates the scheduler group VITEC with the tasks vitec:ssr-health every 5 minutes and vitec:client-error-cleanup daily at 04:23, safe to run again; --disabled creates them switched off, --enable switches them on after the old crontab lines are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
90cb2faaf9
commit
b36dceb486
1 file changed
+136
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Evomedien\Vitec\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use TYPO3\CMS\Core\Core\Bootstrap;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Scheduler\Domain\Repository\SchedulerTaskRepository;
|
||||
use TYPO3\CMS\Scheduler\Execution;
|
||||
use TYPO3\CMS\Scheduler\Service\TaskService;
|
||||
|
||||
/**
|
||||
* Puts the project's recurring jobs into the TYPO3 Scheduler (group
|
||||
* "VITEC"), so they are visible, startable and switchable in
|
||||
* System › Scheduler instead of hiding in the crontab. The only cron
|
||||
* line left on dedi5092 is the core "scheduler:run" every minute.
|
||||
*
|
||||
* Idempotent: a task whose command already exists in the scheduler is
|
||||
* left alone. --disabled creates new tasks switched off (use while the
|
||||
* old crontab lines still run, so nothing runs twice), --enable switches
|
||||
* the VITEC tasks on afterwards.
|
||||
*/
|
||||
#[AsCommand(
|
||||
name: 'vitec:scheduler-setup',
|
||||
description: 'Create the VITEC scheduler tasks (SSR health every 5 min, client error cleanup daily) in the group "VITEC"; safe to run again'
|
||||
)]
|
||||
final class SchedulerSetupCommand extends Command
|
||||
{
|
||||
private const GROUP = 'VITEC';
|
||||
|
||||
/** command => [cron expression, description] */
|
||||
private const TASKS = [
|
||||
'vitec:ssr-health' => [
|
||||
'*/5 * * * *',
|
||||
'SSR health check: samples /__health and /__stats of the SSR node into the health log (cockpit charts), '
|
||||
. 'mails the alert recipients from Site settings › VITEC on down/up changes and falls back to the SPA '
|
||||
. 'after repeated failures. Formerly crontab.',
|
||||
],
|
||||
'vitec:client-error-cleanup' => [
|
||||
'23 4 * * *',
|
||||
'Deletes browser error reports (client errors from the React frontend) older than 30 days. Formerly crontab.',
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private readonly TaskService $taskService,
|
||||
private readonly SchedulerTaskRepository $taskRepository,
|
||||
private readonly ConnectionPool $connectionPool,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addOption('disabled', null, InputOption::VALUE_NONE, 'Create new tasks switched off');
|
||||
$this->addOption('enable', null, InputOption::VALUE_NONE, 'Switch on all VITEC tasks created by this command');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
Bootstrap::initializeBackendAuthentication();
|
||||
$groupUid = $this->groupUid();
|
||||
$output->writeln('Scheduler group "' . self::GROUP . '": uid ' . $groupUid);
|
||||
|
||||
foreach (self::TASKS as $command => [$cron, $description]) {
|
||||
$existing = $this->findTask($command);
|
||||
if ($existing !== null) {
|
||||
if ($input->getOption('enable') && (int)$existing['disable'] === 1) {
|
||||
$task = $this->taskRepository->findByUid((int)$existing['uid']);
|
||||
$task->setDisabled(false);
|
||||
$this->taskRepository->update($task);
|
||||
$output->writeln(sprintf('<info>%s: enabled (uid %d)</info>', $command, $existing['uid']));
|
||||
} else {
|
||||
$output->writeln(sprintf('%s: exists (uid %d, %s)', $command, $existing['uid'],
|
||||
(int)$existing['disable'] === 1 ? 'disabled' : 'enabled'));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!$this->taskService->isTaskTypeRegistered($command)) {
|
||||
$output->writeln('<error>' . $command . ': not available as scheduler task type</error>');
|
||||
continue;
|
||||
}
|
||||
$task = $this->taskService->createNewTask($command);
|
||||
$task->setDescription($description);
|
||||
$task->setTaskGroup($groupUid);
|
||||
$task->setExecution(Execution::createRecurringExecution(time() - 60, 0, 0, false, $cron));
|
||||
$task->setDisabled((bool)$input->getOption('disabled'));
|
||||
if ($this->taskRepository->add($task)) {
|
||||
$output->writeln(sprintf('<info>%s: created (uid %d, %s, %s)</info>', $command, $task->getTaskUid(), $cron,
|
||||
$task->isDisabled() ? 'disabled' : 'enabled'));
|
||||
} else {
|
||||
$output->writeln('<error>' . $command . ': could not be created</error>');
|
||||
}
|
||||
}
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function groupUid(): int
|
||||
{
|
||||
$connection = $this->connectionPool->getConnectionForTable('tx_scheduler_task_group');
|
||||
$uid = $connection->executeQuery(
|
||||
'SELECT uid FROM tx_scheduler_task_group WHERE groupName = ? AND deleted = 0 ORDER BY uid LIMIT 1',
|
||||
[self::GROUP]
|
||||
)->fetchOne();
|
||||
if ($uid !== false) {
|
||||
return (int)$uid;
|
||||
}
|
||||
$sorting = (int)$connection->executeQuery('SELECT MAX(sorting) FROM tx_scheduler_task_group')->fetchOne();
|
||||
$connection->insert('tx_scheduler_task_group', [
|
||||
'pid' => 0,
|
||||
'tstamp' => time(),
|
||||
'crdate' => time(),
|
||||
'groupName' => self::GROUP,
|
||||
'color' => '#f47937',
|
||||
'description' => 'Recurring jobs of the VITEC website (SSR monitoring, cleanups)',
|
||||
'sorting' => $sorting + 256,
|
||||
], ['pid' => Connection::PARAM_INT, 'tstamp' => Connection::PARAM_INT, 'crdate' => Connection::PARAM_INT, 'sorting' => Connection::PARAM_INT]);
|
||||
return (int)$connection->lastInsertId();
|
||||
}
|
||||
|
||||
private function findTask(string $command): ?array
|
||||
{
|
||||
$row = $this->connectionPool->getConnectionForTable('tx_scheduler_task')->executeQuery(
|
||||
'SELECT uid, disable FROM tx_scheduler_task WHERE tasktype = ? AND deleted = 0 ORDER BY uid LIMIT 1',
|
||||
[$command]
|
||||
)->fetchAssociative();
|
||||
return $row === false ? null : $row;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user