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