@@ -116,6 +116,16 @@ class ProcessCronQueueObserver implements ObserverInterface
116
116
*/
117
117
private $ state ;
118
118
119
+ /**
120
+ * @var array
121
+ */
122
+ private $ invalid = [];
123
+
124
+ /**
125
+ * @var array
126
+ */
127
+ private $ jobs ;
128
+
119
129
/**
120
130
* @param \Magento\Framework\ObjectManagerInterface $objectManager
121
131
* @param \Magento\Cron\Model\ScheduleFactory $scheduleFactory
@@ -176,17 +186,21 @@ public function execute(\Magento\Framework\Event\Observer $observer)
176
186
$ phpPath = $ this ->phpExecutableFinder ->find () ?: 'php ' ;
177
187
178
188
foreach ($ jobGroupsRoot as $ groupId => $ jobsRoot ) {
189
+ $ this ->_cleanup ($ groupId );
190
+ $ this ->_generate ($ groupId );
179
191
if ($ this ->_request ->getParam ('group ' ) !== null
180
192
&& $ this ->_request ->getParam ('group ' ) !== '\'' . ($ groupId ) . '\''
181
- && $ this ->_request ->getParam ('group ' ) !== $ groupId ) {
193
+ && $ this ->_request ->getParam ('group ' ) !== $ groupId
194
+ ) {
182
195
continue ;
183
196
}
184
197
if (($ this ->_request ->getParam (self ::STANDALONE_PROCESS_STARTED ) !== '1 ' ) && (
185
198
$ this ->_scopeConfig ->getValue (
186
199
'system/cron/ ' . $ groupId . '/use_separate_process ' ,
187
200
\Magento \Store \Model \ScopeInterface::SCOPE_STORE
188
201
) == 1
189
- )) {
202
+ )
203
+ ) {
190
204
$ this ->_shell ->execute (
191
205
$ phpPath . ' %s cron:run --group= ' . $ groupId . ' -- ' . Cli::INPUT_KEY_BOOTSTRAP . '= '
192
206
. self ::STANDALONE_PROCESS_STARTED . '=1 ' ,
@@ -197,6 +211,7 @@ public function execute(\Magento\Framework\Event\Observer $observer)
197
211
continue ;
198
212
}
199
213
214
+ /** @var \Magento\Cron\Model\Schedule $schedule */
200
215
foreach ($ pendingJobs as $ schedule ) {
201
216
$ jobConfig = isset ($ jobsRoot [$ schedule ->getJobCode ()]) ? $ jobsRoot [$ schedule ->getJobCode ()] : null ;
202
217
if (!$ jobConfig ) {
@@ -232,9 +247,6 @@ public function execute(\Magento\Framework\Event\Observer $observer)
232
247
}
233
248
$ schedule ->save ();
234
249
}
235
-
236
- $ this ->_generate ($ groupId );
237
- $ this ->_cleanup ($ groupId );
238
250
}
239
251
}
240
252
@@ -336,8 +348,10 @@ protected function _generate($groupId)
336
348
/**
337
349
* generate global crontab jobs
338
350
*/
339
- $ jobs = $ this ->_config ->getJobs ();
351
+ $ jobs = $ this ->getJobs ();
352
+ $ this ->invalid = [];
340
353
$ this ->_generateJobs ($ jobs [$ groupId ], $ exists , $ groupId );
354
+ $ this ->cleanupScheduleMismatches ();
341
355
342
356
/**
343
357
* save time schedules generation was ran with no expiration
@@ -363,17 +377,7 @@ protected function _generate($groupId)
363
377
protected function _generateJobs ($ jobs , $ exists , $ groupId )
364
378
{
365
379
foreach ($ jobs as $ jobCode => $ jobConfig ) {
366
- $ cronExpression = null ;
367
- if (isset ($ jobConfig ['config_path ' ])) {
368
- $ cronExpression = $ this ->getConfigSchedule ($ jobConfig ) ?: null ;
369
- }
370
-
371
- if (!$ cronExpression ) {
372
- if (isset ($ jobConfig ['schedule ' ])) {
373
- $ cronExpression = $ jobConfig ['schedule ' ];
374
- }
375
- }
376
-
380
+ $ cronExpression = $ this ->getCronExpression ($ jobConfig );
377
381
if (!$ cronExpression ) {
378
382
continue ;
379
383
}
@@ -385,13 +389,15 @@ protected function _generateJobs($jobs, $exists, $groupId)
385
389
}
386
390
387
391
/**
388
- * Clean existed jobs
392
+ * Clean expired jobs
389
393
*
390
394
* @param string $groupId
391
395
* @return $this
392
396
*/
393
397
protected function _cleanup ($ groupId )
394
398
{
399
+ $ this ->cleanupDisabledJobs ($ groupId );
400
+
395
401
// check if history cleanup is needed
396
402
$ lastCleanup = (int )$ this ->_cache ->load (self ::CACHE_KEY_LAST_HISTORY_CLEANUP_AT . $ groupId );
397
403
$ historyCleanUp = (int )$ this ->_scopeConfig ->getValue (
@@ -478,13 +484,20 @@ protected function saveSchedule($jobCode, $cronExpression, $timeInterval, $exist
478
484
$ currentTime = $ this ->dateTime ->gmtTimestamp ();
479
485
$ timeAhead = $ currentTime + $ timeInterval ;
480
486
for ($ time = $ currentTime ; $ time < $ timeAhead ; $ time += self ::SECONDS_IN_MINUTE ) {
481
- $ ts = strftime ('%Y-%m-%d %H:%M:00 ' , $ time );
482
- if (!empty ($ exists [$ jobCode . '/ ' . $ ts ])) {
483
- // already scheduled
487
+ $ scheduledAt = strftime ('%Y-%m-%d %H:%M:00 ' , $ time );
488
+ $ alreadyScheduled = !empty ($ exists [$ jobCode . '/ ' . $ scheduledAt ]);
489
+ $ schedule = $ this ->generateSchedule ($ jobCode , $ cronExpression , $ time );
490
+ $ valid = $ schedule ->trySchedule ();
491
+ if (!$ valid ) {
492
+ if ($ alreadyScheduled ) {
493
+ if (!isset ($ this ->invalid [$ jobCode ])) {
494
+ $ this ->invalid [$ jobCode ] = [];
495
+ }
496
+ $ this ->invalid [$ jobCode ][] = $ scheduledAt ;
497
+ }
484
498
continue ;
485
499
}
486
- $ schedule = $ this ->generateSchedule ($ jobCode , $ cronExpression , $ time );
487
- if ($ schedule ->trySchedule ()) {
500
+ if (!$ alreadyScheduled ) {
488
501
// time matches cron expression
489
502
$ schedule ->save ();
490
503
}
@@ -523,4 +536,76 @@ protected function getScheduleTimeInterval($groupId)
523
536
524
537
return $ scheduleAheadFor ;
525
538
}
539
+
540
+ /**
541
+ * Clean up scheduled jobs that are disabled in the configuration
542
+ * This can happen when you turn off a cron job in the config and flush the cache
543
+ *
544
+ * @param string $groupId
545
+ * @return void
546
+ */
547
+ private function cleanupDisabledJobs ($ groupId )
548
+ {
549
+ $ jobs = $ this ->getJobs ();
550
+ foreach ($ jobs [$ groupId ] as $ jobCode => $ jobConfig ) {
551
+ if (!$ this ->getCronExpression ($ jobConfig )) {
552
+ /** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */
553
+ $ scheduleResource = $ this ->_scheduleFactory ->create ()->getResource ();
554
+ $ scheduleResource ->getConnection ()->delete ($ scheduleResource ->getMainTable (), [
555
+ 'status=? ' => Schedule::STATUS_PENDING ,
556
+ 'job_code=? ' => $ jobCode ,
557
+ ]);
558
+ }
559
+ }
560
+ }
561
+
562
+ /**
563
+ * @param array $jobConfig
564
+ * @return null|string
565
+ */
566
+ private function getCronExpression ($ jobConfig )
567
+ {
568
+ $ cronExpression = null ;
569
+ if (isset ($ jobConfig ['config_path ' ])) {
570
+ $ cronExpression = $ this ->getConfigSchedule ($ jobConfig ) ?: null ;
571
+ }
572
+
573
+ if (!$ cronExpression ) {
574
+ if (isset ($ jobConfig ['schedule ' ])) {
575
+ $ cronExpression = $ jobConfig ['schedule ' ];
576
+ }
577
+ }
578
+ return $ cronExpression ;
579
+ }
580
+
581
+ /**
582
+ * Clean up scheduled jobs that do not match their cron expression anymore
583
+ * This can happen when you change the cron expression and flush the cache
584
+ *
585
+ * @return $this
586
+ */
587
+ private function cleanupScheduleMismatches ()
588
+ {
589
+ foreach ($ this ->invalid as $ jobCode => $ scheduledAtList ) {
590
+ /** @var \Magento\Cron\Model\ResourceModel\Schedule $scheduleResource */
591
+ $ scheduleResource = $ this ->_scheduleFactory ->create ()->getResource ();
592
+ $ scheduleResource ->getConnection ()->delete ($ scheduleResource ->getMainTable (), [
593
+ 'status=? ' => Schedule::STATUS_PENDING ,
594
+ 'job_code=? ' => $ jobCode ,
595
+ 'scheduled_at in (?) ' => $ scheduledAtList ,
596
+ ]);
597
+ }
598
+ return $ this ;
599
+ }
600
+
601
+ /**
602
+ * @return array
603
+ */
604
+ private function getJobs ()
605
+ {
606
+ if ($ this ->jobs === null ) {
607
+ $ this ->jobs = $ this ->_config ->getJobs ();
608
+ }
609
+ return $ this ->jobs ;
610
+ }
526
611
}
0 commit comments