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