From 79d669c37d7367b42e7489aaff75bd88d9e5d106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 29 Apr 2016 17:36:28 +0200 Subject: [PATCH] Adding background:worker command --- core/Command/Background/Worker.php | 61 ++++++++ core/register_command.php | 1 + db_structure.xml | 12 ++ lib/private/BackgroundJob/JobList.php | 1 + lib/private/Console/CommandLogger.php | 204 ++++++++++++++++++++++++++ version.php | 2 +- 6 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 core/Command/Background/Worker.php create mode 100644 lib/private/Console/CommandLogger.php diff --git a/core/Command/Background/Worker.php b/core/Command/Background/Worker.php new file mode 100644 index 000000000000..fa2106fb3265 --- /dev/null +++ b/core/Command/Background/Worker.php @@ -0,0 +1,61 @@ +jobList = \OC::$server->getJobList(); + parent::__construct(); + } + + protected function configure() { + $this + ->setName("background:worker") + ->setDescription("Listen to the background job queue and execute the jobs") + ->addOption('sleep', null, InputOption::VALUE_OPTIONAL, 'Number of seconds to sleep when no job is available', 3); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $this->logger = new CommandLogger($output); + + $waitTime = $input->getOption('sleep'); + while (true) { + if (is_null($this->executeNext())) { + sleep($waitTime); + } + } + } + + private function executeNext() { + $job = $this->jobList->getNext(); + if (is_null($job)) { + return null; + } + $jobId = $job->getId(); + $this->logger->debug('Run job with ID ' . $job->getId(), ['app' => 'cron']); + $job->execute($this->jobList, $this->logger); + $this->logger->debug('Finished job with ID ' . $job->getId(), ['app' => 'cron']); + + $this->jobList->setLastJob($job); + unset($job); + + return $jobId; + } +} diff --git a/core/register_command.php b/core/register_command.php index 01ec2e7f28e7..a30d3317c514 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -61,6 +61,7 @@ $application->add(new OC\Core\Command\Background\Cron(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Background\WebCron(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Background\Ajax(\OC::$server->getConfig())); + $application->add(new OC\Core\Command\Background\Worker()); $application->add(new OC\Core\Command\Config\App\DeleteConfig(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Config\App\GetConfig(\OC::$server->getConfig())); diff --git a/db_structure.xml b/db_structure.xml index 6e57b003fcf1..445b0eb4ca8f 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -973,6 +973,18 @@ false + + reserved + integer + 0 + false + + + reserved_at + timestamp + + false + job_class_index diff --git a/lib/private/BackgroundJob/JobList.php b/lib/private/BackgroundJob/JobList.php index 2429b8304463..654d0fd909a6 100644 --- a/lib/private/BackgroundJob/JobList.php +++ b/lib/private/BackgroundJob/JobList.php @@ -173,6 +173,7 @@ public function getNext() { $query->select('*') ->from('jobs') ->where($query->expr()->lt('id', $query->createNamedParameter($lastId, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->eq('reserved', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT))) ->orderBy('id', 'DESC') ->setMaxResults(1); $result = $query->execute(); diff --git a/lib/private/Console/CommandLogger.php b/lib/private/Console/CommandLogger.php new file mode 100644 index 000000000000..f1c4f8db21b7 --- /dev/null +++ b/lib/private/Console/CommandLogger.php @@ -0,0 +1,204 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Console; + +use OCP\ILogger; +use OCP\Util; +use Symfony\Component\Console\Output\OutputInterface; + +class CommandLogger implements ILogger { + + /** @var OutputInterface */ + private $output; + + /** + * CommandLogger constructor. + * + * @param OutputInterface $output + */ + public function __construct(OutputInterface $output) { + $this->output = $output; + } + + /** + * System is unusable. + * + * @param string $message + * @param array $context + * @return null + * @since 7.0.0 + */ + public function emergency($message, array $context = array()) { + $this->log(Util::FATAL, $message, $context); + } + + /** + * Action must be taken immediately. + * + * @param string $message + * @param array $context + * @return null + * @since 7.0.0 + */ + public function alert($message, array $context = array()) { + $this->log(Util::ERROR, $message, $context); + } + + /** + * Critical conditions. + * + * @param string $message + * @param array $context + * @return null + * @since 7.0.0 + */ + public function critical($message, array $context = array()) { + $this->log(Util::ERROR, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * @return null + * @since 7.0.0 + */ + public function error($message, array $context = array()) { + $this->log(Util::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * @param string $message + * @param array $context + * @return null + * @since 7.0.0 + */ + public function warning($message, array $context = array()) { + $this->log(Util::WARN, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * @return null + * @since 7.0.0 + */ + public function notice($message, array $context = array()) { + $this->log(Util::INFO, $message, $context); + } + + /** + * Interesting events. + * + * @param string $message + * @param array $context + * @return null + * @since 7.0.0 + */ + public function info($message, array $context = array()) { + $this->log(Util::ERROR, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * @return null + * @since 7.0.0 + */ + public function debug($message, array $context = array()) { + $this->log(Util::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * @return mixed + * @since 7.0.0 + */ + public function log($level, $message, array $context = array()) { + $minLevel = Util::INFO; + $verbosity = $this->output->getVerbosity(); + if ($verbosity === OutputInterface::VERBOSITY_DEBUG) { + $minLevel = Util::DEBUG; + } + if ($verbosity === OutputInterface::VERBOSITY_QUIET) { + $minLevel = Util::ERROR; + } + if ($level < $minLevel) { + return; + } + + // interpolate $message as defined in PSR-3 + $replace = array(); + foreach ($context as $key => $val) { + $replace['{' . $key . '}'] = $val; + } + + // interpolate replacement values into the message and return + $message = strtr($message, $replace); + $style = ($level > 2) ?'error' : 'info'; + + $this->output->writeln("<$style>$message"); + } + + /** + * Logs an exception very detailed + * An additional message can we written to the log by adding it to the + * context. + * + * + * $logger->logException($ex, [ + * 'message' => 'Exception during cron job execution' + * ]); + * + * + * @param \Exception | \Throwable $exception + * @param array $context + * @return void + * @since 8.2.0 + */ + public function logException($exception, array $context = array()) { + $exception = array( + 'Exception' => get_class($exception), + 'Message' => $exception->getMessage(), + 'Code' => $exception->getCode(), + 'Trace' => $exception->getTraceAsString(), + 'File' => $exception->getFile(), + 'Line' => $exception->getLine(), + ); + $exception['Trace'] = preg_replace('!(login|checkPassword|updatePrivateKeyPassword)\(.*\)!', '$1(*** username and password replaced ***)', $exception['Trace']); + $msg = isset($context['message']) ? $context['message'] : 'Exception'; + $msg .= ': ' . json_encode($exception); + $this->error($msg, $context); + } +} diff --git a/version.php b/version.php index d9e1ca1df1c3..a4f1c4dbce07 100644 --- a/version.php +++ b/version.php @@ -26,7 +26,7 @@ // We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades // between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel // when updating major/minor version number. -$OC_Version = array(9, 1, 0, 2); +$OC_Version = array(9, 1, 0, 3); // The human readable string $OC_VersionString = '9.1.0 pre alpha';