/** * if job is sync then we get worker and try execute job * else if job is Async then redirect to other queue * * REMEMBER: Queue name is equal to Worker class name (through all "Cronario") * * @param AbstractJob|Job $job * * @throws ResultException * @throws WorkerException */ protected function doJob(AbstractJob $job) { $gatewayClass = $this->analiseGateway($job); if ($job->isSync()) { $worker = AbstractWorker::factory($gatewayClass); return $worker($job); } if (!$job->hasAttempt()) { throw new ResultException(ResultException::FAILURE_MAX_ATTEMPTS); } // redirect result for new gateway class $job->addDebug(['set_gateway' => $gatewayClass]); $job->setWorkerClass($gatewayClass)->save(); throw new ResultException(ResultException::REDIRECT_GATEWAY_CLASS); }
/** * @param AbstractJob $parentJob * * @return ResultException|null */ public function __invoke(AbstractJob $parentJob = null) { if (null !== $parentJob) { $this->setParentId($parentJob->getId())->setAuthor($parentJob->getAuthor()); } $this->save(); try { if ($this->isSync()) { $worker = AbstractWorker::factory($this->getWorkerClass()); $worker($this); } else { $this->setResult(new ResultException(ResultException::R_QUEUED)); $this->putIntoQueue(); $this->save(); } } catch (\Exception $ex) { $this->setResult($ex); $this->save(); } return $this->getResult(); }
/** * @return bool */ public function run() { $file = $this->getBootstrapFile(); require_once $file; $this->startManagerLive(); $workerClass = $this->getWorkerClass(); $managerId = $this->getId(); $logger = $this->getLogger(); $queue = $this->getQueue(); $logger->info("Manager {$managerId} start work {$workerClass}", [__NAMESPACE__]); try { $worker = AbstractWorker::factory($workerClass); } catch (Exception\WorkerException $ex) { $queue->stop($workerClass); $logger->warning($ex->getMessage(), [__NAMESPACE__]); $logger->info("Manager {$managerId} Queue stop {$workerClass}", [__NAMESPACE__]); $logger->info("Manager {$managerId} finish work {$workerClass}", [__NAMESPACE__]); return false; } try { $waitForJob = $this->getWorkerConfig(AbstractWorker::CONFIG_P_MANAGER_IDLE_DIE_DELAY); while ($jobId = $queue->reserveJob($workerClass, $waitForJob)) { if ($jobId === false) { $logger->debug("Manager {$managerId} queue is empty so {$workerClass}", [__NAMESPACE__]); break; } $logger->debug("Manager {$managerId} reserve job {$jobId}", [__NAMESPACE__]); $job = null; try { /** @var AbstractJob $job */ $job = Facade::getStorage($this->getAppId())->find($jobId); } catch (JobException $ex) { $logger->warning($ex->getMessage(), [__NAMESPACE__]); $queue->deleteJob($jobId); continue; } if ($job === null) { $logger->error("Manager {$managerId} job is not instanceof of AbstractJob {$jobId}", [__NAMESPACE__]); $queue->deleteJob($jobId); continue; } $logger->debug("Manager {$managerId} Worker start working at {$jobId}...", [__NAMESPACE__]); // DO JOB! /** @var ResultException $result */ $result = $worker($job); $logger->debug("Manager {$managerId} Worker finish working at {$jobId} : {$result->getGlobalCode()}", [__NAMESPACE__]); if ($result instanceof ResultException) { if ($result->isSuccess()) { $queue->deleteJob($jobId); $logger->debug("Manager {$managerId} Job ResultException isSuccess {$jobId}", [__NAMESPACE__]); $this->eventTrigger(self::EVENT_SUCCESS); } elseif ($result->isFailure()) { $queue->deleteJob($jobId); $logger->debug("Manager {$managerId} Job ResultException isFail {$jobId}", [__NAMESPACE__]); $this->eventTrigger(self::EVENT_FAIL); } elseif ($result->isError()) { $queue->buryJob($jobId); $logger->debug("Manager {$managerId} Job ResultException isError {$jobId}", [__NAMESPACE__]); $this->eventTrigger(self::EVENT_ERROR); } elseif ($result->isRetry()) { $logger->debug("Manager {$managerId} Job ResultException isRetry {$jobId}", [__NAMESPACE__]); $job->addAttempts(); $job->save(); // important this will saved result to job !!! $attemptCount = $job->getAttempts(); $attemptDelay = $job->countAttemptQueueDelay(); // can be new worker class "like redirect in sms/alpha to sms" $gatewayClass = $job->getWorkerClass(); $queue->deleteJob($jobId); if ($job->hasAttempt()) { $logger->debug("job {$jobId} has {$attemptCount} attempts (max:{$job->getAttemptsMax()}) and will be delayed {$attemptDelay}", [__NAMESPACE__]); $queue->putJob($gatewayClass, $jobId, $attemptDelay); $this->eventTrigger(self::EVENT_RETRY); } else { $logger->debug("job {$jobId} has {$attemptCount} attempts (max:{$job->getAttemptsMax()}) and will have bad result", [__NAMESPACE__]); $job->setResult(new ResultException(ResultException::FAILURE_MAX_ATTEMPTS)); $job->save(); $this->eventTrigger(self::EVENT_FAIL); } } elseif ($result->isRedirect()) { $queue->deleteJob($jobId); $queue->putJob($job->getWorkerClass(), $jobId); $logger->debug("Job ResultException isRedirect {$jobId} to {$job->getWorkerClass()}", [__NAMESPACE__]); $this->eventTrigger(self::EVENT_REDIRECT); } else { $queue->deleteJob($jobId); $logger->error("Undefined result job id : {$jobId}", [__NAMESPACE__]); } } else { $logger->error('job result is not type of AbstractResultException', [__NAMESPACE__]); } if ($job->getSchedule()) { $newJob = clone $job; $newJob(); $logger->debug("Manager {$managerId} catch daemon shutdown, finish listening queue", [__NAMESPACE__]); } if ($this->isLimitsExceeded() || $this->isProducerShutDown()) { break; } $this->waitDelay(); } } catch (\Exception $ex) { $logger->warning($ex->getMessage()); } $logger->info("Manager {$managerId} finish work {$workerClass}", [__NAMESPACE__]); $this->finishManagerLive(); return true; }