WordPress Cron

Cómo trabajar con los crons de WordPress y no morir en el intento

Si has desarrollado alguna vez sobre WordPress, es muy probable que en alguna ocasión hayas necesitado utilizar crons y WordPress provee de una utilidad para ello. Siendo justos, más que crons mucha gente les llama pseudo-crons, y a continuación veremos porqué.

Siempre me acuerdo de este tweet de Mauricio cada vez que me toca programar uno:

¿Qué es un cron y qué es un pseudo-cron?

Veamos que dice la Wikipedia sobre lo que es un cron:

En el sistema operativo Unix, cron es un administrador regular de procesos en segundo plano (demonio) que ejecuta procesos o guiones a intervalos regulares (por ejemplo, cada minuto, día, semana o mes). Los procesos que deben ejecutarse y la hora a la que deben hacerlo se especifican en el archivo crontab. El nombre cron proviene del griego chronos (χρόνος) que significa «tiempo».

Cron se puede definir como el equivalente a Tareas Programadas de Windows.

Wikipedia

Resumiendo son tareas o procesos que programas para que se ejecuten cada cierto tiempo. ¿Y qué sería un pseudo-cron? Básicamente lo mismo pero con un «pero». Y es que ese cierto tiempo no depende del intervalo de tiempo en sí, si no de que tu sitio tenga una visita para disparar un evento que busca si hay crons pendientes y los ejecuta.

Los crons de WordPress se almacenan en base de datos, por lo tanto si por ejemplo programas que un cron se ejecute cada hora, y justo cuando «le toca» ejecutar una tarea resulta que tienes poco tráfico, y no recibes ninguna visita en 30 minutos, la tarea se ejecutará cuando recibas esa visita, habiendo pasado 1h 30 min desde que se ejecutó por última vez.

Es decir, los crons de WordPress emulan los crons de sistema, y tienen dependencia de que una visita dispare el evento.

Y esto puede derivar en problemas, sobre todo si lo que programas es la típica tarea que envía un mail o un informe cada 24h. Si programas la tarea para que envíe el informe todos los días a las 8:00, y la primera visita que lo dispara es a las 8:10, quedará programado para las 8:10 del día siguiente, y si al día siguiente acumulas X minutos más, al final vas acumulando un retraso que evidencia que no es una buena solución depende de para qué cosas.

¿Es buena idea entonces utilizar los crons de WordPress?

Depende, de si la tarea es crítica o no, de si tu sitio tiene mucho tráfico o no… Al final la decisión es de cada cual y de sus necesidades.

Si tienes programada una tarea que hace unas funciones críticas como por ejemplo enviar informes, actualizar datos desde una API, etc… nada sucederá hasta que una visita dispare el evento.

En cualquier caso vamos a ver cómo se programaría un cron en WordPress y las alternativas que tenemos si queremos no depender de si tenemos tráfico o no.

Cómo crear un cron en WordPress

Para crear un cron de WordPress debemos primero crear nuestro propio hook, y en él indicar la función/método que se ejecutará. A continuación deberemos programar la tarea con la función wp_next_scheduled de WordPress, donde indicaremos cuando queremos que se ejecute por primera vez, el intervalo de tiempo en el que se ejecutará a partir de entonces y el hook que queremos que dispare (el que vamos a crear al principio).

Como todo esto se entiende mejor con un ejemplo… Lo primero es crear nuestro propio hook e indicar qué método/función ejecutará:

add_action('my_cron_name', 'my_function_name');

El siguiente paso es programar la tarea. En este punto hay que tener en cuenta que si no tomamos precauciones, en cada petición estaremos programando de nuevo la tarea. Es decir, este punto debería hacerse una única vez.

Si este cron lo estás programando desde un plugin, este punto encaja perfectamente en el hook register_activation_hook, que sólo se ejecuta cuando activas un plugin. En cualquier caso como decía, mejor consultar primero si la tarea está programada o no:

if (! wp_next_scheduled('my_cron_name')) {
    wp_schedule_event((strtotime('09:00:00')), 'daily', 'my_cron_name');
}

El primer parámetro es la fecha/hora en la que queremos que se ejecute nuestro cron por primera vez. El segundo parámetro es el intervalo de tiempo (lo podemos indicar en segundos o utilizar unos valores predefinidos). El último parámetro es el nombre de nuestro hook, que creamos con anterioridad y disparará nuestro método/función my_function_name.

Por defecto tenemos una serie de intervalos definidos:

  • hourly: Cada hora
  • twicedaily: Dos veces al día (cada 12 horas)
  • daily: Una vez al día (cada 24 horas)
  • weekly: Una vez a la semana

Podemos generar nuestros propios intervalos añadiéndolos al filtro cron_schedules. Por ejemplo, para crear un cron que se ejecute cada 4 horas:

function add_my_cron_interval(array $schedules) {
    $schedules['four_hours'] = [
        'interval' => 4 * HOUR_IN_SECONDS,
        'display'  => esc_html__( 'Cada 4 horas' ),
    ];

    return $schedules;
}
add_filter('cron_schedules', 'add_my_cron_interval');

Por último, si llega el momento en que no necesitamos o no queremos que se sigan ejecutando estas tareas, deberemos desprogramarlas. Tenemos dos opciones, bien por código (al igual que antes, si estás desarrollando un plugin dispones de un hook adecuado para eliminar tareas: register_deactivation_hook):

$timestamp = wp_next_scheduled('my_cron_name');
wp_unschedule_event($timestamp, 'my_cron_name');

Bien por línea de comandos:

wp cron event delete cron_test

Cómo generar un cron de sistema que dispare los crons de WordPress

El primer paso es desactivar los crons de WordPress añadiendo esta línea en tu archivo wp-config.php:

define('DISABLE_WP_CRON', true);

A continuación genera el cron como se ha explicado en el punto anterior. Éste no se ejecutará porque lo tenemos desactivado, por lo tanto el siguiente punto es configurar un cron de sistema que ejecute los crons de WordPress.

Accede a tu servidor por ssh y ejecuta el comando:

crontab -e

Esto abrirá un editor con los crontabs que tengas programados, si es que tenías alguno. Aquí añadiremos una línea que apunte al fichero wp-cron.php, e indicaremos el intervalo para que se haga una petición cada X tiempo y se ejecuten las tareas cron programadas en WordPress:

*/15 * * * * wget -q -O - https://mydomain.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1

El formato para establecer el intervalo de las tareas cron sigue una determinada sintaxis. Son los 5 asteriscos, y su significado de izquierda a derecha es el siguiente:

  • Minutos: de 0 a 59
  • Horas de 0 a 23
  • Día del mes: de 1 a 31
  • Mes: de 1 a 12
  • Día de la semana: de 0 a 6 (el 0 es domingo)

Cada asterisco representa cualquier valor y si en lugar de asterisco utilizamos un número estaremos estableciendo un dato concreto. También podemos utilizar intervalos. De este modo:

  • */15 * * * *: Cada 15 minutos
  • 0 */1 * * *: Cada hora
  • 0 0 * * *: Todos los días a las 0:00 de la noche
  • 0 8,20 * * *: Todos los días a las 8:00 de la mañana y a las 20:00 de la tarde
  • 0 8 * * 1-5: De lunes a viernes a las 8:00 de la mañana

Espero que después de este post te quede más claro cómo funcionan los crons de WordPress, y cuando es conveniente apoyarse en los crons de sistema para que no se te descuadre nada.

¿Te ha resultado útil esta información? 🍺

Si este post te ha resuelto un problema, invítame a un café o a una cerveza. Con este pequeño gesto me animas a seguir escribiendo.

Comentarios

2 comentarios en Cómo trabajar con los crons de WordPress y no morir en el intento

  1. Artículo de obligada lectura para desarrolladores e implementadores en WordPress ya que es un grandísimo desconocido para la mayoría.

    Para mí la utilización del cron de sistema es la mejor solución, siempre, me permite controlar mucho más en qué momentos ejecutar o parar acciones, sobre todo en sitios con mucho tráfico.

    Pablo, añado unos apuntes sobre el famoso wget que me han salvado de muchos problemas:
    – Añado el parámetro -t 1 para que sólo intente una vez realizar la llamada, ya que si hay algún problema de carga en el servidor o el cron tarda mucho, el wget reintenta hasta 20 veces y podemos matar nuestro propio sistema.
    – Añado el parámetro –no-check-certificate que elimina el chequeo necesario del certificado al ser un entorno verificado y conocido y que si el servidor no está bien configurado, puede fallar.

    Y recordar siempre que hay que verificar cómo está configurado en el servidor donde ejecutamos el cron las DNS y que resuelvan a la propia máquina, ya que a veces hay que utilizar «localhost» o la propia IP que esté configurada en el servidor web, sino estaremos creando tráfico de red innecesario (wget que sale y entra en la máquina).

    Un abrazo

    1. Muchas gracias Fernando por la respuesta y tu aportación. Sin lugar a dudas lo tendré en cuenta para el futuro. ¡Un abrazo!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *:

  • El fin del tratamiento es únicamente la moderación de comentarios para evitar spam
  • La legitimación es tu consentimiento al comentar
  • No se comunicará ningún dato a terceros salvo por obligación legal
  • Tienes derecho al acceso, rectificación y eliminación de los comentarios