Skip to content

Commit eacb702

Browse files
MAGETWO-69629: Locking all cronjobs with the same job code
1 parent 5f7f226 commit eacb702

File tree

3 files changed

+42
-5
lines changed

3 files changed

+42
-5
lines changed

app/code/Magento/Cron/Model/ResourceModel/Schedule.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,40 @@ public function trySetJobStatusAtomic($scheduleId, $newStatus, $currentStatus)
4545
}
4646
return false;
4747
}
48+
49+
/**
50+
* If job is currently in $currentStatus and there are no existing jobs
51+
* with $newStatus, set it to $newStatus and return true. Otherwise,
52+
* return false and do not change the job.
53+
* This method is used to implement locking for cron jobs that share a
54+
* job code.
55+
*
56+
* @param string $scheduleId
57+
* @param string $newStatus
58+
* @param string $currentStatus
59+
* @return bool
60+
*/
61+
public function trySetJobUniqueStatusAtomic($scheduleId, $newStatus, $currentStatus)
62+
{
63+
$connection = $this->getConnection();
64+
65+
$match = $connection->quoteInto('existing.job_code = current.job_code AND existing.status = ?', $newStatus);
66+
$selectIfUnlocked = $connection->select()
67+
->joinLeft(
68+
['existing' => $this->getTable('cron_schedule')],
69+
$match,
70+
['status' => new \Zend_Db_Expr($connection->quote($newStatus))]
71+
)
72+
->where('current.schedule_id = ?', $scheduleId)
73+
->where('current.status = ?', $currentStatus)
74+
->where('existing.schedule_id IS NULL');
75+
76+
$update = $connection->updateFromSelect($selectIfUnlocked, ['current' => $this->getTable('cron_schedule')]);
77+
$result = $connection->query($update)->rowCount();
78+
79+
if ($result == 1) {
80+
return true;
81+
}
82+
return false;
83+
}
4884
}

app/code/Magento/Cron/Model/Schedule.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ public function getNumeric($value)
221221
}
222222

223223
/**
224-
* Sets a job to STATUS_RUNNING only if it is currently in STATUS_PENDING.
224+
* Sets a job to STATUS_RUNNING only if it is currently in STATUS_PENDING
225+
* and no other jobs of the same code are currently in STATUS_RUNNING.
225226
* Returns true if status was changed and false otherwise.
226227
*
227228
* This is used to implement locking for cron jobs.
@@ -230,7 +231,7 @@ public function getNumeric($value)
230231
*/
231232
public function tryLockJob()
232233
{
233-
if ($this->_getResource()->trySetJobStatusAtomic(
234+
if ($this->_getResource()->trySetJobUniqueStatusAtomic(
234235
$this->getId(),
235236
self::STATUS_RUNNING,
236237
self::STATUS_PENDING

app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ protected function setUp()
2323

2424
$this->resourceJobMock = $this->getMockBuilder(\Magento\Cron\Model\ResourceModel\Schedule::class)
2525
->disableOriginalConstructor()
26-
->setMethods(['trySetJobStatusAtomic', '__wakeup', 'getIdFieldName'])
26+
->setMethods(['trySetJobUniqueStatusAtomic', '__wakeup', 'getIdFieldName'])
2727
->getMockForAbstractClass();
2828

2929
$this->resourceJobMock->expects($this->any())
@@ -336,7 +336,7 @@ public function testTryLockJobSuccess()
336336
$scheduleId = 1;
337337

338338
$this->resourceJobMock->expects($this->once())
339-
->method('trySetJobStatusAtomic')
339+
->method('trySetJobUniqueStatusAtomic')
340340
->with($scheduleId, Schedule::STATUS_RUNNING, Schedule::STATUS_PENDING)
341341
->will($this->returnValue(true));
342342

@@ -360,7 +360,7 @@ public function testTryLockJobFailure()
360360
$scheduleId = 1;
361361

362362
$this->resourceJobMock->expects($this->once())
363-
->method('trySetJobStatusAtomic')
363+
->method('trySetJobUniqueStatusAtomic')
364364
->with($scheduleId, Schedule::STATUS_RUNNING, Schedule::STATUS_PENDING)
365365
->will($this->returnValue(false));
366366

0 commit comments

Comments
 (0)