<? phpukraine СПІВБЕСІДИ
Пошук по платформі
LARAVEL · SENIOR

Що змінює Octane і які пастки він приносить?

Octane тримає застосунок у памʼяті між запитами через Swoole, FrankenPHP або RoadRunner: бутстрап відбувається один раз, але стан більше не скидається сам.

Чому під Octane один користувач бачить дані іншого?
Чим scoped відрізняється від singleton під Octane?
Що треба перевірити в застосунку перед переходом на Octane?
Octane Swoole стан між запитами

Класичний PHP-FPM створює застосунок з нуля на кожен запит: автозавантаження, провайдери, конфігурація, роутинг. Octane завантажує все один раз у воркер на Swoole, FrankenPHP чи RoadRunner і далі обробляє запити тим самим процесом. Звідси виграш: бутстрап Laravel зникає з кожного запиту. Сам PHP-код не стає швидшим.

Ціна — стан, який більше не помирає разом із запитом. Три джерела проблем: singleton у контейнері, статичні властивості класів і глобальні змінні чи memoization у хелперах. Синглтон, який отримав Request у конструкторі, на другому запиті тримає Request першого користувача. Статичний кеш, який раніше жив 50 мс, тепер живе добу й росте.

Octane сам скидає те, про що знає: auth, session, database connections, cache resolvers, translator. Про ваші класи він не знає. Тому перед міграцією потрібен аудит привʼязок: усе, що тримає стан запиту, переводиться на scoped, статичні кеші отримують очищення на RequestTerminated, а Request передається в методи, а не в конструктори довгоживучих сервісів.

Перед переходом варто виміряти, де йде час. Якщо запит триває 500 мс через SQL і зовнішні API, економія 30 мс бутстрапу непомітна, а ризики витоків реальні. Octane виправданий на легких ендпоінтах з великим RPS, де бутстрап є значною частиною часу відповіді.

// НЕБЕЗПЕЧНО під Octane: Request живе в синглтоні між запитами
final class CurrentTenant
{
    private ?Tenant $tenant = null;

    public function __construct(private Request $request) {}

    public function get(): Tenant
    {
        return $this->tenant ??= Tenant::whereHost($this->request->getHost())->firstOrFail();
    }
}
$this->app->singleton(CurrentTenant::class); // другий користувач побачить tenant першого

// БЕЗПЕЧНО: scoped скидається після кожного запиту
$this->app->scoped(CurrentTenant::class);

// Або взагалі не тримати Request у стані: брати його в момент виклику
final class CurrentTenant
{
    public function get(Request $request): Tenant { /* ... */ }
}

// Прихований витік: статичний кеш росте з кожним запитом
final class Pricing
{
    private static array $cache = [];   // ніколи не очищається під Octane
}

// Скидання власного стану між запитами
Octane::tick('flush-pricing', fn () => Pricing::flush())->seconds(60);
Event::listen(RequestTerminated::class, fn () => Pricing::flush());
Що виграш іде саме з відсутності бутстрапу фреймворку на кожен запит, а не з магічного прискорення PHP-коду.
Що singleton тепер живе між запитами, тому синглтон, який зберігає Request, користувача чи будь-які дані запиту, протікає між користувачами.
Що є три джерела витоків стану: singleton у контейнері, статичні властивості класів і глобальні змінні або memoization у хелперах.
Що Octane сам скидає частину стану (auth, session, database, cache resolvers), але не знає про ваші класи.
Практику: замінити singleton на scoped там, де є стан запиту, перевіряти memory через --max-requests, тримати ліміт памʼяті, і памʼятати, що інʼєкція Request у конструктор синглтона це антипатерн.
Казати, що Octane компілює PHP чи робить код швидшим: він лише прибирає повторний бутстрап.
Інжектити Request або Auth у конструктор singleton-сервісу: під FPM це працювало, під Octane дає чужі дані.
Не помічати статичні кеші в пакетах: static::$resolved, memoized конфіги, реєстри, які ростуть з кожним запитом.
Переходити на Octane без бенчмарка власного застосунку: якщо час іде на SQL і зовнішні API, бутстрап це 5 % і виграшу майже немає.
Забувати про конкурентність Swoole: блокуючий sleep або довгий запит тримає воркер, а коррутини змінюють семантику деяких розширень.
ПОРАДА

Обовʼязково згадайте, що синглтони, які приймають Request, треба переписати на scoped, інакше дані одного користувача побачить інший. І назвіть FrankenPHP як актуальний варіант поруч зі Swoole та RoadRunner.

оновлено 3 вересня 2026 · ліцензія CC-BY-SA-4.0 Знайшли неточність? Напишіть →
ПЕРЕВІРТЕ СЕБЕ

Виграш у швидкості з бутстрапу, ціна у тому, що синглтони і статичний стан живуть між запитами.