public function run()
{
try {
// register shutdown handler
register_shutdown_function($this->getDefaultShutdownMethod());
// bootstrap the daemon
$this->bootstrap();
// synchronize the application instance and register the class loaders
$application = $this->application;
// mark the daemon as successfully shutdown
$this->synchronized(function ($self) {
$self->state = EnumState::get(EnumState::RUNNING);
}, $this);
// create local instances of the storages
$messages = $this->messages;
$priorityKey = $this->priorityKey;
$jobsToExecute = $this->jobsToExecute;
// load the maximum number of jobs to process in parallel
$maximumJobsToProcess = $this->getManagerSettings()->getMaximumJobsToProcess();
// initialize the arrays for the message states and the jobs executing
$messageStates = array();
$jobsExecuting = array();
// keep the daemon running
while ($this->keepRunning()) {
// iterate over all job wrappers
foreach ($jobsToExecute as $jobWrapper) {
try {
// load the message
$message = $messages[$jobWrapper->jobId];
// check if we've a message found
if ($message instanceof MessageInterface) {
// set the inital message state if not done
if (isset($messageStates[$jobWrapper->jobId]) === false) {
// initialize the default message state
if ($state = $message->getState()) {
$messageStates[$jobWrapper->jobId] = $state->getState();
} else {
$messageStates[$jobWrapper->jobId] = StateUnknown::KEY;
}
}
// check the message state
switch ($messageStates[$jobWrapper->jobId]) {
// message is active and ready to be processed
case StateActive::KEY:
// set the new state now
$messageStates[$message->getMessageId()] = StateToProcess::KEY;
break;
// message is paused or in progress
// message is paused or in progress
case StatePaused::KEY:
case StateInProgress::KEY:
// make sure the job has been finished
if (isset($jobsExecuting[$message->getMessageId()]) && $jobsExecuting[$message->getMessageId()] instanceof JobInterface && $jobsExecuting[$message->getMessageId()]->isFinished()) {
// log a message that the job is still in progress
$this->getApplication()->getInitialContext()->getSystemLogger()->info(sprintf('Job %s has been finished, remove it from job queue now', $message->getMessageId()));
// set the new state now
$messageStates[$message->getMessageId()] = StateProcessed::KEY;
} else {
// log a message that the job is still in progress
$this->getApplication()->getInitialContext()->getSystemLogger()->debug(sprintf('Job %s is still in progress', $message->getMessageId()));
}
break;
// message processing failed or has been successfully processed
// message processing failed or has been successfully processed
case StateFailed::KEY:
case StateProcessed::KEY:
// load the unique message-ID
$messageId = $message->getMessageId();
// remove the job from the queue with jobs that has to be executed
unset($jobsToExecute[$messageId]);
// also remove the job
unset($jobsExecuting[$messageId]);
// finally, remove the message states and the message from the queue
unset($messageStates[$messageId]);
unset($messages[$messageId]);
break;
// message has to be processed now
// message has to be processed now
case StateToProcess::KEY:
// count messages in queue
$inQueue = sizeof($jobsExecuting);
// we only process 200 jobs in parallel
if ($inQueue < $maximumJobsToProcess) {
// start the job and add it to the internal array
$jobsExecuting[$message->getMessageId()] = new Job(clone $message, $application);
// set the new state now
$messageStates[$message->getMessageId()] = StateInProgress::KEY;
} else {
// log a message that queue is actually full
$application->getInitialContext()->getSystemLogger()->info(sprintf('Job queue full - (%d jobs/%d msg wait)', $inQueue, sizeof($messages)));
// if the job queue is full, restart iteration to remove processed jobs from queue first
continue 2;
}
break;
// message is in an unknown state -> this is weired and should never happen!
// message is in an unknown state -> this is weired and should never happen!
case StateUnknown::KEY:
// set new state now
$messageStates[$message->getMessageId()] = StateFailed::KEY;
// log a message that we've a message with a unknown state
$this->getApplication()->getInitialContext()->getSystemLogger()->critical(sprintf('Message %s has state %s', $message->getMessageId(), StateFailed::KEY));
break;
// we don't know the message state -> this is weired and should never happen!
// we don't know the message state -> this is weired and should never happen!
default:
// set the failed message state
$messageStates[$message->getMessageId()] = StateFailed::KEY;
// log a message that we've a message with an invalid state
$this->getApplication()->getInitialContext()->getSystemLogger()->critical(sprintf('Message %s has an invalid state', $message->getMessageId()));
break;
}
}
// catch all exceptions
} catch (\Exception $e) {
$application->getInitialContext()->getSystemLogger()->critical($e->__toString());
}
// reduce CPU load depending on queue priority
$this->iterate($this->getQueueTimeout());
}
// reduce CPU load after each iteration
$this->iterate($this->getDefaultTimeout());
// profile the size of the session pool
if ($this->profileLogger) {
$this->profileLogger->debug(sprintf('Processed queue worker with priority %s, size of queue size is: %d', $priorityKey, sizeof($jobsToExecute)));
}
}
// clean up the instances and free memory
$this->cleanUp();
// mark the daemon as successfully shutdown
$this->synchronized(function ($self) {
$self->state = EnumState::get(EnumState::SHUTDOWN);
}, $this);
} catch (\Exception $e) {
$application->getInitialContext()->getSystemLogger()->error($e->__toString());
}
}