Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 23 additions & 17 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,36 @@
"php": ">=8.1",
"ext-posix": "*",
"dragonmantank/cron-expression": "~3.3",
"symfony/console": "~6.0|~5.0",
"symfony/event-dispatcher": "~6.0|~5.0",
"symfony/security-bundle": "~5.3|~6.0",
"symfony/framework-bundle": "~6.0|~5.0",
"symfony/process": "~6.0|~5.0",
"symfony/console": "~6.0.0|~5.0",
"symfony/event-dispatcher": "~6.0.0|~5.0",
"symfony/security-bundle": "~5.3|~6.0.0",
"symfony/framework-bundle": "~6.0.0|~5.0",
"symfony/process": "~6.0.0|~5.0",
"symfony/webpack-encore-bundle": "^1.14"
},
"require-dev": {
"symfony/phpunit-bridge": "^v6.0",
"symfony/http-kernel": "^v6.0",
"symfony/config": "^v6.0",
"symfony/dependency-injection": "^v6.0",
"symfony/yaml": "^v6.0",
"symfony/phpunit-bridge": "~6.0.0",
"symfony/http-kernel": "~6.0.0",
"symfony/config": "~6.0.0",
"symfony/dependency-injection": "~6.0.0",
"symfony/yaml": "~6.0.0",
"symfony/framework-bundle": "^v5.4.6",
"symfony/validator": "^6.0",
"symfony/validator": "~6.0.0",
"symfony/maker-bundle": "^1.0.0",
"nyholm/symfony-bundle-test": "^1.8",
"doctrine/doctrine-bundle": "^2.5",
"doctrine/orm": "^2.11",
"doctrine/annotations": "^1.13.2",
"whatwedo/php-coding-standard": "dev-develop",
"zenstruck/foundry": "^1.16",
"zenstruck/console-test": "^v1.1.0",
"zenstruck/foundry": "^1.21",
"zenstruck/console-test": "^v1.3.0",
"symfony/orm-pack": "^1.0",
"symfony/translation": "^6.0",
"symfony/twig-bundle": "^6.0",
"symfony/translation": "~6.0.0",
"symfony/twig-bundle": "~6.0.0",
"gedmo/doctrine-extensions": "^3.5",
"symfony/dotenv": "^6.0",
"symfony/dotenv": "~6.0.0",
"symfony/test-pack": "^1.0",
"symfony/runtime": "^6.0"
"symfony/runtime": "~6.0.0"
},
"autoload": {
"psr-4": {
Expand All @@ -60,5 +60,11 @@
"symfony/runtime": true,
"dealerdirect/phpcodesniffer-composer-installer": true
}
},
"extra": {
"symfony": {
"allow-contrib": false,
"require": "6.0.*"
}
}
}
1 change: 1 addition & 0 deletions src/Entity/Execution.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
*/
class Execution
{
public const STATE_PENDING = 'pending';
public const STATE_RUNNING = 'running';
public const STATE_FINISHED = 'finished';
public const STATE_STALE = 'stale';
Expand Down
65 changes: 35 additions & 30 deletions src/Manager/ExecutionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,30 +41,16 @@
*/
class ExecutionManager
{
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var EntityManagerInterface
*/
protected $em;
/**
* @var CronJobManager
*/
protected $cronJobManager;
/**
* @var string
*/
protected $projectDir;
/**
* @var string
*/
protected $environment;
protected LoggerInterface $logger;

protected EntityManagerInterface $em;

protected CronJobManager $cronJobManager;

protected string $projectDir;

protected string $environment;

/**
* ExecutionManager constructor.
*/
public function __construct(LoggerInterface $logger, EntityManagerInterface $em, CronJobManager $cronJobManager, string $projectDir, string $environment)
{
$this->logger = $logger;
Expand Down Expand Up @@ -116,41 +102,49 @@ public function getNextExecutionDate(CronInterface $cronJob): ?DateTime
public function isRunNeeded(CronInterface $cronJob): bool
{
// Debug log
$this->logger->debug(sprintf('Checking if execution of %s is needed', get_class($cronJob)));
$this->logger->debug(sprintf('Checking if execution of %s is needed', $cronJob::class));

// Check if cron is disabled.
if (!$cronJob->isActive()) {
$this->logger->debug(sprintf('%s do not need to run. It\'s disabled.', get_class($cronJob)));
$this->logger->debug(sprintf('%s do not need to run. It\'s disabled.', $cronJob::class));
return false;
}

// Check for pending
$pendingExcecution = $this->getPendingExecution($cronJob);
if ($pendingExcecution) {
$this->logger->debug(sprintf('%s has pending exection. Scheduling it now.', $cronJob::class));
$this->cleanupPending($cronJob);
return true;
}

// Get next execution date
$nextExecutionDate = $this->getNextExecutionDate($cronJob);
if (!$nextExecutionDate) {
$this->logger->debug(sprintf('%s has no previous run. Scheduling it now.', get_class($cronJob)));
$this->logger->debug(sprintf('%s has no previous run. Scheduling it now.', $cronJob::class));
return true;
}

// Check if run needed
$now = new DateTime();
if ($nextExecutionDate > $now) {
$this->logger->debug(sprintf('%s do not need to run. Next run at %s', get_class($cronJob), $nextExecutionDate->format('Y-m-d H:i:s')));
$this->logger->debug(sprintf('%s do not need to run. Next run at %s', $cronJob::class, $nextExecutionDate->format('Y-m-d H:i:s')));
return false;
}

// Check if parallel execution allowed
if ($cronJob->isParallelAllowed()) {
$this->logger->debug(sprintf('%s needs to run, Parallel execution is allowed.', get_class($cronJob)));
$this->logger->debug(sprintf('%s needs to run, Parallel execution is allowed.', $cronJob::class));
return true;
}

// Check if previous execution still running
$lastExecution = $this->getLastExecution($cronJob);
if ($lastExecution->getState() == Execution::STATE_RUNNING) {
$this->logger->debug(sprintf('%s has a still running previous execution. Skipping it until previous execution finished.', get_class($cronJob)));
$this->logger->debug(sprintf('%s has a still running previous execution. Skipping it until previous execution finished.', $cronJob::class));
return false;
}
$this->logger->debug(sprintf('%s needs to run, Parallel execution is not allowed', get_class($cronJob)));
$this->logger->debug(sprintf('%s needs to run, Parallel execution is not allowed', $cronJob::class));
return true;
}

Expand All @@ -159,6 +153,11 @@ public function getLastExecution(CronInterface $cronJob): ?Execution
return $this->em->getRepository(Execution::class)->findLastExecution($cronJob);
}

public function getPendingExecution(CronInterface $cronJob)
{
return $this->em->getRepository(Execution::class)->findPendingExcecution($cronJob);
}

protected function schedule(CronInterface $cronJob): void
{
$this->logger->info(sprintf('Scheduling execution of %s', $cronJob::class));
Expand All @@ -167,6 +166,12 @@ protected function schedule(CronInterface $cronJob): void
$this->logger->debug(sprintf('Helper process running with PID %d', $process->getPid()));
}

protected function cleanupPending(CronInterface $cronJob): void
{
$this->em->getRepository(Execution::class)->deletePendingJob($cronJob);
$this->em->flush();
}

protected function cleanupStale(): void
{
$executions = $this->em->getRepository(Execution::class)->findByState(Execution::STATE_RUNNING);
Expand Down
43 changes: 43 additions & 0 deletions src/Repository/ExecutionRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,49 @@ public function findLastExecution(CronInterface $cronJob): ?Execution
->getQuery()
->getOneOrNullResult();
}

public function findNonPendingExcecution(CronInterface $cronJob)
{
return $this->createQueryBuilder('e')
->where('e.job = :job')
->andWhere('e.state != :statePending')
->orderBy('e.startedAt', 'ASC')
->setParameters([
'job' => $cronJob::class,
'statePending' => Execution::STATE_PENDING,
])
->getQuery()
->getResult();

}

public function findPendingExcecution(CronInterface $cronJob)
{
return $this->createQueryBuilder('e')
->where('e.job = :job')
->andWhere('e.state = :statePending')
->setParameters([
'job' => $cronJob::class,
'statePending' => Execution::STATE_PENDING,
])
->getQuery()
->getResult();
}

public function deletePendingJob(CronInterface $cronJob)
{
return $this->createQueryBuilder('e')
->delete()
->where('e.job = :job')
->andWhere('e.state = :statePending')
->setParameters([
'job' => $cronJob::class,
'statePending' => Execution::STATE_PENDING,
])
->getQuery()
->execute()
;
}

public function deleteSuccessfulJobs(DateTimeInterface $retention, $limit = null)
{
Expand Down