diff --git a/src/EventDispatcher/EventSubscribersCollection.php b/src/EventDispatcher/EventSubscribersCollection.php index 4c6133d..212389d 100644 --- a/src/EventDispatcher/EventSubscribersCollection.php +++ b/src/EventDispatcher/EventSubscribersCollection.php @@ -8,9 +8,11 @@ use RuntimeException; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use WonderNetwork\SlimKernel\ServiceFactory; +use WonderNetwork\SlimKernel\ServicesBuilder; use function DI\decorate; -final readonly class EventSubscribersCollection { +final readonly class EventSubscribersCollection implements ServiceFactory { public static function start(): self { return new self([]); } @@ -21,6 +23,10 @@ public static function start(): self { public function __construct(private array $subscribers) { } + public function __invoke(ServicesBuilder $builder): iterable { + yield from $this->register(); + } + /** * @param class-string ...$subscribers */ diff --git a/src/Messenger/WorkerMemoryUsageSubscriber.php b/src/Messenger/WorkerMemoryUsageSubscriber.php new file mode 100644 index 0000000..6e2513a --- /dev/null +++ b/src/Messenger/WorkerMemoryUsageSubscriber.php @@ -0,0 +1,67 @@ + + */ + public static function getSubscribedEvents(): iterable { + return [ + WorkerRunningEvent::class => 'onWorkerRunning', + ]; + } + + public function __construct( + private readonly LoggerInterface $logger, + private readonly int $cutoff = 1024, + System $system = null, + ) { + $this->system = $system ?? new RealSystem(); + $this->memoryUsage = $this->system->memoryUsage(); + } + + public function onWorkerRunning(): void { + $currentUsage = $this->system->memoryUsage(); + + $difference = abs($this->memoryUsage - $currentUsage); + + if ($difference < $this->cutoff) { + return; + } + + $this->logger->debug( + "Memory usage changed: {current} ({sign}{difference})", + [ + 'current' => $this->formatBytes($currentUsage), + 'sign' => ($this->memoryUsage > $currentUsage) ? "-" : "+", + 'difference' => $this->formatBytes($difference), + ], + ); + + $this->memoryUsage = $currentUsage; + } + + private function formatBytes(int $bytes): string { + $units = ['B', 'KB', 'MB', 'GB', 'TB']; + + $bytes = max($bytes, 0); + $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); + $pow = (int) min($pow, count($units) - 1); + + $bytes /= 1024 ** $pow; + + return sprintf("%s %s", round($bytes, 2), $units[$pow]); + } +} diff --git a/src/Supervisor/GenerateSupervisorConfigCommand.php b/src/Supervisor/GenerateSupervisorConfigCommand.php index fc923c9..61f1e2f 100644 --- a/src/Supervisor/GenerateSupervisorConfigCommand.php +++ b/src/Supervisor/GenerateSupervisorConfigCommand.php @@ -106,6 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int command=$fullCommand process_name=$processName numprocs=$concurrency + startretries=$program->startretries user=www-data autostart=true autorestart=true diff --git a/src/Supervisor/SupervisorProgram.php b/src/Supervisor/SupervisorProgram.php index 2681a64..0298dfa 100644 --- a/src/Supervisor/SupervisorProgram.php +++ b/src/Supervisor/SupervisorProgram.php @@ -10,16 +10,19 @@ public static function single(string $name, string $command): self { name: $name, command: $command, concurrency: 1, + startretries: 15, ); } /** * @param positive-int $concurrency + * @param positive-int $startretries */ public function __construct( public string $name, public string $command, public int $concurrency, + public int $startretries, ) { } } diff --git a/src/System/RealSystem.php b/src/System/RealSystem.php new file mode 100644 index 0000000..839b087 --- /dev/null +++ b/src/System/RealSystem.php @@ -0,0 +1,11 @@ + autowire(), - ...EventSubscribersCollection::start() - ->add(ConsoleHandlerEventSubscriber::class) - ->register(), ], ) + ->register( + EventSubscribersCollection::start() + ->add(ConsoleHandlerEventSubscriber::class), + ) ->register( new SymfonyConsoleServiceFactory( path: '/src/*Command.php', diff --git a/tests/Messenger/WorkerMemoryUsageSubscriberTest.php b/tests/Messenger/WorkerMemoryUsageSubscriberTest.php new file mode 100644 index 0000000..677ebd5 --- /dev/null +++ b/tests/Messenger/WorkerMemoryUsageSubscriberTest.php @@ -0,0 +1,46 @@ +pushHandler($buffer); + + $system = new FakeSystem(); + + $sut = new WorkerMemoryUsageSubscriber( + logger: $logger, + cutoff: 10 * 1024, + system: $system, + ); + $sut->onWorkerRunning(); + self::assertEmpty($buffer->getRecords()); + + $system->setMemoryUsage(10 * 1024 - 1); + $sut->onWorkerRunning(); + self::assertCount(0, $buffer->getRecords()); + + $system->setMemoryUsage(10 * 1024); + $sut->onWorkerRunning(); + self::assertCount(1, $buffer->getRecords()); + self::assertSame([ + 'current' => '10 KB', + 'sign' => '+', + 'difference' => '10 KB', + ], $buffer->getRecords()[0]->context); + + $sut->onWorkerRunning(); + $system->setMemoryUsage(12 * 1024); + $sut->onWorkerRunning(); + self::assertCount(1, $buffer->getRecords()); + } +} diff --git a/tests/Supervisor/GenerateSupervisorConfigCommandTest.php b/tests/Supervisor/GenerateSupervisorConfigCommandTest.php index 45f46fb..57cc5db 100644 --- a/tests/Supervisor/GenerateSupervisorConfigCommandTest.php +++ b/tests/Supervisor/GenerateSupervisorConfigCommandTest.php @@ -17,6 +17,7 @@ public function testGeneratesConfig(): void { name: 'jobs', command: 'bin/worker jobs', concurrency: 3, + startretries: 10, ), ); $configDir = __DIR__.'/../Resources/Supervisor'; @@ -47,6 +48,7 @@ public function testGeneratesConfig(): void { command=/var/app/current/bin/worker jobs process_name=%(program_name)s_%(process_num)02d numprocs=3 + startretries=10 user=www-data autostart=true autorestart=true @@ -58,6 +60,7 @@ public function testGeneratesConfig(): void { command=/var/app/current/bin/worker async process_name=worker numprocs=1 + startretries=15 user=www-data autostart=true autorestart=true @@ -84,6 +87,7 @@ public function testGeneratesConfigWithStdio(): void { name: 'jobs', command: 'bin/worker jobs', concurrency: 3, + startretries: 10, ), ); $configDir = __DIR__.'/../Resources/Supervisor'; @@ -105,6 +109,7 @@ public function testGeneratesConfigWithStdio(): void { command=/var/app/current/bin/worker jobs process_name=%(program_name)s_%(process_num)02d numprocs=3 + startretries=10 user=www-data autostart=true autorestart=true @@ -116,6 +121,7 @@ public function testGeneratesConfigWithStdio(): void { command=/var/app/current/bin/worker async process_name=worker numprocs=1 + startretries=15 user=www-data autostart=true autorestart=true diff --git a/tests/System/FakeSystem.php b/tests/System/FakeSystem.php new file mode 100644 index 0000000..3562efd --- /dev/null +++ b/tests/System/FakeSystem.php @@ -0,0 +1,17 @@ +memoryUsage = $memoryUsage; + } + + public function memoryUsage(): int { + return $this->memoryUsage; + } +}