Skip to content

Commit 1830a72

Browse files
oshmyheliukshiftedreality
authored andcommitted
MAGECLOUD-1343: Drop Stuck Crons During Deployment Process (#103)
1 parent 567f3aa commit 1830a72

File tree

3 files changed

+152
-0
lines changed

3 files changed

+152
-0
lines changed

src/App/Container.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public function __construct(string $root, array $config)
9696
$this->make(DeployProcess\InstallUpdate::class),
9797
$this->make(DeployProcess\DeployStaticContent::class),
9898
$this->make(DeployProcess\DisableGoogleAnalytics::class),
99+
$this->make(DeployProcess\UnlockCronJobs::class)
99100
],
100101
]);
101102
});

src/Process/Deploy/UnlockCronJobs.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\MagentoCloud\Process\Deploy;
7+
8+
use Magento\MagentoCloud\DB\ConnectionInterface;
9+
use Magento\MagentoCloud\Process\ProcessInterface;
10+
use Psr\Log\LoggerInterface;
11+
12+
/**
13+
* Updates running cron jobs.
14+
*
15+
* In magento version 2.2 was implemented locking functionality for cron jobs, new cron jobs can't be started
16+
* if exist job in status 'running' with same 'job_code'.
17+
* This process are used for unlocking cron jobs that stuck in 'running' status.
18+
*/
19+
class UnlockCronJobs implements ProcessInterface
20+
{
21+
const STATUS_RUNNING = 'running';
22+
const STATUS_MISSED = 'missed';
23+
24+
/**
25+
* @var ConnectionInterface
26+
*/
27+
private $connection;
28+
29+
/**
30+
* @var LoggerInterface
31+
*/
32+
private $logger;
33+
34+
/**
35+
* @param ConnectionInterface $connection
36+
* @param LoggerInterface $logger
37+
*/
38+
public function __construct(
39+
ConnectionInterface $connection,
40+
LoggerInterface $logger
41+
) {
42+
$this->connection = $connection;
43+
$this->logger = $logger;
44+
}
45+
46+
/**
47+
* Updates running cron jobs to status 'missed'.
48+
*
49+
* {@inheritdoc}
50+
*/
51+
public function execute()
52+
{
53+
$updateCronStatusQuery = 'UPDATE `cron_schedule` SET `status` = :to_status WHERE `status` = :from_status';
54+
55+
$updatedJobsCount = $this->connection->affectingQuery(
56+
$updateCronStatusQuery,
57+
[
58+
':to_status' => self::STATUS_MISSED,
59+
':from_status' => self::STATUS_RUNNING
60+
]
61+
);
62+
63+
if ($updatedJobsCount) {
64+
$this->logger->info(
65+
sprintf(
66+
'%d cron jobs were updated from status "%s" to status "%s"',
67+
$updatedJobsCount,
68+
self::STATUS_RUNNING,
69+
self::STATUS_MISSED
70+
)
71+
);
72+
}
73+
}
74+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\MagentoCloud\Test\Unit\Process\Deploy;
7+
8+
use Magento\MagentoCloud\Process\Deploy\UnlockCronJobs;
9+
use PHPUnit\Framework\TestCase;
10+
use PHPUnit_Framework_MockObject_MockObject as Mock;
11+
use Psr\Log\LoggerInterface;
12+
use Magento\MagentoCloud\DB\ConnectionInterface;
13+
14+
class UnlockCronJobsTest extends TestCase
15+
{
16+
/**
17+
* @var LoggerInterface|Mock
18+
*/
19+
private $loggerMock;
20+
21+
/**
22+
* @var ConnectionInterface|Mock
23+
*/
24+
private $connectionMock;
25+
26+
/**
27+
* @var UnlockCronJobs
28+
*/
29+
private $process;
30+
31+
protected function setUp()
32+
{
33+
$this->loggerMock = $this->getMockForAbstractClass(LoggerInterface::class);
34+
$this->connectionMock = $this->getMockForAbstractClass(ConnectionInterface::class);
35+
36+
$this->process = new UnlockCronJobs(
37+
$this->connectionMock,
38+
$this->loggerMock
39+
);
40+
}
41+
42+
public function testExecute()
43+
{
44+
$this->updateCronJobs(5);
45+
$this->loggerMock->expects($this->once())
46+
->method('info')
47+
->with('5 cron jobs were updated from status "running" to status "missed"');
48+
49+
$this->process->execute();
50+
}
51+
52+
public function testExecuteNoJobsUpdated()
53+
{
54+
$this->updateCronJobs(0);
55+
$this->loggerMock->expects($this->never())
56+
->method('info');
57+
58+
$this->process->execute();
59+
}
60+
61+
/**
62+
* @param int $updatedRowsCount
63+
*/
64+
private function updateCronJobs(int $updatedRowsCount)
65+
{
66+
$this->connectionMock->expects($this->once())
67+
->method('affectingQuery')
68+
->with(
69+
'UPDATE `cron_schedule` SET `status` = :to_status WHERE `status` = :from_status',
70+
[
71+
':to_status' => UnlockCronJobs::STATUS_MISSED,
72+
':from_status' => UnlockCronJobs::STATUS_RUNNING
73+
]
74+
)
75+
->willReturn($updatedRowsCount);
76+
}
77+
}

0 commit comments

Comments
 (0)