Skip to content

[FEATURE] New operation - HasStuckSchedulerTask #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wu-psvneo opened this issue May 9, 2023 · 3 comments
Closed

[FEATURE] New operation - HasStuckSchedulerTask #35

wu-psvneo opened this issue May 9, 2023 · 3 comments

Comments

@wu-psvneo
Copy link

As the title says. This is about a new operation for detecting a stuck scheduler task.

We recently had a customer where a scheduler task wouldn't stop running. This operation could help you detect such tasks.

I created the operation class myself to test it. @svewap , if you think this operation adds value to your extension, I would be happy to contribute to your project.

You can define the max runnign hours via parameter or use the default of 6 hours.

<?php

namespace WapplerSystems\ZabbixClient\Operation;

/**
 * This file is part of the "zabbix_client" Extension for TYPO3 CMS.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 */

use DateTime;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\Exception;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Database\ConnectionPool;
use WapplerSystems\ZabbixClient\OperationResult;

class HasStuckSchedulerTask implements IOperation, SingletonInterface
{
    const MAX_RUNNING_HOURS = 6;

    /**
     * @param array $parameter
     * @return OperationResult
     *
     * @throws Exception|DBALException
     */
    public function execute($parameter = [])
    {
        $maxRunningHours = intval(isset($parameter['maxRunningHours']) ? $parameter['maxRunningHours'] : 0);
        // Make sure we do not use a number smaller than 1 here.
        if($maxRunningHours < 1) {
            $maxRunningHours = self::MAX_RUNNING_HOURS;
        }

        $schedulerRecords = $this->fetchSchedulerTasks();
        if (0 === count($schedulerRecords)) {
            // No tasks found.
            return new OperationResult(true, false);
        }

        foreach ($schedulerRecords as $schedulerRecord) {
            // Check if the task is running.
            $isRunning = !empty($schedulerRecord['serialized_executions']);
            if (!$isRunning) {
                continue;
            }
            // Validate for required column value (lastexecution_time).
            if (empty($schedulerRecord['lastexecution_time'])) {
                continue;
            }

            // Compare lastexecution_time with current time.
            $currentDateTime = new DateTime();
            $lastExecutedDateTime = (new DateTime())->setTimestamp((int)$schedulerRecord['lastexecution_time']);
            $runningHours = intval($currentDateTime->diff($lastExecutedDateTime)->format('%h'));
            $runningHours += intval($currentDateTime->diff($lastExecutedDateTime)->days) * 24;

            if ($runningHours >= $maxRunningHours) {
                return new OperationResult(true, true);
            }
        }

        // No task is running.
        return new OperationResult(true, false);
    }

    /**
     * @return array
     *
     * @throws Exception|DBALException
     */
    private function fetchSchedulerTasks()
    {
        /** @var ConnectionPool $connectionPool */
        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
        $queryBuilder = $connectionPool->getQueryBuilderForTable('tx_scheduler_task');
        $queryBuilder
            ->select('uid', 'serialized_executions', 'lastexecution_time')
            ->from('tx_scheduler_task')
            ->where(
                $queryBuilder->expr()->eq('disable', $queryBuilder->createNamedParameter(0, Connection::PARAM_INT))
            );

        if (version_compare(TYPO3_version, '9.0.0', '>=')) {
            $queryBuilder->andWhere(
                $queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, Connection::PARAM_INT))
            );
        }

        return $queryBuilder->execute()->fetchAllAssociative();
    }
}
@secured
Copy link

secured commented Jul 11, 2023

@svewap @hannesbochmann could you review #36 ?

@hannesbochmann
Copy link
Contributor

Looks good to me. mklib has a scheduler task checking the same thing but it's a good idea to not rely on a scheduler task to check that.

@svewap
Copy link
Collaborator

svewap commented Apr 17, 2024

The task will be included in the next release for TYPO3 12. If an older TYPO3 version is needed feel free to reopen it. My personal focus is set on 12 and 13.

@svewap svewap closed this as completed Apr 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants