annotate core/lib/Drupal/Core/Cron.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents 1fec387a4317
children af1871eacc83
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core;
Chris@0 4
Chris@14 5 use Drupal\Component\Datetime\TimeInterface;
Chris@0 6 use Drupal\Component\Utility\Timer;
Chris@0 7 use Drupal\Core\Extension\ModuleHandlerInterface;
Chris@0 8 use Drupal\Core\Queue\QueueWorkerManagerInterface;
Chris@0 9 use Drupal\Core\Queue\RequeueException;
Chris@0 10 use Drupal\Core\State\StateInterface;
Chris@0 11 use Drupal\Core\Lock\LockBackendInterface;
Chris@0 12 use Drupal\Core\Queue\QueueFactory;
Chris@0 13 use Drupal\Core\Session\AnonymousUserSession;
Chris@0 14 use Drupal\Core\Session\AccountSwitcherInterface;
Chris@0 15 use Drupal\Core\Queue\SuspendQueueException;
Chris@0 16 use Psr\Log\LoggerInterface;
Chris@0 17 use Psr\Log\NullLogger;
Chris@0 18
Chris@0 19 /**
Chris@0 20 * The Drupal core Cron service.
Chris@0 21 */
Chris@0 22 class Cron implements CronInterface {
Chris@0 23
Chris@0 24 /**
Chris@0 25 * The module handler service.
Chris@0 26 *
Chris@0 27 * @var \Drupal\Core\Extension\ModuleHandlerInterface
Chris@0 28 */
Chris@0 29 protected $moduleHandler;
Chris@0 30
Chris@0 31 /**
Chris@0 32 * The lock service.
Chris@0 33 *
Chris@0 34 * @var \Drupal\Core\Lock\LockBackendInterface
Chris@0 35 */
Chris@0 36 protected $lock;
Chris@0 37
Chris@0 38 /**
Chris@0 39 * The queue service.
Chris@0 40 *
Chris@0 41 * @var \Drupal\Core\Queue\QueueFactory
Chris@0 42 */
Chris@0 43 protected $queueFactory;
Chris@0 44
Chris@0 45 /**
Chris@0 46 * The state service.
Chris@0 47 *
Chris@0 48 * @var \Drupal\Core\State\StateInterface
Chris@0 49 */
Chris@0 50 protected $state;
Chris@0 51
Chris@0 52 /**
Chris@0 53 * The account switcher service.
Chris@0 54 *
Chris@0 55 * @var \Drupal\Core\Session\AccountSwitcherInterface
Chris@0 56 */
Chris@0 57 protected $accountSwitcher;
Chris@0 58
Chris@0 59 /**
Chris@0 60 * A logger instance.
Chris@0 61 *
Chris@0 62 * @var \Psr\Log\LoggerInterface
Chris@0 63 */
Chris@0 64 protected $logger;
Chris@0 65
Chris@0 66 /**
Chris@0 67 * The queue plugin manager.
Chris@0 68 *
Chris@0 69 * @var \Drupal\Core\Queue\QueueWorkerManagerInterface
Chris@0 70 */
Chris@0 71 protected $queueManager;
Chris@0 72
Chris@0 73 /**
Chris@14 74 * The time service.
Chris@14 75 *
Chris@14 76 * @var \Drupal\Component\Datetime\TimeInterface
Chris@14 77 */
Chris@14 78 protected $time;
Chris@14 79
Chris@14 80 /**
Chris@0 81 * Constructs a cron object.
Chris@0 82 *
Chris@0 83 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
Chris@0 84 * The module handler
Chris@0 85 * @param \Drupal\Core\Lock\LockBackendInterface $lock
Chris@0 86 * The lock service.
Chris@0 87 * @param \Drupal\Core\Queue\QueueFactory $queue_factory
Chris@0 88 * The queue service.
Chris@0 89 * @param \Drupal\Core\State\StateInterface $state
Chris@0 90 * The state service.
Chris@0 91 * @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher
Chris@0 92 * The account switching service.
Chris@0 93 * @param \Psr\Log\LoggerInterface $logger
Chris@0 94 * A logger instance.
Chris@0 95 * @param \Drupal\Core\Queue\QueueWorkerManagerInterface $queue_manager
Chris@0 96 * The queue plugin manager.
Chris@14 97 * @param \Drupal\Component\Datetime\TimeInterface $time
Chris@14 98 * The time service.
Chris@0 99 */
Chris@14 100 public function __construct(ModuleHandlerInterface $module_handler, LockBackendInterface $lock, QueueFactory $queue_factory, StateInterface $state, AccountSwitcherInterface $account_switcher, LoggerInterface $logger, QueueWorkerManagerInterface $queue_manager, TimeInterface $time = NULL) {
Chris@0 101 $this->moduleHandler = $module_handler;
Chris@0 102 $this->lock = $lock;
Chris@0 103 $this->queueFactory = $queue_factory;
Chris@0 104 $this->state = $state;
Chris@0 105 $this->accountSwitcher = $account_switcher;
Chris@0 106 $this->logger = $logger;
Chris@0 107 $this->queueManager = $queue_manager;
Chris@14 108 $this->time = $time ?: \Drupal::service('datetime.time');
Chris@0 109 }
Chris@0 110
Chris@0 111 /**
Chris@0 112 * {@inheritdoc}
Chris@0 113 */
Chris@0 114 public function run() {
Chris@0 115 // Allow execution to continue even if the request gets cancelled.
Chris@0 116 @ignore_user_abort(TRUE);
Chris@0 117
Chris@0 118 // Force the current user to anonymous to ensure consistent permissions on
Chris@0 119 // cron runs.
Chris@0 120 $this->accountSwitcher->switchTo(new AnonymousUserSession());
Chris@0 121
Chris@0 122 // Try to allocate enough time to run all the hook_cron implementations.
Chris@0 123 drupal_set_time_limit(240);
Chris@0 124
Chris@0 125 $return = FALSE;
Chris@0 126
Chris@0 127 // Try to acquire cron lock.
Chris@0 128 if (!$this->lock->acquire('cron', 900.0)) {
Chris@0 129 // Cron is still running normally.
Chris@0 130 $this->logger->warning('Attempting to re-run cron while it is already running.');
Chris@0 131 }
Chris@0 132 else {
Chris@0 133 $this->invokeCronHandlers();
Chris@0 134 $this->setCronLastTime();
Chris@0 135
Chris@0 136 // Release cron lock.
Chris@0 137 $this->lock->release('cron');
Chris@0 138
Chris@0 139 // Return TRUE so other functions can check if it did run successfully
Chris@0 140 $return = TRUE;
Chris@0 141 }
Chris@0 142
Chris@0 143 // Process cron queues.
Chris@0 144 $this->processQueues();
Chris@0 145
Chris@0 146 // Restore the user.
Chris@0 147 $this->accountSwitcher->switchBack();
Chris@0 148
Chris@0 149 return $return;
Chris@0 150 }
Chris@0 151
Chris@0 152 /**
Chris@0 153 * Records and logs the request time for this cron invocation.
Chris@0 154 */
Chris@0 155 protected function setCronLastTime() {
Chris@0 156 // Record cron time.
Chris@14 157 $request_time = $this->time->getRequestTime();
Chris@14 158 $this->state->set('system.cron_last', $request_time);
Chris@0 159 $this->logger->notice('Cron run completed.');
Chris@0 160 }
Chris@0 161
Chris@0 162 /**
Chris@0 163 * Processes cron queues.
Chris@0 164 */
Chris@0 165 protected function processQueues() {
Chris@0 166 // Grab the defined cron queues.
Chris@0 167 foreach ($this->queueManager->getDefinitions() as $queue_name => $info) {
Chris@0 168 if (isset($info['cron'])) {
Chris@0 169 // Make sure every queue exists. There is no harm in trying to recreate
Chris@0 170 // an existing queue.
Chris@0 171 $this->queueFactory->get($queue_name)->createQueue();
Chris@0 172
Chris@0 173 $queue_worker = $this->queueManager->createInstance($queue_name);
Chris@0 174 $end = time() + (isset($info['cron']['time']) ? $info['cron']['time'] : 15);
Chris@0 175 $queue = $this->queueFactory->get($queue_name);
Chris@0 176 $lease_time = isset($info['cron']['time']) ?: NULL;
Chris@0 177 while (time() < $end && ($item = $queue->claimItem($lease_time))) {
Chris@0 178 try {
Chris@0 179 $queue_worker->processItem($item->data);
Chris@0 180 $queue->deleteItem($item);
Chris@0 181 }
Chris@0 182 catch (RequeueException $e) {
Chris@0 183 // The worker requested the task be immediately requeued.
Chris@0 184 $queue->releaseItem($item);
Chris@0 185 }
Chris@0 186 catch (SuspendQueueException $e) {
Chris@0 187 // If the worker indicates there is a problem with the whole queue,
Chris@0 188 // release the item and skip to the next queue.
Chris@0 189 $queue->releaseItem($item);
Chris@0 190
Chris@0 191 watchdog_exception('cron', $e);
Chris@0 192
Chris@0 193 // Skip to the next queue.
Chris@0 194 continue 2;
Chris@0 195 }
Chris@0 196 catch (\Exception $e) {
Chris@0 197 // In case of any other kind of exception, log it and leave the item
Chris@0 198 // in the queue to be processed again later.
Chris@0 199 watchdog_exception('cron', $e);
Chris@0 200 }
Chris@0 201 }
Chris@0 202 }
Chris@0 203 }
Chris@0 204 }
Chris@0 205
Chris@0 206 /**
Chris@0 207 * Invokes any cron handlers implementing hook_cron.
Chris@0 208 */
Chris@0 209 protected function invokeCronHandlers() {
Chris@0 210 $module_previous = '';
Chris@0 211
Chris@0 212 // If detailed logging isn't enabled, don't log individual execution times.
Chris@0 213 $time_logging_enabled = \Drupal::config('system.cron')->get('logging');
Chris@0 214 $logger = $time_logging_enabled ? $this->logger : new NullLogger();
Chris@0 215
Chris@0 216 // Iterate through the modules calling their cron handlers (if any):
Chris@0 217 foreach ($this->moduleHandler->getImplementations('cron') as $module) {
Chris@0 218
Chris@0 219 if (!$module_previous) {
Chris@0 220 $logger->notice('Starting execution of @module_cron().', [
Chris@0 221 '@module' => $module,
Chris@0 222 ]);
Chris@0 223 }
Chris@0 224 else {
Chris@0 225 $logger->notice('Starting execution of @module_cron(), execution of @module_previous_cron() took @time.', [
Chris@0 226 '@module' => $module,
Chris@0 227 '@module_previous' => $module_previous,
Chris@0 228 '@time' => Timer::read('cron_' . $module_previous) . 'ms',
Chris@0 229 ]);
Chris@0 230 }
Chris@0 231 Timer::start('cron_' . $module);
Chris@0 232
Chris@0 233 // Do not let an exception thrown by one module disturb another.
Chris@0 234 try {
Chris@0 235 $this->moduleHandler->invoke($module, 'cron');
Chris@0 236 }
Chris@0 237 catch (\Exception $e) {
Chris@0 238 watchdog_exception('cron', $e);
Chris@0 239 }
Chris@0 240
Chris@0 241 Timer::stop('cron_' . $module);
Chris@0 242 $module_previous = $module;
Chris@0 243 }
Chris@0 244 if ($module_previous) {
Chris@0 245 $logger->notice('Execution of @module_previous_cron() took @time.', [
Chris@0 246 '@module_previous' => $module_previous,
Chris@0 247 '@time' => Timer::read('cron_' . $module_previous) . 'ms',
Chris@0 248 ]);
Chris@0 249 }
Chris@0 250 }
Chris@0 251
Chris@0 252 }