diff --git a/composer.json b/composer.json index f289993..91fbd05 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ ], "require": { "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/log": "^1.1 || ^2.0 || ^3.0", "symfony/event-dispatcher": "^5.4.26 || ^6.4.0 || ^7.0.0 || ^8.0.0" }, "require-dev": { @@ -33,6 +34,9 @@ "symfony/var-dumper": "^5.4.29 || ^6.4.0 || ^7.0.0 || ^8.0.0", "wnx/commonmark-markdown-renderer": "^1.6" }, + "suggest": { + "ext-pcntl": "Required to stop the worker gracefully on SIGTERM" + }, "config": { "preferred-install": { "*": "dist" diff --git a/composer.lock b/composer.lock index 70b1b9c..ca3b884 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "293e4cc7b82100df5338843829efec9a", + "content-hash": "1ba9db2e4b9681a4e4fc4e04047375ef", "packages": [ { "name": "psr/event-dispatcher", @@ -56,6 +56,56 @@ }, "time": "2019-01-08T18:20:26+00:00" }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v3.7.1", @@ -3100,56 +3150,6 @@ }, "time": "2021-11-05T16:47:00+00:00" }, - { - "name": "psr/log", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" - }, - "time": "2024-09-11T13:17:53+00:00" - }, { "name": "sanmai/di-container", "version": "0.1.23", diff --git a/src/Listener/StopWorkerOnMemoryLimitListener.php b/src/Listener/StopWorkerOnMemoryLimitListener.php index 0e8644e..bbf37db 100644 --- a/src/Listener/StopWorkerOnMemoryLimitListener.php +++ b/src/Listener/StopWorkerOnMemoryLimitListener.php @@ -28,7 +28,7 @@ public function onWorkerRunning(WorkerRunningEvent $event): void } $this->logger?->info( - 'Worker stopped due to memory limit of {limit} bytes exceeded ({memory} bytes used)', + 'Worker stopped due to memory limit of {limit} exceeded ({memory} used)', ['limit' => $this->memoryLimit->formatted(), 'memory' => $usedMemory->formatted()], ); diff --git a/src/Listener/StopWorkerOnSigtermSignalListener.php b/src/Listener/StopWorkerOnSigtermSignalListener.php index 9abdb9a..2ca5147 100644 --- a/src/Listener/StopWorkerOnSigtermSignalListener.php +++ b/src/Listener/StopWorkerOnSigtermSignalListener.php @@ -9,6 +9,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function function_exists; +use function pcntl_async_signals; use function pcntl_signal; use const SIGTERM; @@ -22,6 +23,8 @@ public function __construct( public function onWorkerStarted(WorkerStartedEvent $event): void { + pcntl_async_signals(true); + pcntl_signal(SIGTERM, function () use ($event): void { $this->logger?->info('Received SIGTERM signal.'); $event->worker->stop(); diff --git a/src/Worker.php b/src/Worker.php index 04d81d2..11a6ccc 100644 --- a/src/Worker.php +++ b/src/Worker.php @@ -6,7 +6,7 @@ interface Worker { - /** @param 0|positive-int $sleepTimer sleepTimer in microseconds */ + /** @param 0|positive-int $sleepTimer sleepTimer in milliseconds */ public function run(int $sleepTimer = 1000): void; public function stop(): void; diff --git a/tests/Unit/Listener/StopWorkerOnSigtermSignalListenerTest.php b/tests/Unit/Listener/StopWorkerOnSigtermSignalListenerTest.php new file mode 100644 index 0000000..973406d --- /dev/null +++ b/tests/Unit/Listener/StopWorkerOnSigtermSignalListenerTest.php @@ -0,0 +1,66 @@ +createMock(LoggerInterface::class); + $logger + ->expects($this->once()) + ->method('info') + ->with('Received SIGTERM signal.'); + + $eventDispatcher = new EventDispatcher(); + $eventDispatcher->addSubscriber(new StopWorkerOnSigtermSignalListener($logger)); + + $calls = 0; + + $worker = new DefaultWorker( + static function (callable $stop) use (&$calls): void { + $calls++; + + if ($calls === 1) { + posix_kill((int)getmypid(), SIGTERM); + } + + if ($calls < 5) { + return; + } + + $stop(); + }, + $eventDispatcher, + ); + + $worker->run(0); + + self::assertSame(1, $calls); + } +}