Ir al contenido

Octane acelera Laravel rompiendo una suposición

Cada petición en una aplicación Laravel normal empieza de cero. Octane mantiene la aplicación en memoria entre peticiones, y de ahí viene la velocidad y también los fallos.

4 min de lectura

Lo que hace indulgente a PHP es que olvida. Llega una petición, arranca el framework, ocurre el trabajo, el proceso sale, y todas las variables creadas por el camino desaparecen. Una fuga de memoria dura milisegundos. Una propiedad estática contaminada con los datos de un usuario desaparece antes de que llegue el siguiente.

Octane quita eso. La aplicación arranca una vez y se queda en memoria, atendiendo petición tras petición en el mismo proceso. Saltarse el arranque es de donde viene la velocidad, y el olvido estaba sosteniendo cosas que la mayoría de las bases de código nunca ha tenido que pensar.

Qué deja de ser cierto

Las propiedades estáticas persisten. Una caché estática que era por petición la comparten ahora todas las peticiones que atiende el worker. Si está indexada por usuario, es una fuga de datos.

Los singletons sobreviven a la petición que los creó. Un servicio resuelto una vez y registrado como singleton se queda con lo que capturase en la primera resolución, incluidos, en el peor caso, una petición o un usuario autenticado.

Las globales persisten. Todo lo escrito en una superglobal o en una variable global sobrevive.

Las fugas se acumulan. Un array que crecía un poco en cada petición era gratis. Ahora la memoria del worker sube hasta que algo lo reinicia.

El patrón que muerde de verdad

Rara vez es un array estático. Es un servicio que tomó una dependencia que debería haber resuelto más tarde:

// AppServiceProvider
$this->app->singleton(ReportBuilder::class, function ($app) {
    return new ReportBuilder($app->make(Request::class));
});

Registrado como singleton, esto captura la primera petición que atiende el worker y se la queda. Cada petición posterior recibe un builder que sostiene la entrada de otra persona.

Con PHP normal esto es inofensivo: el proceso muere, el singleton muere con él, y nadie llega a notar el fallo latente. Con Octane es una fuga de datos entre usuarios que aparece de forma intermitente y que es casi imposible de reproducir a partir de un reporte.

El arreglo es resolver el estado propio de la petición cuando hace falta, en lugar de cuando se construye el servicio:

$this->app->singleton(ReportBuilder::class, fn () => new ReportBuilder());
 
// y dentro del método que lo necesita
public function build(Request $request): Report { ... }

Lo mismo vale para cualquier cosa que capture el usuario autenticado, el inquilino actual o el idioma en el momento de construirse. En una plataforma multiinquilino esto pasa de vergüenza a incidente serio, porque la frontera entre inquilinos es exactamente lo que se está cruzando.

Encontrarlos antes que producción

Lea primero los service providers. Cada enlace singleton, y cada bind cuyo closure resuelva algo con forma de petición, es candidato.

Después busque propiedades estáticas en las que se escribe y no solo se lee:

grep -rn "protected static \|private static \|public static " app/ | grep -v "function\|const"

La mayoría serán legítimas. Las que sostienen datos en lugar de configuración son la lista que hay que ir resolviendo.

Laravel ofrece ganchos para reiniciar estado entre peticiones, y los paquetes registran los suyos. Cubren bien los servicios del propio framework y no saben nada de los suyos.

La parte que no va de corrección

Incluso con el estado resuelto, un proceso de larga ejecución tiene otra forma operativa.

La memoria hay que vigilarla durante horas, no durante minutos: una fuga de cien kilobytes por petición es invisible en una prueba y mortal de madrugada. Los workers necesitan un máximo de peticiones para reciclarse antes de degradarse. Y un despliegue tiene que reiniciarlos, que es la misma lección que con los workers de cola: un proceso de vida larga sostiene el código con el que arrancó, así que el código nuevo no le llega hasta que algo le hace salir.

¿Merece la pena?

A veces, y menos a menudo de lo que sugieren los benchmarks.

El arranque es lo que Octane quita, y una petición cuyo tiempo se va en una consulta lenta o en una llamada a un tercero no gana absolutamente nada. Perfile primero un endpoint real. Si el arranque del framework es una parte significativa de su tiempo de respuesta, la ganancia es real; si lo es la base de datos, arregle la base de datos.

Y si la aplicación tiene singletons que capturan estado de petición, arréglelos decida lo que decida sobre Octane. Son fallos hoy: solo que PHP lleva tiempo tapándolos en silencio.

En una plataforma multiinquilino, un inquilino capturado cruza la única frontera sobre la que se vende el producto. Y un proceso que retiene el código con el que arrancó es el mismo fallo que un worker de colas con la versión de la semana pasada. Por eso un despliegue tiene que reiniciar los dos.

Si el arranque le está costando algo de verdad es una medición, y es lo primero que hacemos.

Preguntas relacionadas

¿Es seguro poner encima una aplicación existente?
No sin leer antes la aplicación. Es seguro para la mayor parte del código y silenciosamente incorrecto para un conjunto concreto de patrones: cualquier cosa que sostenga estado propio de una petición sobre algo que vive más que una petición. El fallo es que los datos de un usuario aparezcan para otro, que es la peor categoría de fallo que encontrar en producción.
¿Cuánto más rápido es en realidad?
Depende por completo de en qué gastan el tiempo sus peticiones. Una aplicación cuyo tiempo se va en arrancar el framework gana muchísimo; una cuyo tiempo se va en una consulta lenta no gana nada, porque la consulta sigue tardando lo mismo. Mida dónde están los milisegundos antes de suponer que esto es el arreglo.
¿Cuál es la forma más segura de adoptarlo?
Arregle primero los patrones inseguros, todavía en ejecución normal: son fallos en espera de cualquier manera. Después actívelo en un entorno que no sea producción con una prueba de remojo larga, vigile la memoria durante horas y no minutos, y despliéguelo detrás de algo que pueda apagar deprisa.
¿Podemos obtener el beneficio sin él?
A menudo. Buena parte de lo que se espera de Octane está disponible cacheando configuración y rutas, con caché de opcode y precarga, y arreglando lo que de verdad es lento. Eso es de menor riesgo y merece la pena agotarlo primero.

← Volver a todos los artículos

Llamar+1 848 272 7583WhatsApp+90 850 308 5436Correoinfo@codefacture.comPágina de contacto