Звичайний PHP-FPM: на кожен запит фреймворк завантажується з нуля і після відповіді все знищується. Будь-який стан живе рівно один запит - це «безкоштовна» ізоляція.
Octane (FrankenPHP, Swoole, RoadRunner) завантажує застосунок один раз і обробляє ним тисячі запитів. Звідси приріст швидкості - і нові класи помилок: стан переживає запит.
Що ламається:
1. Сінглтони, що захопили дані запиту:
// погано: сінглтон створено при першому запиті - і він назавжди тримає ТОГО користувача
$this->app->singleton(CartService::class, fn ($app) => new CartService($app['request']->user()));
Наступні запити інших користувачів отримають кошик першого. Рішення - не впроваджувати запит, користувача, конфігурацію, що змінюється, у конструктори сінглтонів; передавати їх у методи; або використовувати scoped() - екземпляр на запит.
2. Статичні властивості й статичні кеші (static $cache = []) накопичують дані між запитами - витоки пам'яті й даних між користувачами.
3. Впровадження контейнера чи запиту в сінглтон - сінглтон отримає контейнер першого запиту. Документація Octane радить замикання-резолвери (fn () => app('request')) замість прямого впровадження.
4. Витоки пам'яті: масиви, що лише ростуть (логування в статичний масив, реєстрація слухачів на кожен запит). Octane перезапускає воркери після N запитів (--max-requests), але це страховка, а не рішення.
5. З'єднання з базою й сторонніми сервісами живуть довго - потрібна обробка розірваних з'єднань.
Що варто переглянути в коді:
- усі
singleton()у сервіс-провайдерах - чи не тримають вони стан запиту; - статичні змінні в класах застосунку й пакетів;
- пакети, не сумісні з Octane (зберігають стан у статичних властивостях);
- код, що покладається на «чистий» старт (глобальні змінні,
define).
Нові можливості:
- кешування на рівні воркера (
Octane::table(), кеш у пам'яті) для дуже гарячих даних; - паралельні задачі (
Octane::concurrently()на Swoole) чиConcurrency::run(); - фонові «тікери» на Swoole.
Тести: логіка, що працює в звичайних тестах, може ламатися лише під Octane - варто запускати навантажувальні тести з кількома користувачами й перевіряти ізоляцію даних.
Чи потрібен Octane: якщо основний час відповіді - запити до бази й сторонні API, Octane дасть менше, ніж оптимізація запитів. Він найкорисніший, коли значну частину часу займає завантаження фреймворку.
Докладніше в документації: Laravel Octane: впровадження залежностей