Octane bricht eine Annahme, auf die Sie sich verlassen
Jede Anfrage in einer normalen Laravel-Anwendung beginnt bei null. Octane hält die Anwendung zwischen Anfragen im Speicher - daher kommt die Geschwindigkeit, und daher kommen auch die Fehler.
Was PHP nachsichtig macht, ist dass es vergisst. Eine Anfrage kommt an, das Framework bootet, die Arbeit passiert, der Prozess endet, und jede unterwegs angelegte Variable verschwindet. Ein Speicherleck dauert Millisekunden. Eine statische Eigenschaft, die mit den Daten eines Nutzers verunreinigt wurde, ist weg, bevor der nächste Nutzer ankommt.
Octane nimmt das weg. Die Anwendung bootet einmal und bleibt im Speicher, bedient Anfrage um Anfrage im selben Prozess. Den Boot zu überspringen ist, wo die Geschwindigkeit herkommt – und das Vergessen war tragend, auf Weisen, über die die meisten Codebasen nie nachdenken mussten.
Was aufhört zu stimmen
Statische Eigenschaften bleiben bestehen. Ein statischer Cache, der anfragebezogen war, wird jetzt von jeder Anfrage geteilt, die dieser Worker bedient. Ist er nach Nutzer geschlüsselt, ist er ein Datenleck.
Singletons überleben die Anfrage, die sie erzeugt hat. Ein einmal aufgelöster und als Singleton registrierter Dienst hält, was er bei der ersten Auflösung eingefangen hat – im schlimmsten Fall eine Anfrage oder einen angemeldeten Nutzer.
Globale Werte bleiben bestehen. Alles, was in eine Superglobale oder eine globale Variable geschrieben wurde, überlebt.
Lecks summieren sich. Ein Array, das bei jeder Anfrage ein wenig wächst, war früher umsonst. Jetzt klettert der Speicher des Workers, bis ihn etwas neu startet.
Das Muster, das wirklich zubeißt
Es ist selten ein statisches Array. Es ist ein Dienst, der eine Abhängigkeit genommen hat, die er später hätte auflösen sollen:
// AppServiceProvider
$this->app->singleton(ReportBuilder::class, function ($app) {
return new ReportBuilder($app->make(Request::class));
});Als Singleton registriert, fängt das die erste Anfrage ein, die der Worker je bedient, und behält sie. Jede folgende Anfrage bekommt einen Builder, der die Eingaben von jemand anderem hält.
Unter normalem PHP ist das harmlos – der Prozess stirbt, das Singleton stirbt mit ihm, und niemand bemerkt je den schlummernden Fehler. Unter Octane ist es ein nutzerübergreifendes Datenleck, das sporadisch auftritt und aus einer Meldung heraus fast nicht reproduzierbar ist.
Die Behebung besteht darin, anfragebezogenen Zustand dann aufzulösen, wenn er gebraucht wird, und nicht dann, wenn der Dienst gebaut wird:
$this->app->singleton(ReportBuilder::class, fn () => new ReportBuilder());
// und in der Methode, die ihn braucht
public function build(Request $request): Report { ... }Dasselbe gilt für alles, was den angemeldeten Nutzer, den aktuellen Mandanten oder die Sprache beim Konstruieren einfängt. Auf einer mandantenfähigen Plattform wird daraus aus einer Peinlichkeit ein ernster Vorfall, denn die Grenze zwischen Mandanten ist genau das, was überschritten wird.
Sie finden, bevor die Produktion es tut
Lesen Sie zuerst die Service Provider. Jede singleton-Bindung und jede
bind-Bindung, deren Closure etwas Anfrageförmiges auflöst, ist ein Kandidat.
Suchen Sie dann nach statischen Eigenschaften, in die geschrieben wird statt nur aus ihnen zu lesen:
grep -rn "protected static \|private static \|public static " app/ | grep -v "function\|const"Die meisten werden legitim sein. Die, die Daten halten statt Konfiguration, sind die Liste zum Abarbeiten.
Laravel bietet Haken, um Zustand zwischen Anfragen zurückzusetzen, und Pakete registrieren ihre eigenen. Sie decken die Dienste des Frameworks gut ab und wissen nichts über Ihre.
Der Teil, der nicht von Korrektheit handelt
Selbst mit erledigtem Zustand hat ein langlebiger Prozess eine andere Betriebsform.
Speicher muss über Stunden beobachtet werden, nicht über Minuten – ein Leck von hundert Kilobyte pro Anfrage ist in einem Test unsichtbar und über Nacht tödlich. Worker brauchen eine maximale Anfragezahl, damit sie recycelt werden, bevor sie degradieren. Und ein Deployment muss sie neu starten, was dieselbe Lektion ist wie bei Queue-Workern: Ein langlebiger Prozess hält den Code, mit dem er gebootet hat, also erreicht ihn neuer Code erst, wenn ihn etwas zum Beenden bringt.
Lohnt es sich
Manchmal, und seltener als die Benchmarks nahelegen.
Der Boot ist das, was Octane entfernt, und eine Anfrage, deren Zeit in eine langsame Abfrage oder einen Aufruf bei Dritten geht, gewinnt gar nichts. Profilieren Sie zuerst einen echten Endpunkt. Ist der Framework-Boot ein nennenswerter Anteil Ihrer Antwortzeit, ist der Gewinn real; ist es die Datenbank, beheben Sie die Datenbank.
Und wenn die Anwendung Singletons hat, die anfragebezogenen Zustand einfangen, beheben Sie die unabhängig davon, wie Sie sich zu Octane entscheiden. Sie sind heute Fehler – nur Fehler, für die PHP still und leise eingesprungen ist.
Auf einer mandantenfähigen Plattform überschreitet ein eingefangener Mandant genau die Grenze, auf der das Produkt verkauft wird. Und ein Prozess, der den Code festhält, mit dem er gestartet ist, ist derselbe Bug wie ein Queue-Worker auf dem Release der Vorwoche. Deshalb muss ein Deployment beide neu starten.
Ob der Bootvorgang Sie tatsächlich etwas kostet, ist eine Messung, und damit fangen wir an.
