Skip to content

Commit 5f6d181

Browse files
author
Anton Evers
committed
Don't prefetch all product data before forking
Collect it just in time before resizing, in each fork, so that when the productRepository cache is full and it starts purging, we don't need the purged products later on.
1 parent 3ab0518 commit 5f6d181

2 files changed

Lines changed: 65 additions & 54 deletions

File tree

app/code/Magento/Catalog/Console/Command/ImagesResizeCommand.php

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -121,20 +121,8 @@ protected function execute(
121121
);
122122

123123
try {
124-
$output->writeln("<info>Reading product image information</info>");
125-
foreach ($productIds as $productId) {
126-
try {
127-
/** @var \Magento\Catalog\Model\Product $product */
128-
$product = $this->productRepository->getById($productId);
129-
$product->getMediaGalleryImages();
130-
} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
131-
continue;
132-
}
133-
134-
$queue->add($product);
135-
$output->write('.');
136-
}
137-
$output->writeln("\n<info>Resizing product images</info>");
124+
$queue->setProducts($productIds);
125+
$output->writeln("<info>Resizing product images</info>");
138126
$queue->process();
139127
} catch (\Exception $e) {
140128
$output->writeln("<error>{$e->getMessage()}</error>");

app/code/Magento/Catalog/Model/Product/Image/Process/Queue.php

Lines changed: 63 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
*/
66
namespace Magento\Catalog\Model\Product\Image\Process;
77

8+
use Magento\Catalog\Api\ProductRepositoryInterface;
89
use Magento\Catalog\Model\Product;
910
use Magento\Catalog\Model\Product\Image\CacheFactory;
1011
use Magento\Framework\App\ResourceConnection;
12+
use Magento\Framework\Exception\NoSuchEntityException;
1113
use Symfony\Component\Console\Output\OutputInterface;
1214

1315
/**
@@ -38,7 +40,7 @@ class Queue
3840
protected $processIds = [];
3941

4042
/**
41-
* @var \Magento\Catalog\Model\Product[]
43+
* @var int[]
4244
*/
4345
protected $inProgress = [];
4446

@@ -47,6 +49,11 @@ class Queue
4749
*/
4850
protected $maxProcesses;
4951

52+
/**
53+
* @var ProductRepositoryInterface
54+
*/
55+
protected $productRepository;
56+
5057
/**
5158
* @var ResourceConnection
5259
*/
@@ -88,19 +95,22 @@ class Queue
8895
protected $imageCache;
8996

9097
/**
98+
* @param ProductRepositoryInterface $productRepository
9199
* @param ResourceConnection $resourceConnection
92100
* @param CacheFactory $imageCacheFactory
93101
* @param OutputInterface $output
94102
* @param array $options
95103
* @param int $maxProcesses
96104
*/
97105
public function __construct(
106+
ProductRepositoryInterface $productRepository,
98107
ResourceConnection $resourceConnection,
99108
CacheFactory $imageCacheFactory,
100109
OutputInterface $output,
101110
array $options = [],
102111
$maxProcesses = self::DEFAULT_MAX_PROCESSES_AMOUNT
103112
) {
113+
$this->productRepository = $productRepository;
104114
$this->resourceConnection = $resourceConnection;
105115
$this->output = $output;
106116
$this->imageCacheFactory = $imageCacheFactory;
@@ -110,13 +120,11 @@ public function __construct(
110120
}
111121

112122
/**
113-
* @param Product $product
114-
* @return bool true on success
123+
* @param array $productIds
115124
*/
116-
public function add(Product $product)
125+
public function setProducts(array $productIds)
117126
{
118-
$this->products[$product->getId()] = $product;
119-
return true;
127+
$this->products = array_combine($productIds, $productIds);
120128
}
121129

122130
/**
@@ -139,12 +147,12 @@ public function process()
139147
$this->start = $this->lastJobStarted = time();
140148
$products = $this->products;
141149
while (count($products)) {
142-
foreach ($products as $productId => $product) {
143-
$this->assertAndExecute($productId, $products, $product);
150+
foreach ($products as $productId) {
151+
$this->assertAndExecute($productId, $products);
144152
}
145153
usleep(20000);
146-
foreach ($this->inProgress as $productId => $product) {
147-
if ($this->isResized($product)) {
154+
foreach ($this->inProgress as $productId) {
155+
if ($this->isResized($productId)) {
148156
unset($this->inProgress[$productId]);
149157
}
150158
}
@@ -160,14 +168,13 @@ public function process()
160168
*
161169
* @param int $productId
162170
* @param array $products
163-
* @param Product $product
164171
* @return void
165172
*/
166-
protected function assertAndExecute($productId, array & $products, Product $product)
173+
public function assertAndExecute($productId, array & $products)
167174
{
168175
if ($this->maxProcesses < 2 || (count($this->inProgress) < $this->maxProcesses)) {
169176
unset($products[$productId]);
170-
$this->execute($product);
177+
$this->execute($productId);
171178
}
172179
}
173180

@@ -176,7 +183,7 @@ protected function assertAndExecute($productId, array & $products, Product $prod
176183
*
177184
* @return void
178185
*/
179-
protected function awaitForAllProcesses()
186+
public function awaitForAllProcesses()
180187
{
181188
while ($this->inProgress) {
182189
foreach ($this->inProgress as $productId => $product) {
@@ -195,17 +202,17 @@ protected function awaitForAllProcesses()
195202
/**
196203
* @return bool
197204
*/
198-
protected function isCanBeParalleled()
205+
public function isCanBeParalleled()
199206
{
200207
return function_exists('pcntl_fork') && $this->maxProcesses > 1;
201208
}
202209

203210
/**
204-
* @param Product $product
211+
* @param int $productId
205212
* @return bool true on success for main process and exit for child process
206213
* @SuppressWarnings(PHPMD.ExitExpression)
207214
*/
208-
protected function execute(Product $product)
215+
public function execute(int $productId)
209216
{
210217
$this->lastJobStarted = time();
211218

@@ -216,72 +223,88 @@ protected function execute(Product $product)
216223
}
217224

218225
if ($pid) {
219-
$this->inProgress[$product->getId()] = $product;
220-
$this->processIds[$product->getId()] = $pid;
226+
$this->inProgress[$productId] = $productId;
227+
$this->processIds[$productId] = $pid;
221228
return true;
222229
}
223230

231+
try {
232+
/** @var \Magento\Catalog\Model\Product $product */
233+
$product = $this->productRepository->getById($productId);
234+
$product->getMediaGalleryImages();
235+
} catch (NoSuchEntityException $e) {
236+
exit();
237+
}
238+
224239
// process child process
225240
$this->inProgress = [];
226241
$this->imageCache->generate($product);
227242
$this->output->write('.');
228243
exit();
229244
}
230245

246+
try {
247+
/** @var \Magento\Catalog\Model\Product $product */
248+
$product = $this->productRepository->getById($productId);
249+
$product->getMediaGalleryImages();
250+
} catch (NoSuchEntityException $e) {
251+
return true;
252+
}
253+
231254
$this->imageCache->generate($product);
232255
$this->output->write('.');
233256
return true;
234257
}
235258

236259
/**
237-
* @param \Magento\Catalog\Model\Product $product
260+
* @param int $productId
238261
* @return bool
239262
*/
240-
protected function isResized($product)
263+
public function isResized($productId)
241264
{
242265
if ($this->isCanBeParalleled()) {
243-
if ($this->getState($product) === null) {
244-
$pid = pcntl_waitpid($this->getPid($product), $status, WNOHANG);
245-
if ($pid === $this->getPid($product)) {
246-
$this->setState($product, self::STATE_RESIZED);
266+
if ($this->getState($productId) === null) {
267+
$pid = pcntl_waitpid($this->getPid($productId), $status, WNOHANG);
268+
if ($pid === $this->getPid($productId)) {
269+
$this->setState($productId, self::STATE_RESIZED);
247270

248-
unset($this->inProgress[$product->getId()]);
271+
unset($this->inProgress[$productId]);
249272
return pcntl_wexitstatus($status) === 0;
250273
}
251274
return false;
252275
}
253276

254277
}
255-
return $this->getState($product);
278+
return $this->getState($productId);
256279
}
257280

258281
/**
259-
* @param Product $product
282+
* @param int $productId
260283
* @return null|int
261284
*/
262-
protected function getState(Product $product)
285+
public function getState($productId)
263286
{
264-
return isset($this->state[$product->getId()]) ?: null;
287+
return isset($this->state[$productId]) ?: null;
265288
}
266289

267290
/**
268-
* @param Product $product
291+
* @param int $productId
269292
* @param int $state
270293
* @return null|int
271294
*/
272-
protected function setState(Product $product, $state)
295+
public function setState($productId, $state)
273296
{
274-
return $this->state[$product->getId()] = $state;
297+
return $this->state[$productId] = $state;
275298
}
276299

277300
/**
278-
* @param Product $product
301+
* @param int $productId
279302
* @return int|null
280303
*/
281-
protected function getPid(Product $product)
304+
public function getPid($productId)
282305
{
283-
return isset($this->processIds[$product->getId()])
284-
? $this->processIds[$product->getId()]
306+
return isset($this->processIds[$productId])
307+
? $this->processIds[$productId]
285308
: null;
286309
}
287310

@@ -294,10 +317,10 @@ protected function getPid(Product $product)
294317
*/
295318
public function __destruct()
296319
{
297-
foreach ($this->inProgress as $product) {
298-
if (pcntl_waitpid($this->getPid($product), $status) === -1) {
320+
foreach ($this->inProgress as $productId) {
321+
if (pcntl_waitpid($this->getPid($productId), $status) === -1) {
299322
throw new \RuntimeException(
300-
'Error while waiting for image resize for product: ' . $this->getPid($product)
323+
'Error while waiting for image resize for product ID: ' . $this->getPid($productId)
301324
. '; Status: ' . $status
302325
);
303326
}

0 commit comments

Comments
 (0)