Skip to content

Commit a8c3fe5

Browse files
committed
Add schedule backlog to indexer:status
1 parent f93b914 commit a8c3fe5

File tree

2 files changed

+236
-32
lines changed

2 files changed

+236
-32
lines changed

app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
use Symfony\Component\Console\Input\InputInterface;
99
use Symfony\Component\Console\Output\OutputInterface;
10+
use Magento\Framework\Indexer;
11+
use Magento\Framework\Mview;
1012

1113
/**
1214
* Command for displaying status of indexers.
@@ -30,21 +32,84 @@ protected function configure()
3032
*/
3133
protected function execute(InputInterface $input, OutputInterface $output)
3234
{
35+
$table = $this->getHelperSet()->get('table');
36+
$table->setHeaders(['Title', 'Status', 'Update On', 'Schedule Status', 'Schedule Updated']);
37+
38+
$rows = [];
39+
3340
$indexers = $this->getIndexers($input);
3441
foreach ($indexers as $indexer) {
35-
$status = 'unknown';
36-
switch ($indexer->getStatus()) {
37-
case \Magento\Framework\Indexer\StateInterface::STATUS_VALID:
38-
$status = 'Ready';
39-
break;
40-
case \Magento\Framework\Indexer\StateInterface::STATUS_INVALID:
41-
$status = 'Reindex required';
42-
break;
43-
case \Magento\Framework\Indexer\StateInterface::STATUS_WORKING:
44-
$status = 'Processing';
45-
break;
42+
$view = $indexer->getView();
43+
44+
$rowData = [
45+
'Title' => $indexer->getTitle(),
46+
'Status' => $this->getStatus($indexer),
47+
'Update On' => $indexer->isScheduled() ? 'Schedule' : 'Save',
48+
'Schedule Status' => '',
49+
'Updated' => '',
50+
];
51+
52+
if ($indexer->isScheduled()) {
53+
$state = $view->getState();
54+
$rowData['Schedule Status'] = "{$state->getStatus()} ({$this->getPendingCount($view)} in backlog)";
55+
$rowData['Updated'] = $state->getUpdated();
4656
}
47-
$output->writeln(sprintf('%-50s ', $indexer->getTitle() . ':') . $status);
57+
58+
$rows[] = $rowData;
59+
}
60+
61+
usort($rows, function ($comp1, $comp2) {
62+
return strcmp($comp1['Title'], $comp2['Title']);
63+
});
64+
65+
$table->addRows($rows);
66+
$table->render($output);
67+
}
68+
69+
/**
70+
* @param Indexer\IndexerInterface $indexer
71+
* @return string
72+
*/
73+
private function getStatus(Indexer\IndexerInterface $indexer)
74+
{
75+
$status = 'unknown';
76+
switch ($indexer->getStatus()) {
77+
case \Magento\Framework\Indexer\StateInterface::STATUS_VALID:
78+
$status = 'Ready';
79+
break;
80+
case \Magento\Framework\Indexer\StateInterface::STATUS_INVALID:
81+
$status = 'Reindex required';
82+
break;
83+
case \Magento\Framework\Indexer\StateInterface::STATUS_WORKING:
84+
$status = 'Processing';
85+
break;
4886
}
87+
return $status;
88+
}
89+
90+
/**
91+
* @param Mview\ViewInterface $view
92+
* @return string
93+
*/
94+
private function getPendingCount(Mview\ViewInterface $view)
95+
{
96+
$changelog = $view->getChangelog();
97+
98+
try {
99+
$currentVersionId = $changelog->getVersion();
100+
} catch (Mview\View\ChangelogTableNotExistsException $e) {
101+
return '';
102+
}
103+
104+
$state = $view->getState();
105+
106+
$pendingCount = count($changelog->getList($state->getVersionId(), $currentVersionId));
107+
108+
$pendingString = "<error>$pendingCount</error>";
109+
if ($pendingCount <= 0) {
110+
$pendingString = "<info>$pendingCount</info>";
111+
}
112+
113+
return $pendingString;
49114
}
50115
}

app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php

Lines changed: 159 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use Magento\Framework\Indexer\StateInterface;
99
use Magento\Indexer\Console\Command\IndexerStatusCommand;
1010
use Symfony\Component\Console\Tester\CommandTester;
11+
use Symfony\Component\Console\Helper\HelperSet;
12+
use Symfony\Component\Console\Helper\TableHelper;
1113

1214
class IndexerStatusCommandTest extends AbstractIndexerCommandCommonSetup
1315
{
@@ -18,35 +20,134 @@ class IndexerStatusCommandTest extends AbstractIndexerCommandCommonSetup
1820
*/
1921
private $command;
2022

23+
/**
24+
* @param \PHPUnit_Framework_MockObject_MockObject $indexerMock
25+
* @param array $data
26+
* @return mixed
27+
*/
28+
private function attachViewToIndexerMock($indexerMock, array $data)
29+
{
30+
/** @var \Magento\Framework\Mview\View\Changelog|\PHPUnit_Framework_MockObject_MockObject $changelog */
31+
$changelog = $this->getMockBuilder(\Magento\Framework\Mview\View\Changelog::class)
32+
->disableOriginalConstructor()
33+
->getMock();
34+
35+
$changelog->expects($this->any())
36+
->method('getList')
37+
->willReturn(range(0, $data['view']['changelog']['list_size']-1));
38+
39+
/** @var \Magento\Indexer\Model\Mview\View\State|\PHPUnit_Framework_MockObject_MockObject $stateMock */
40+
$stateMock = $this->getMockBuilder(\Magento\Indexer\Model\Mview\View\State::class)
41+
->disableOriginalConstructor()
42+
->setMethods(null)
43+
->getMock();
44+
45+
$stateMock->addData($data['view']['state']);
46+
47+
/** @var \Magento\Framework\Mview\View|\PHPUnit_Framework_MockObject_MockObject $viewMock */
48+
$viewMock = $this->getMockBuilder(\Magento\Framework\Mview\View::class)
49+
->disableOriginalConstructor()
50+
->setMethods(['getChangelog', 'getState'])
51+
->getMock();
52+
53+
$viewMock->expects($this->any())
54+
->method('getState')
55+
->willReturn($stateMock);
56+
$viewMock->expects($this->any())
57+
->method('getChangelog')
58+
->willReturn($changelog);
59+
60+
$indexerMock->method('getView')
61+
->willReturn($viewMock);
62+
63+
return $indexerMock;
64+
}
65+
2166
/**
2267
* @param array $indexers
23-
* @param array $statuses
68+
*
2469
* @dataProvider executeAllDataProvider
2570
*/
26-
public function testExecuteAll(array $indexers, array $statuses)
71+
public function testExecuteAll(array $indexers)
2772
{
2873
$this->configureAdminArea();
2974
$indexerMocks = [];
3075
foreach ($indexers as $indexerData) {
3176
$indexerMock = $this->getIndexerMock(
32-
['getStatus'],
77+
['getStatus', 'isScheduled', 'getState', 'getView'],
3378
$indexerData
3479
);
80+
3581
$indexerMock->method('getStatus')
36-
->willReturn($statuses[$indexerData['indexer_id']]);
82+
->willReturn($indexerData['status']);
83+
$indexerMock->method('isScheduled')
84+
->willReturn($indexerData['is_scheduled']);
85+
86+
if ($indexerData['is_scheduled']) {
87+
$this->attachViewToIndexerMock($indexerMock, $indexerData);
88+
}
89+
3790
$indexerMocks[] = $indexerMock;
3891
}
92+
3993
$this->initIndexerCollectionByItems($indexerMocks);
4094
$this->command = new IndexerStatusCommand($this->objectManagerFactory);
95+
96+
$objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
97+
98+
$this->command->setHelperSet(
99+
$objectManager->getObject(
100+
HelperSet::class,
101+
['helpers' => [$objectManager->getObject(TableHelper::class)]]
102+
)
103+
);
104+
41105
$commandTester = new CommandTester($this->command);
42106
$commandTester->execute([]);
43-
$actualValue = $commandTester->getDisplay();
44-
$expectedValue = sprintf('%-50s ', 'Title_indexerOne' . ':') . 'Ready' . PHP_EOL
45-
. sprintf('%-50s ', 'Title_indexerTwo' . ':') . 'Reindex required' . PHP_EOL
46-
. sprintf('%-50s ', 'Title_indexerThree' . ':') . 'Processing' . PHP_EOL
47-
. sprintf('%-50s ', 'Title_indexerFour' . ':') . 'unknown' . PHP_EOL;
48107

49-
$this->assertStringStartsWith($expectedValue, $actualValue);
108+
$linesOutput = array_filter(explode(PHP_EOL, $commandTester->getDisplay()));
109+
110+
$spacer = '+----------------+------------------+-----------+-------------------------+---------------------+';
111+
112+
$this->assertCount(8, $linesOutput, 'There should be 8 lines output. 3 Spacers, 1 header, 4 content.');
113+
$this->assertEquals($linesOutput[0], $spacer, "Lines 0, 2, 7 should be spacer lines");
114+
$this->assertEquals($linesOutput[2], $spacer, "Lines 0, 2, 7 should be spacer lines");
115+
$this->assertEquals($linesOutput[7], $spacer, "Lines 0, 2, 7 should be spacer lines");
116+
117+
$headerValues = array_values(array_filter(explode('|', $linesOutput[1])));
118+
$this->assertEquals('Title', trim($headerValues[0]));
119+
$this->assertEquals('Status', trim($headerValues[1]));
120+
$this->assertEquals('Update On', trim($headerValues[2]));
121+
$this->assertEquals('Schedule Status', trim($headerValues[3]));
122+
$this->assertEquals('Schedule Updated', trim($headerValues[4]));
123+
124+
$indexer1 = array_values(array_filter(explode('|', $linesOutput[3])));
125+
$this->assertEquals('Title_indexer1', trim($indexer1[0]));
126+
$this->assertEquals('Ready', trim($indexer1[1]));
127+
$this->assertEquals('Schedule', trim($indexer1[2]));
128+
$this->assertEquals('idle (10 in backlog)', trim($indexer1[3]));
129+
$this->assertEquals('2017-01-01 11:11:11', trim($indexer1[4]));
130+
131+
$indexer2 = array_values(array_filter(explode('|', $linesOutput[4])));
132+
$this->assertEquals('Title_indexer2', trim($indexer2[0]));
133+
$this->assertEquals('Reindex required', trim($indexer2[1]));
134+
$this->assertEquals('Save', trim($indexer2[2]));
135+
$this->assertEquals('', trim($indexer2[3]));
136+
$this->assertEquals('', trim($indexer2[4]));
137+
138+
$indexer3 = array_values(array_filter(explode('|', $linesOutput[5])));
139+
$this->assertEquals('Title_indexer3', trim($indexer3[0]));
140+
$this->assertEquals('Processing', trim($indexer3[1]));
141+
$this->assertEquals('Schedule', trim($indexer3[2]));
142+
$this->assertEquals('idle (100 in backlog)', trim($indexer3[3]));
143+
$this->assertEquals('2017-01-01 11:11:11', trim($indexer3[4]));
144+
145+
$indexer4 = array_values(array_filter(explode('|', $linesOutput[6])));
146+
$this->assertEquals('Title_indexer4', trim($indexer4[0]));
147+
$this->assertEquals('unknown', trim($indexer4[1]));
148+
$this->assertEquals('Schedule', trim($indexer4[2]));
149+
$this->assertEquals('running (20 in backlog)', trim($indexer4[3]));
150+
$this->assertEquals('2017-01-01 11:11:11', trim($indexer4[4]));
50151
}
51152

52153
/**
@@ -59,27 +160,65 @@ public function executeAllDataProvider()
59160
'indexers' => [
60161
'indexer_1' => [
61162
'indexer_id' => 'indexer_1',
62-
'title' => 'Title_indexerOne'
163+
'title' => 'Title_indexer1',
164+
'status' => StateInterface::STATUS_VALID,
165+
'is_scheduled' => true,
166+
'view' => [
167+
'state' => [
168+
'status' => 'idle',
169+
'updated' => '2017-01-01 11:11:11',
170+
],
171+
'changelog' => [
172+
'list_size' => 10
173+
]
174+
]
63175
],
64176
'indexer_2' => [
65177
'indexer_id' => 'indexer_2',
66-
'title' => 'Title_indexerTwo'
178+
'title' => 'Title_indexer2',
179+
'status' => StateInterface::STATUS_INVALID,
180+
'is_scheduled' => false,
181+
'view' => [
182+
'state' => [
183+
'status' => 'idle',
184+
'updated' => '2017-01-01 11:11:11',
185+
],
186+
'changelog' => [
187+
'list_size' => 99999999
188+
]
189+
]
67190
],
68191
'indexer_3' => [
69192
'indexer_id' => 'indexer_3',
70-
'title' => 'Title_indexerThree'
193+
'title' => 'Title_indexer3',
194+
'status' => StateInterface::STATUS_WORKING,
195+
'is_scheduled' => true,
196+
'view' => [
197+
'state' => [
198+
'status' => 'idle',
199+
'updated' => '2017-01-01 11:11:11',
200+
],
201+
'changelog' => [
202+
'list_size' => 100
203+
]
204+
]
71205
],
72206
'indexer_4' => [
73207
'indexer_id' => 'indexer_4',
74-
'title' => 'Title_indexerFour'
208+
'title' => 'Title_indexer4',
209+
'status' => null,
210+
'is_scheduled' => true,
211+
'view' => [
212+
'state' => [
213+
'status' => 'running',
214+
'updated' => '2017-01-01 11:11:11',
215+
],
216+
'changelog' => [
217+
'list_size' => 20
218+
]
219+
]
75220
],
76221
],
77-
'Statuses' => [
78-
'indexer_1' => StateInterface::STATUS_VALID,
79-
'indexer_2' => StateInterface::STATUS_INVALID,
80-
'indexer_3' => StateInterface::STATUS_WORKING,
81-
'indexer_4' => null,
82-
]
83222
],
84223
];
85224
}

0 commit comments

Comments
 (0)