10000 Fixes #43866 configurable stop signals for messenger · symfony/symfony@8aaa16d · GitHub
[go: up one dir, main page]

Skip to content

Commit 8aaa16d

Browse files
committed
Fixes #43866 configurable stop signals for messenger
1 parent aa51dc7 commit 8aaa16d

File tree

8 files changed

+179
-27
lines changed

8 files changed

+179
-27
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,18 @@ private function addMessengerSection(ArrayNodeDefinition $rootNode, callable $en
13071307
->then(static function (array $v): void { throw new InvalidConfigurationException(sprintf('The specified default bus "%s" is not configured. Available buses are "%s".', $v['default_bus'], implode('", "', array_keys($v['buses'])))); })
13081308
->end()
13091309
->children()
1310+
->arrayNode('stop_signals')
1311+
->beforeNormalization()
1312+
->ifString()->then(static fn (string $value): int => constant($value))
1313+
->castToArray()
1314+
->end()
1315+
->validate()
1316+
->ifFalse(static fn ($v): bool => function_exists('pcntl_signal'))
1317+
->then(static function (): void { throw new InvalidConfigurationException('You must install pcntl extension in order to use stop_signals.'); })
1318+
->ifNull()->then(static function (): void { throw new InvalidConfigurationException('Invalid signal passed to stop_signals'); })
1319+
->end()
1320+
->scalarPrototype()->end()
1321+
13101322
->arrayNode('routing')
13111323
->normalizeKeys(false)
13121324
->useAttributeAsKey('message_class')

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1908,6 +1908,8 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder
19081908
$config['default_bus'] = key($config['buses']);
19091909
}
19101910

1911+
$container->getDefinition('stop_worker_on_sigterm_signal_listener')->setArgument('$signals', $config['stop_signals'] ?? \SIGTERM);
1912+
19111913
$defaultMiddleware = [
19121914
'before' => [
19131915
['id' => 'add_bus_name_stamp_middleware'],

src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Symfony\Component\Messenger\EventListener\SendFailedMessageToFailureTransportListener;
2424
use Symfony\Component\Messenger\EventListener\StopWorkerOnCustomStopExceptionListener;
2525
use Symfony\Component\Messenger\EventListener\StopWorkerOnRestartSignalListener;
26+
use Symfony\Component\Messenger\EventListener\StopWorkerOnSignalListener;
2627
use Symfony\Component\Messenger\EventListener\StopWorkerOnSigtermSignalListener;
2728
use Symfony\Component\Messenger\Middleware\AddBusNameStampMiddleware;
2829
use Symfony\Component\Messenger\Middleware\DispatchAfterCurrentBusMiddleware;
@@ -192,8 +193,9 @@
192193
->tag('kernel.event_subscriber')
193194
->tag('monolog.logger', ['channel' => 'messenger'])
194195

195-
->set('messenger.listener.stop_worker_on_sigterm_signal_listener', StopWorkerOnSigtermSignalListener::class)
196+
->set('messenger.listener.stop_worker_on_signal_listener', StopWorkerOnSignalListener::class)
196197
->args([
198+
abstract_arg('signals'),
197199
service('logger')->ignoreOnInvalid(),
198200
])
199201
->tag('kernel.event_subscriber')

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,91 @@ public function testLockMergeConfigs()
264264
);
265265
}
266266

267+
public function testMessengerWithoutPcntl()
268+
{
269+
$this->expectException(InvalidConfigurationException::class);
270+
$this->expectExceptionMessage('You must install pcntl extension in order to use stop_signals.');
271+
$processor = new Processor();
272+
$configuration = new Configuration(true);
273+
274+
$processor->processConfiguration($configuration, [
275+
'framework' => [
276+
'messenger' => [
277+
'stop_signals' => \SIGTERM
278+
],
279+
],
280+
]);
281+
}
282+
283+
public function testMessengerWithoutPcntlWithoutStopSignals()
284+
{
285+
$processor = new Processor();
286+
$configuration = new Configuration(true);
287+
288+
$processor->processConfiguration($configuration, [
289+
'framework' => [
290+
'messenger' => [
291+
],
292+
],
293+
]);
294+
295+
$this->expectNotToPerformAssertions();
296+
}
297+
298+
public function testMessengerWithInvalidSignal()
299+
{
300+
$this->expectException(InvalidConfigurationException::class);
301+
$this->expectExceptionMessage('Invalid signal passed to stop_signals');
302+
$processor = new Processor();
303+
$configuration = new Configuration(true);
304+
305+
$processor->processConfiguration($configuration, [
306+
'framework' => [
307+
'messenger' => [
308+
'stop_signals' => \SIG_IGN
309+
],
310+
],
311+
]);
312+
}
313+
314+
public function testMessengerWithMultipleStopSignals()
315+
{
316+
$processor = new Processor();
317+
$configuration = new Configuration(true);
318+
319+
$config = $processor->processConfiguration($configuration, [
320+
'framework' => [
321+
'messenger' => [
322+
'stop_signals' => [\SIGTERM, \SIGQUIT]
323+
],
324+
],
325+
]);
326+
327+
static::assertEquals(
328+
[\SIGTERM, \SIGQUIT],
329+
$config['messenger']['stop_signals']
330+
);
331+
}
332+
333+
public function testMessengerWithStringStopSignal()
334+
{
335+
$processor = new Processor();
336+
$configuration = new Configuration(true);
337+
338+
$config = $processor->processConfiguration($configuration, [
339+
'framework' => [
340+
'messenger' => [
341+
'stop_signals' => ['SIGTERM', 'SIGQUIT']
342+
],
343+
],
344+
]);
345+
346+
static::assertEquals(
347+
[\SIGTERM, \SIGQUIT],
348+
$config['messenger']['stop_signals']
349+
);
350+
}
351+
267352
public function testItShowANiceMessageIfTwoMessengerBusesAreConfiguredButNoDefaultBus()
268353
{
269354
$expectedMessage = 'You must specify the "default_bus" if you define more than one bus.';

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\FooMessage;
55

66
$container->loadFromExtension('framework', [
7+
'stop_signals'=> \SIGTERM,
78
'messenger' => [
89
'routing' => [
910
FooMessage::class => ['sender.bar', 'sender.biz'],

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
framework:
22
messenger:
3+
stop_signals: SIGTERM
34
routing:
45
'Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\FooMessage': ['sender.bar', 'sender.biz']
56
'Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Messenger\BarMessage': 'sender.foo'
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\EventListener;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16+
use Symfony\Component\Messenger\Event\WorkerStartedEvent;
17+
18+
/**
19+
* @author Tobias Schultze <http://tobion.de>
20+
*/
21+
class StopWorkerOnSignalListener implements EventSubscriberInterface
22+
{
23+
private array $signals = [];
24+
private ?LoggerInterface $logger;
25+
26+
public function __construct(int|array $signals, LoggerInterface $logger = null)
27+
{
28+
foreach ((array)$signals as $signal) {
29+
$this->signals[$signal] = $this->strsignal($signal);
30+
}
31+
32+
$this->logger = $logger;
33+
}
34+
35+
private function strsignal(int $signal): string
36+
{
37+
foreach (get_defined_constants(true)['pcntl'] as $name => $num) {
38+
// the _ is to ignore SIG_IGN and SIG_DFL and SIG_ERR and SIG_BLOCK and SIG_UNBLOCK and SIG_SETMARK, and maybe more, who knows
39+
if ($num === $signal && 'SIG' === substr($name, 0, 3) && '_' !== $name[3]) {
40+
return $name;
41+
}
42+
}
43+
44+
throw new \InvalidArgumentException(sprintf('The signal "%d" is not valid.', $signal));
45+
}
46+
47+
public function onWorkerStarted(WorkerStartedEvent $event): void
48+
{
49+
foreach ($this->signals as $signal => $signalName) {
50+
\pcntl_signal($signal, function () use ($event, $signalName) {
51+
$this->logger?->info(
52+
\sprintf('Received %s signal.', $signalName),
53+
['transport_names' => $event->getWorker()->getMetadata()->getTransportNames()]
54+
);
55+
56+
$event->getWorker()->stop();
57+
});
58+
59+
}
60+
}
61+
62+
public static function getSubscribedEvents(): array
63+
{
64+
if (!\function_exists('pcntl_signal')) {
65+
return [];
66+
}
67+
68+
return [
69+
WorkerStartedEvent::class => ['onWorkerStarted', 100],
70+
];
71+
}
72+
}

src/Symfony/Component/Messenger/EventListener/StopWorkerOnSigtermSignalListener.php

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,15 @@
1212
namespace Symfony\Component\Messenger\EventListener;
1313

1414
use Psr\Log\LoggerInterface;
15-
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16-
use Symfony\Component\Messenger\Event\WorkerStartedEvent;
1715

1816
/**
1917
* @author Tobias Schultze <http://tobion.de>
18< E7B0 span class="diff-text-marker">+
* @deprecated Use StopWorkerOnSignalListener instead.
2019
*/
21-
class StopWorkerOnSigtermSignalListener implements EventSubscriberInterface
20+
class StopWorkerOnSigtermSignalListener extends StopWorkerOnSignalListener
2221
{
23-
private ?LoggerInterface $logger;
24-
2522
public function __construct(LoggerInterface $logger = null)
2623
{
27-
$this->logger = $logger;
28-
}
29-
30-
public function onWorkerStarted(WorkerStartedEvent $event): void
31-
{
32-
pcntl_signal(\SIGTERM, function () use ($event) {
33-
$this->logger?->info('Received SIGTERM signal.', ['transport_names' => $event->getWorker()->getMetadata()->getTransportNames()]);
34-
35-
$event->getWorker()->stop();
36-
});
37-
}
38-
39-
public static function getSubscribedEvents(): array
40-
{
41-
if (!\function_exists('pcntl_signal')) {
42-
return [];
43-
}
44-
45-
return [
46-
WorkerStartedEvent::class => ['onWorkerStarted', 100],
47-
];
24+
parent::__construct(\SIGTERM, $logger);
4825
}
4926
}

0 commit comments

Comments
 (0)
0