taskStorage = $taskStorage; $this->executor = $executor; } /** * @inheritDoc */ public function cycle(): int { // First: if there are no tasks, load them $this->tasks = $this->taskStorage->readTasks(true); // If there are still no tasks, nothing is queued, so this cycle can end. if (empty($this->tasks)) return SuperVisor::FINISHED; for ($i=0;$itasks);$i++) { $task = $this->tasks[$i]; // PENDING: should start if not constrained if ($task->getStatus() === Task::PENDING) { // Test if constrained $task = $this->testConstraints($task); // If the task changed status, task is no longer pending and should be processed by another statement if ($task->getStatus() !== Task::PENDING) continue; // Start the process using the executor service $task = $this->executor->startTask($task); $task->setStatus(Task::RUNNING); $task->startTaskTime(); // Modify the task in TaskStorage $this->taskStorage->modifyTask($task); fwrite(STDOUT, "\nChanged status of task '".$task->getId()."' to status " . Task::getStatusType($task->getStatus())); } // DELAYED: If task is delayed, and enough time has passed, change the status back to pending elseif ($task->getStatus() === Task::DELAYED && time() > $task->getDelayTime()) { $task->setStatus(Task::PENDING); $this->taskStorage->modifyTask($task); fwrite(STDOUT, "\nChanged status of task '".$task->getId()."' to status " . Task::getStatusType($task->getStatus())); } // RUNNING: check if task is still running. If not, set result based on output elseif ($task->getStatus() === Task::RUNNING) { $isRunning = $this->executor->getTaskRunning($task); $output = $this->taskStorage->readTaskOutput($task); $hasOutput = !is_null($output); // If nothing is found, the process has crashed and status PFAILED should be set if (!$isRunning && !$hasOutput) $task->setStatus(Task::PFAILED); // @todo Set PFAILED after $max_Time // If output is found, use the status code from that elseif (!$isRunning && $hasOutput) { try { $task->setOutput($output['output'], $output['errors']); $task->setStatus($output['statusCode']); } catch (TasksException $e) { // On failure to set output, consider as a process failure $task->setStatus(Task::PFAILED); } } // In any other situation the process is still running and should be left alone else continue; // If any changes have been made, they should be written to TaskStorage $task->endTaskTime(); $this->taskStorage->modifyTask($task); fwrite(STDOUT, "\nChanged status of task '".$task->getId()."' to status " . Task::getStatusType($task->getStatus())); } // FAILED: if a process has failed, attempt to rety if requested to do so elseif ($task->getStatus() === Task::PFAILED || $task->getStatus() === Task::FAILED) { // First fetch retry conditions $settings = $task->getSettings(); // First test if any retries should be tried at all if ($settings['retryOnFail'] === true && $task->getRetries() < $settings['maxRetries']) { // Then test if this type of failure should be retried and whether the mexRetries has been exceeded if ( ($task->getStatus() === Task::PFAILED && $settings['retryPFailures'] === true) || ($task->getStatus() === Task::FAILED && $settings['retryRFailures'] === true) ) { // If eligible, reset task to pending $task->addRetry(); $task = $this->executor->startTask($task); $task->setStatus(Task::RUNNING); $task->startTaskTime(); $this->taskStorage->modifyTask($task); fwrite(STDOUT, "\nChanged status of task '".$task->getId()."' to status " . Task::getStatusType($task->getStatus())); continue; } } // If the task is not eligible for a retry, either cancel it or move it to a postHandler if ($task->getUsePostHandler() === true) { $task->resetRetries(); $task = $this->executor->startTask($task, true); $task->startPostTime(); $task->setStatus(Task::POST); } else $task->setStatus(Task::CANCELLED); $this->taskStorage->modifyTask($task); fwrite(STDOUT, "\nChanged status of task '".$task->getId()."' to status " . Task::getStatusType($task->getStatus())); } // SUCCESS: if a task has succeeded, see if it needs a postHandler elseif ($task->getStatus() === Task::SUCCESS) { if ($task->getUsePostHandler() === true) { $task->resetRetries(); $task = $this->executor->startTask($task, true); $task->startPostTime(); $task->setStatus(Task::POST); } else $task->setStatus(Task::COMPLETED); $this->taskStorage->modifyTask($task); fwrite(STDOUT, "\nChanged status of task '".$task->getId()."' to status " . Task::getStatusType($task->getStatus())); } // POST: when a task is currently running in it's postHandler elseif ($task->getStatus() === Task::POST) { $isRunning = $this->executor->getTaskRunning($task); $output = $this->taskStorage->readPostOutput($task); $hasOutput = !is_null($output); // If a task is not running and has no output, an error has occurred if (!$isRunning && !$hasOutput) { // Test if a retry should be attempted $settings = $task->getSettings(); if ($settings['retryOnFail'] === true && $settings['retryPostFailures'] === true && $settings['maxRetries'] > $task->getRetries()) { $task->addRetry(); $task = $this->executor->startTask($task, true); } elseif ($settings['retryOnFail'] === true && $settings['retryPostFailures'] === true && $settings['maxRetries'] <= $task->getRetries()) $task->setStatus(Task::CANCELLED); else $task->setStatus(Task::CANCELLED); } // @todo Retry after $max_Time // If a task is not running and has output, set that output and mark as completed elseif (!$isRunning && $hasOutput) { $task->setPostOutput($output['output'], $output['errors']); if ($output['statusCode'] === Task::SUCCESS) $task->setStatus(Task::COMPLETED); else $task->setStatus(Task::CANCELLED); } // If the task is still running, leave it be else continue; // If any changes have been made, they should be written to TaskStorage $task->endPostTime(); $this->taskStorage->modifyTask($task); fwrite(STDOUT, "\nChanged status of task '".$task->getId()."' to status " . Task::getStatusType($task->getStatus())); } } // Check if all tasks are completed $allCompleted = true; $anyDelayed = false; foreach ($this->tasks as $task) { if ($task->getStatus() !== Task::COMPLETED && $task->getStatus() !== Task::CANCELLED) $allCompleted = false; elseif ($task->getStatus() === Task::DELAYED) $anyDelayed = true; } // If all are finished and none are delayed if ($allCompleted && !$anyDelayed) return SuperVisor::FINISHED; if ($allCompleted && $anyDelayed) return SuperVisor::CONSTRAINED; else return SuperVisor::RUNNING; } private function testConstraints(Task $task): Task { $constraints = $task->getConstraints(); foreach ($constraints as $constraint) { if ($constraint->intervene($task) && $constraint->blockCode() != 0) { $task->setStatus($constraint->blockCode()); if ($constraint->blockCode() === Task::DELAYED) $task->setDelayTime($constraint->delayTime()); // Save changes to TaskStorage $this->taskStorage->modifyTask($task); fwrite(STDOUT, "\nChanged status of task '".$task->getId()."' to status " . Task::getStatusType($task->getStatus())); } } return $task; } }