|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
declare(strict_types=1); |
|
4
|
|
|
|
|
5
|
|
|
/** |
|
6
|
|
|
* balloon |
|
7
|
|
|
* |
|
8
|
|
|
* @copyright Copryright (c) 2012-2019 gyselroth GmbH (https://gyselroth.com) |
|
9
|
|
|
* @license GPL-3.0 https://opensource.org/licenses/GPL-3.0 |
|
10
|
|
|
*/ |
|
11
|
|
|
|
|
12
|
|
|
namespace Balloon\App\Elasticsearch\Console; |
|
13
|
|
|
|
|
14
|
|
|
use Balloon\App\Elasticsearch\Job; |
|
15
|
|
|
use Balloon\Filesystem; |
|
16
|
|
|
use Balloon\Server; |
|
17
|
|
|
use GetOpt\GetOpt; |
|
18
|
|
|
use InvalidArgumentException; |
|
19
|
|
|
use MongoDB\Driver\Exception\ServerException; |
|
20
|
|
|
use Psr\Log\LoggerInterface; |
|
21
|
|
|
use TaskScheduler\Scheduler; |
|
22
|
|
|
|
|
23
|
|
|
class Elasticsearch |
|
24
|
|
|
{ |
|
25
|
|
|
/** |
|
26
|
|
|
* Logger. |
|
27
|
|
|
* |
|
28
|
|
|
* @var LoggerInterface |
|
29
|
|
|
*/ |
|
30
|
|
|
protected $logger; |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* Getopt. |
|
34
|
|
|
* |
|
35
|
|
|
* @var GetOpt |
|
36
|
|
|
*/ |
|
37
|
|
|
protected $getopt; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* Scheduler. |
|
41
|
|
|
* |
|
42
|
|
|
* @var Scheduler |
|
43
|
|
|
*/ |
|
44
|
|
|
protected $scheduler; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* Filesystem. |
|
48
|
|
|
* |
|
49
|
|
|
* @var Filesystem |
|
50
|
|
|
*/ |
|
51
|
|
|
protected $fs; |
|
52
|
|
|
|
|
53
|
|
|
/** |
|
54
|
|
|
* Bulk. |
|
55
|
|
|
* |
|
56
|
|
|
* @var int |
|
57
|
|
|
*/ |
|
58
|
|
|
protected $bulk = 200; |
|
59
|
|
|
|
|
60
|
|
|
/** |
|
61
|
|
|
* Constructor. |
|
62
|
|
|
*/ |
|
63
|
|
View Code Duplication |
public function __construct(GetOpt $getopt, Server $server, Scheduler $scheduler, LoggerInterface $logger, array $config = []) |
|
|
|
|
|
|
64
|
|
|
{ |
|
65
|
|
|
$this->logger = $logger; |
|
66
|
|
|
$this->getopt = $getopt; |
|
67
|
|
|
$this->fs = $server->getFilesystem(); |
|
68
|
|
|
$this->scheduler = $scheduler; |
|
69
|
|
|
$this->setOptions($config); |
|
70
|
|
|
} |
|
71
|
|
|
|
|
72
|
|
|
/** |
|
73
|
|
|
* Reindex elasticsearch. |
|
74
|
|
|
*/ |
|
75
|
|
|
public function __invoke(): bool |
|
76
|
|
|
{ |
|
77
|
|
|
if (!in_array('reindex', $this->getopt->getOperands())) { |
|
78
|
|
|
echo $this->getopt->getHelpText(); |
|
79
|
|
|
|
|
80
|
|
|
return false; |
|
81
|
|
|
} |
|
82
|
|
|
|
|
83
|
|
|
$total = $this->fs->countNodes(); |
|
84
|
|
|
$stack = []; |
|
|
|
|
|
|
85
|
|
|
$done = 0; |
|
|
|
|
|
|
86
|
|
|
$result = false; |
|
87
|
|
|
$skip = $this->getopt->getOption('skip') | 0; |
|
88
|
|
|
$this->bulk = $this->getopt->getOption('bulk') ? (int) $this->getopt->getOption('bulk') : $this->bulk; |
|
89
|
|
|
$total -= $skip; |
|
90
|
|
|
|
|
91
|
|
|
if ($total < 0) { |
|
92
|
|
|
$total = 0; |
|
93
|
|
|
} |
|
94
|
|
|
|
|
95
|
|
|
$this->logger->info('reindex elasticsearch, nodes left: {total}/{total}', [ |
|
96
|
|
|
'category' => get_class($this), |
|
97
|
|
|
'total' => $total, |
|
98
|
|
|
]); |
|
99
|
|
|
|
|
100
|
|
|
$stack = []; |
|
|
|
|
|
|
101
|
|
|
$done = 0; |
|
|
|
|
|
|
102
|
|
|
|
|
103
|
|
|
while ($result === false) { |
|
104
|
|
|
try { |
|
105
|
|
|
$this->index($total, $skip); |
|
106
|
|
|
$result = true; |
|
107
|
|
|
} catch (ServerException $e) { |
|
|
|
|
|
|
108
|
|
|
$skip -= $this->bulk; |
|
109
|
|
|
|
|
110
|
|
|
$this->logger->error('cursor timeout captchered, restart from {skip}', [ |
|
111
|
|
|
'category' => get_class($this), |
|
112
|
|
|
'exception' => $e, |
|
113
|
|
|
'skip' => $skip, |
|
114
|
|
|
]); |
|
115
|
|
|
} |
|
116
|
|
|
} |
|
117
|
|
|
|
|
118
|
|
|
return true; |
|
119
|
|
|
} |
|
120
|
|
|
|
|
121
|
|
|
/** |
|
122
|
|
|
* Set options. |
|
123
|
|
|
*/ |
|
124
|
|
View Code Duplication |
public function setOptions(array $config = []) |
|
|
|
|
|
|
125
|
|
|
{ |
|
126
|
|
|
foreach ($config as $key => $value) { |
|
127
|
|
|
switch ($key) { |
|
128
|
|
|
case 'bulk': |
|
129
|
|
|
$this->{$key} = (int) $value; |
|
130
|
|
|
|
|
131
|
|
|
break; |
|
132
|
|
|
default: |
|
133
|
|
|
throw new InvalidArgumentException('invalid option '.$key.' given'); |
|
134
|
|
|
} |
|
135
|
|
|
} |
|
136
|
|
|
|
|
137
|
|
|
return $this; |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
/** |
|
141
|
|
|
* Get operands. |
|
142
|
|
|
*/ |
|
143
|
|
|
public static function getOperands(): array |
|
144
|
|
|
{ |
|
145
|
|
|
return [ |
|
146
|
|
|
\GetOpt\Operand::create('reindex')->setDescription('Reindex entire elasticsearch indices, note this may take a while according to your number of nodes)'), |
|
147
|
|
|
]; |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
/** |
|
151
|
|
|
* Get options. |
|
152
|
|
|
*/ |
|
153
|
|
|
public static function getOptions(): array |
|
154
|
|
|
{ |
|
155
|
|
|
return [ |
|
156
|
|
|
\GetOpt\Option::create('b', 'bulk', \GetOpt\GetOpt::REQUIRED_ARGUMENT)->setDescription('Specify the bulk size [Default: 200]'), |
|
157
|
|
|
\GetOpt\Option::create('s', 'skip', \GetOpt\GetOpt::REQUIRED_ARGUMENT)->setDescription('Instead start from node 0, you may skip nodes by specifing a different start index'), |
|
158
|
|
|
]; |
|
159
|
|
|
} |
|
160
|
|
|
|
|
161
|
|
|
/** |
|
162
|
|
|
* Index. |
|
163
|
|
|
*/ |
|
164
|
|
|
protected function index(int $total = 0, int &$done = 0): bool |
|
165
|
|
|
{ |
|
166
|
|
|
$stack = []; |
|
167
|
|
|
|
|
168
|
|
|
foreach ($this->fs->findNodesByFilter([], $done) as $node) { |
|
169
|
|
|
$stack[] = $this->scheduler->addJob(Job::class, [ |
|
170
|
|
|
'id' => $node->getId(), |
|
171
|
|
|
'action' => Job::ACTION_CREATE, |
|
172
|
|
|
]); |
|
173
|
|
|
|
|
174
|
|
|
if (count($stack) >= $this->bulk) { |
|
175
|
|
|
$this->logger->info('waiting for ['.$this->bulk.'] jobs to be finished', [ |
|
176
|
|
|
'category' => get_class($this), |
|
177
|
|
|
]); |
|
178
|
|
|
|
|
179
|
|
|
$done += $this->bulk; |
|
180
|
|
|
$this->logger->info('reindex elasticsearch {percent}, nodes left: {done}/{total}', [ |
|
181
|
|
|
'category' => get_class($this), |
|
182
|
|
|
'percent' => round($done / $total * 100, 1).'%', |
|
183
|
|
|
'total' => $total, |
|
184
|
|
|
'done' => $done, |
|
185
|
|
|
]); |
|
186
|
|
|
|
|
187
|
|
|
$this->scheduler->waitFor($stack); |
|
188
|
|
|
$stack = []; |
|
189
|
|
|
} |
|
190
|
|
|
} |
|
191
|
|
|
|
|
192
|
|
|
return true; |
|
193
|
|
|
} |
|
194
|
|
|
} |
|
195
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.