Skip to content

Commit 4e960c3

Browse files
committed
MC-19250: The stuck deployment on the Cloud because of consumers
1 parent 74aa9c6 commit 4e960c3

File tree

2 files changed

+130
-2
lines changed

2 files changed

+130
-2
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\MessageQueue\Setup;
9+
10+
use Magento\Framework\Setup\ConfigOptionsListInterface;
11+
use Magento\Framework\Setup\Option\SelectConfigOption;
12+
use Magento\Framework\App\DeploymentConfig;
13+
use Magento\Framework\Config\Data\ConfigData;
14+
use Magento\Framework\Config\File\ConfigFilePool;
15+
16+
/**
17+
* Deployment configuration consumers options needed for Setup application
18+
*/
19+
class ConfigOptionsList implements ConfigOptionsListInterface
20+
{
21+
/**
22+
* Input key for the option
23+
*/
24+
const INPUT_KEY_QUEUE_CONSUMERS_WAIT_FOR_MESSAGES ='consumers-wait-for-messages';
25+
26+
/**
27+
* Path to the value in the deployment config
28+
*/
29+
const CONFIG_PATH_QUEUE_CONSUMERS_WAIT_FOR_MESSAGES = 'queue/consumers_wait_for_messages';
30+
31+
/**
32+
* Default value
33+
*/
34+
const DEFULT_CONSUMERS_WAIT_FOR_MESSAGES = 1;
35+
36+
private $selectOptions = [0, 1];
37+
38+
/**
39+
* @inheritdoc
40+
*/
41+
public function getOptions()
42+
{
43+
return [
44+
new SelectConfigOption(
45+
self::INPUT_KEY_QUEUE_CONSUMERS_WAIT_FOR_MESSAGES,
46+
SelectConfigOption::FRONTEND_WIZARD_SELECT,
47+
$this->selectOptions,
48+
self::CONFIG_PATH_QUEUE_CONSUMERS_WAIT_FOR_MESSAGES,
49+
'Should consumers wait for message from the queue? 1 - Yes, 0 - No',
50+
self::DEFULT_CONSUMERS_WAIT_FOR_MESSAGES
51+
),
52+
];
53+
}
54+
55+
/**
56+
* @inheritdoc
57+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
58+
*/
59+
public function createConfig(array $data, DeploymentConfig $deploymentConfig)
60+
{
61+
$configData = new ConfigData(ConfigFilePool::APP_ENV);
62+
63+
if (!$this->isDataEmpty($data, self::INPUT_KEY_QUEUE_CONSUMERS_WAIT_FOR_MESSAGES)) {
64+
$configData->set(
65+
self::CONFIG_PATH_QUEUE_CONSUMERS_WAIT_FOR_MESSAGES,
66+
(int)$data[self::INPUT_KEY_QUEUE_CONSUMERS_WAIT_FOR_MESSAGES]
67+
);
68+
}
69+
70+
return[$configData];
71+
}
72+
73+
/**
74+
* @inheritdoc
75+
*/
76+
public function validate(array $options, DeploymentConfig $deploymentConfig)
77+
{
78+
$errors = [];
79+
80+
if (!$this->isDataEmpty($options, self::INPUT_KEY_QUEUE_CONSUMERS_WAIT_FOR_MESSAGES)
81+
&& !in_array($options[self::INPUT_KEY_QUEUE_CONSUMERS_WAIT_FOR_MESSAGES], $this->selectOptions)) {
82+
$errors[] = 'You can use only 1 or 0 for ' . self::INPUT_KEY_QUEUE_CONSUMERS_WAIT_FOR_MESSAGES . ' option';
83+
}
84+
85+
return $errors;
86+
}
87+
88+
/**
89+
* Check if data ($data) with key ($key) is empty
90+
*
91+
* @param array $data
92+
* @param string $key
93+
* @return bool
94+
*/
95+
private function isDataEmpty(array $data, $key)
96+
{
97+
if (isset($data[$key]) && $data[$key] !== '') {
98+
return false;
99+
}
100+
101+
return true;
102+
}
103+
}

lib/internal/Magento/Framework/MessageQueue/CallbackInvoker.php

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
use Magento\Framework\MessageQueue\PoisonPill\PoisonPillCompareInterface;
1010
use Magento\Framework\MessageQueue\PoisonPill\PoisonPillReadInterface;
11+
use Magento\Framework\App\DeploymentConfig;
1112

1213
/**
1314
* Class CallbackInvoker to invoke callbacks for consumer classes
@@ -29,16 +30,24 @@ class CallbackInvoker implements CallbackInvokerInterface
2930
*/
3031
private $poisonPillCompare;
3132

33+
/**
34+
* @var DeploymentConfig
35+
*/
36+
private $deploymentConfig;
37+
3238
/**
3339
* @param PoisonPillReadInterface $poisonPillRead
3440
* @param PoisonPillCompareInterface $poisonPillCompare
41+
* @param DeploymentConfig $deploymentConfig
3542
*/
3643
public function __construct(
3744
PoisonPillReadInterface $poisonPillRead,
38-
PoisonPillCompareInterface $poisonPillCompare
45+
PoisonPillCompareInterface $poisonPillCompare,
46+
DeploymentConfig $deploymentConfig
3947
) {
4048
$this->poisonPillRead = $poisonPillRead;
4149
$this->poisonPillCompare = $poisonPillCompare;
50+
$this->deploymentConfig = $deploymentConfig;
4251
}
4352

4453
/**
@@ -56,13 +65,29 @@ public function invoke(QueueInterface $queue, $maxNumberOfMessages, $callback)
5665
do {
5766
$message = $queue->dequeue();
5867
// phpcs:ignore Magento2.Functions.DiscouragedFunction
59-
} while ($message === null && (sleep(1) === 0));
68+
} while ($message === null && $this->isWaitingNextMessage() && (sleep(1) === 0));
69+
70+
if ($message === null) {
71+
break;
72+
}
73+
6074
if (false === $this->poisonPillCompare->isLatestVersion($this->poisonPillVersion)) {
6175
$queue->reject($message);
6276
// phpcs:ignore Magento2.Security.LanguageConstruct.ExitUsage
6377
exit(0);
6478
}
79+
6580
$callback($message);
6681
}
6782
}
83+
84+
/**
85+
* Checks if consumers should wait for message from the queue
86+
*
87+
* @return bool
88+
*/
89+
private function isWaitingNextMessage(): bool
90+
{
91+
return $this->deploymentConfig->get('queue/consumers_wait_for_messages', 1) === 1;
92+
}
6893
}

0 commit comments

Comments
 (0)