Completed
Push — issue/416 ( ddfba0 )
by Tomas Norre
04:30
created

ProcessService::multiProcess()   C

Complexity

Conditions 13
Paths 21

Size

Total Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 150.3885

Importance

Changes 0
Metric Value
cc 13
nc 21
nop 1
dl 0
loc 46
rs 6.6166
c 0
b 0
f 0
ccs 3
cts 45
cp 0.0667
crap 150.3885

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace AOE\Crawler\Service;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2019 AOE GmbH <[email protected]>
8
 *
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 3 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
28
use AOE\Crawler\Controller\CrawlerController;
29
use AOE\Crawler\Domain\Repository\ProcessRepository;
30
use AOE\Crawler\Domain\Repository\QueueRepository;
31
use TYPO3\CMS\Core\Utility\CommandUtility;
32
use TYPO3\CMS\Core\Utility\GeneralUtility;
33
use TYPO3\CMS\Extbase\Object\ObjectManager;
34
35
/**
36
 * Class ProcessService
37
 *
38
 * @package AOE\Crawler\Service
39
 */
40
class ProcessService
41
{
42
    /**
43
     * @var $timeToLive integer
44
     */
45
    private $timeToLive;
46
47
    /**
48
     * @var integer
49
     */
50
    private $countInARun;
51
52
    /**
53
     * @var integer
54
     */
55
    private $processLimit;
56
57
    /**
58
     * @var CrawlerController
59
     */
60
    private $crawlerController;
61
62
    /**
63
     * @var \AOE\Crawler\Domain\Repository\QueueRepository
64
     */
65
    private $queueRepository;
66
67
    /**
68
     * @var \AOE\Crawler\Domain\Repository\ProcessRepository
69
     */
70
    private $processRepository;
71
72
    /**
73
     * @var $verbose boolean
74
     */
75
    private $verbose;
76
77
    /**
78
     * the constructor
79
     */
80
    public function __construct()
81
    {
82
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
83
        $this->processRepository = $objectManager->get(ProcessRepository::class);
84
        $this->queueRepository = $objectManager->get(QueueRepository::class);
85
        $this->crawlerController = $objectManager->get(CrawlerController::class);
86
        $this->timeToLive = intval($this->crawlerController->extensionSettings['processMaxRunTime']);
87
        $this->countInARun = intval($this->crawlerController->extensionSettings['countInARun']);
88
        $this->processLimit = intval($this->crawlerController->extensionSettings['processLimit']);
89
        $this->verbose = intval($this->crawlerController->extensionSettings['processVerbose']);
90
    }
91
92
    /**
93
     * starts multiple processes
94
     *
95
     * @param integer $timeout
96
     *
97
     * @throws \RuntimeException
98
     */
99 1
    public function multiProcess($timeout)
100
    {
101 1
        if ($this->processLimit <= 1) {
102 1
            throw new \RuntimeException('To run crawler in multi process mode you have to configure the processLimit > 1.' . PHP_EOL);
103
        }
104
105
        $pendingItemsStart = $this->queueRepository->countAllPendingItems();
106
        $itemReportLimit = 20;
107
        $reportItemCount = $pendingItemsStart - $itemReportLimit;
108
        if ($this->verbose) {
109
            $this->reportItemStatus();
110
        }
111
        $this->startRequiredProcesses();
112
        $nextTimeOut = time() + $this->timeToLive;
113
        $currentPendingItems = '';
114
        for ($i = 0; $i < $timeout; $i++) {
115
            $currentPendingItems = $this->queueRepository->countAllPendingItems();
116
            if ($this->startRequiredProcesses()) {
117
                $nextTimeOut = time() + $this->timeToLive;
118
            }
119
            if ($currentPendingItems == 0) {
120
                if ($this->verbose) {
121
                    echo 'Finished...' . chr(10);
122
                }
123
                break;
124
            }
125
            if ($currentPendingItems < $reportItemCount) {
126
                if ($this->verbose) {
127
                    $this->reportItemStatus();
128
                }
129
                $reportItemCount = $currentPendingItems - $itemReportLimit;
130
            }
131
            sleep(1);
132
            if ($nextTimeOut < time()) {
133
                $timedOutProcesses = $this->processRepository->findAll('', 'DESC', null, 0, 'ttl >' . $nextTimeOut);
134
                $nextTimeOut = time() + $this->timeToLive;
135
                if ($this->verbose) {
136
                    echo 'Cleanup' . implode(',', $timedOutProcesses->getProcessIds()) . chr(10);
137
                }
138
                $this->crawlerController->CLI_releaseProcesses($timedOutProcesses->getProcessIds(), true);
0 ignored issues
show
Deprecated Code introduced by
The method AOE\Crawler\Controller\C...:CLI_releaseProcesses() has been deprecated with message: since crawler v6.5.1, will be removed in crawler v9.0.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
139
            }
140
        }
141
        if ($currentPendingItems > 0 && $this->verbose) {
142
            echo 'Stop with timeout' . chr(10);
143
        }
144
    }
145
146
    /**
147
     * Reports curent Status of queue
148
     */
149
    protected function reportItemStatus()
150
    {
151
        echo 'Pending:' . $this->queueRepository->countAllPendingItems() . ' / Assigned:' . $this->queueRepository->countAllAssignedPendingItems() . chr(10);
152
    }
153
154
    /**
155
     * according to the given count of pending items and the countInARun Setting this method
156
     * starts more crawling processes
157
     *
158
     * @return boolean if processes are started
159
     * @throws \Exception
160
     *
161
     */
162
    private function startRequiredProcesses()
163
    {
164
        $ret = false;
165
        $currentProcesses = $this->processRepository->countActive();
166
        $availableProcessesCount = $this->processLimit - $currentProcesses;
167
        $requiredProcessesCount = ceil($this->queueRepository->countAllUnassignedPendingItems() / $this->countInARun);
168
        $startProcessCount = min([$availableProcessesCount, $requiredProcessesCount]);
169
        if ($startProcessCount <= 0) {
170
            return $ret;
171
        }
172
        if ($startProcessCount && $this->verbose) {
173
            echo 'Start ' . $startProcessCount . ' new processes (Running:' . $currentProcesses . ')';
174
        }
175
        for ($i = 0; $i < $startProcessCount; $i++) {
176
            usleep(100);
177
            if ($this->startProcess()) {
178
                if ($this->verbose) {
179
                    echo '.';
180
                    $ret = true;
181
                }
182
            }
183
        }
184
        if ($this->verbose) {
185
            echo chr(10);
186
        }
187
        return $ret;
188
    }
189
190
    /**
191
     * starts new process
192
     * @throws \Exception if no crawler process was started
193
     */
194
    public function startProcess()
195
    {
196
        $ttl = (time() + $this->timeToLive - 1);
197
        $current = $this->processRepository->countNotTimeouted($ttl);
198
199
        // Check whether OS is Windows
200
        if (TYPO3_OS === 'WIN') {
201
            $completePath = escapeshellcmd('start ' . $this->getCrawlerCliPath());
202
        } else {
203
            $completePath = '(' . escapeshellcmd($this->getCrawlerCliPath()) . ' &) > /dev/null';
204
        }
205
206
        $fileHandler = CommandUtility::exec($completePath);
207
        if ($fileHandler === false) {
208
            throw new \Exception('could not start process!');
209
        } else {
210
            for ($i = 0; $i < 10; $i++) {
211
                if ($this->processRepository->countNotTimeouted($ttl) > $current) {
212
                    return true;
213
                }
214
                sleep(1);
215
            }
216
            throw new \Exception('Something went wrong: process did not appear within 10 seconds.');
217
        }
218
    }
219
220
    /**
221
     * Returns the path to start the crawler from the command line
222
     *
223
     * @return string
224
     */
225 1
    public function getCrawlerCliPath()
226
    {
227 1
        $composerRootDir = getenv('TYPO3_PATH_COMPOSER_ROOT') . '/';
228 1
        $jsonDecoded = json_decode(file_get_contents($composerRootDir . 'composer.json'), true);
229
230 1
        if (isset($jsonDecoded['config']['bin-dir'])) {
231 1
            $binDir = $jsonDecoded['config']['bin-dir'];
232
        } elseif (isset($jsonDecoded['config']['vendor-dir'])) {
233
            $binDir = $jsonDecoded['config']['vendor-dir'] . '/bin';
234
        } else {
235
            $binDir = 'vendor/bin';
236
        }
237
238 1
        $phpPath = $this->crawlerController->extensionSettings['phpPath'] . ' ';
239 1
        $cliPart = '/typo3cms crawler:crawlqueue';
240 1
        $scriptPath = $phpPath . $composerRootDir . $binDir . $cliPart;
241
242 1
        if (TYPO3_OS === 'WIN') {
243
            $scriptPath = str_replace('/', '\\', $scriptPath);
244
        }
245
246 1
        return ltrim($scriptPath);
247
    }
248
}
249