Cómo tratar con procesos largos en PHP

Antes de meternos en los detalles distingamos dos escenarios:

  1. PHP dentro del contexto de un WebServer
  2. PHP como lenguaje de scripting de algún proceso off-line.

Cuando se trata del segundo caso, es posible que existan oportunidades de mejorar el tiempo que insume el proceso, pero, en la gran mayoría de los casos, no pasará de ser una pequeña molestia si no se consigue este objetivo.

Cuando estamos en el contexto de WebServer, la molestia puede ser tal que haga que nuestros visitantes abandonen el sitio (Para no regresar jamás).

Por otro lado, es de suponer que en un ambiente web la concurrencia es alta (Es decir, un script PHP que está ejecutando está compitiendo contra muchos otros similares a él por el uso de los recursos), con lo cual, el propio sistema suele tomarse algunos recaudos para evitar que muchos inocentes sufran por los desmanes de unos pocos alborotadores :).

Uno de los límites que se nos impone (desde el archivo php.ini) es el tiempo máximo de ejecución (Que normalmente está en los 30 segundos).

Pero eso es lo que el servidor soporta antes de determinar que nuestro script tarda demasiado… los usuarios reales tienen mucha menor tolerancia (Hay estudios que afirman que una demora de más de 4 segundos hace que el 75% de los visitantes pierdan la paciencia…).

Entonces… ¿cómo atacar este problema?

Como diría Zun Tzu, la mejor manera de salir vivo de una batalla es no entrar nunca (No sé si eso es exactamente lo que dijo, pero si no, seguro que pensaba algo parecido :)).

Llevado al mundo de la programación web, la primera pregunta que debe hacerse es: ¿es realmente necesario ejecutar este proceso consumidor de tiempo?. Aunque parezca una trivialidad, en muchas ocasiones la respuesta es un rotundo NO, y simplemente se hace por razones que pueden haber sido válidas tiempo atrás pero ya no lo son más.

La siguiente pregunta (Asumiendo que la respuesta a la primera haya sido SI) es: ¿es realmente necesario ejecutar este proceso ahora mismo?. Esta es una pregunta un poco más sutil, pero sumamente importante. Para responderla hay que pensar mucho sobre lo que significa «ahora mismo»: el momento en que se está ejecutando PHP en un WebServer es cuando se ha recibido una petición de un usuario y se está intentando generar una respuesta.

Aquí nos metemos un poco en temas de psicología más que de programación, pero vale la pena intentarlo: muchas veces un usuario preferirá una respuesta rápida y casi correcta (o que parezca correcta) antes que una respuesta perfecta pero retrasada.

Se puede pensar en esto como en un truco de magia (un acto de ilusionismo si se quiere), donde es más importante lo que se ve que lo que efectivamente sucede.

Un típico ejemplo de estos problemas los constituye el recálculo de contadores. Tomo a Facebook como ejemplo, si te fijás en los números que indican la cantidad de actualizaciones no leídas podrás notar que rara vez es correcto… sin embargo, ¿cuál es el riesgo de tener un número ligeramente diferente del real? ¿estarías dispuesto a esperar más para tener la tranquilidad de que el número es correcto?

En la mayoría de los casos, con que eventualmente los números cierren no habrá mayores problemas, lo cual da lugar a un esquema de solución muy utilizado: diferir la ejecución de procesos largos.

¿Cómo se logra esto? Una forma artesanal de hacerlo es guardar en algún medio de almacenamiento (por ejemplo una base de datos), toda la información necesaria para realizar el cálculo en cuestión, devolver al usuario algo que le de tranquilidad y, mediante algún mecanismo offline (un cronjob por ejemplo), terminar los trabajos que se dejaron a la mitad.

Otro modo de resolverlo (un poco más profesional) es utilizar algún sistema de procesamiento de trabajos en paralelo, por ejemplo Gearman.

¿Qué otras alternativas se te ocurren para resolver este problema?

mchojrin

4 comentarios en “Cómo tratar con procesos largos en PHP

  • Jon Li

    Como puedo hacer con el envío de mails de un hosting con phpMailer que solo me deja enviar 9 mails por minuto pero yo necesito enviar 200 al dia, si lo meto en un script me dice
    «This request takes too long to process, it is timed out by the server. If it should not be timed out, please contact administrator of this web site to increase ‘Connection Timeout’.»
    Ya he solicitado se me amplié el tiempo de ejecución pero me dicen que ya estoy en el máximo permitido que son 2 minutos.. Gracias

    • Hola Jon, gracias por tu consulta.

      Según mis cálculos, para enviar 200 mails al día deberías enviar uno cada 7.2 minutos, lo cual te deja dentro de los límites de tu servidor.

      Algo que podrías hacer para lograr eso es guardar los mails en una cola y tener un proceso que corra cada, por ejemplo, 5 minutos y realice algunos envíos encolados.

      ¿Tienes posibilidad de usar cronjobs?

¿Te quedó alguna duda? Publica aca tu pregunta

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

A %d blogueros les gusta esto: