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/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/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; + } +}