/**
* @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;
}