Ir a la consola

Reintenta la ejecución de funciones asíncronas

Este documento describe cómo puedes solicitar funciones asíncronas en segundo plano (no HTTPS) para volver a ejecutarlas en caso de error.

Semántica de intentos reiterados

Cloud Functions garantiza la ejecución al menos una vez de una función en segundo plano para cada evento que emite una fuente de eventos. Sin embargo, según la configuración predeterminada, si la invocación de una función termina con un error, esta no se invocará de nuevo y se descartará el evento. Cuando habilitas los reintentos en una función en segundo plano, Cloud Functions volverá a intentar la invocación de una función con errores hasta que se complete correctamente o el período de reintentos (7 días, de forma predeterminada) expire.

Por qué no se completan las funciones en segundo plano

En ocasiones poco comunes, es posible que una función se cierre de forma prematura debido a un error interno y, según la configuración predeterminada, podría volver a ejecutarse la función, automáticamente o no.

Por lo general, es posible que una función no logre completarse correctamente debido a errores en el propio código de la función. A continuación, se muestran algunos de los motivos por los que puede ocurrir:

  • La función contiene un error y el tiempo de ejecución muestra una excepción.
  • La función no puede alcanzar el extremo del servicio o se agota el tiempo de espera mientras intenta alcanzarlo.
  • La función muestra una excepción de forma intencional (por ejemplo, cuando falla la validación de un parámetro).
  • Cuando las funciones escritas en Node.js muestran una promesa rechazada o pasan un valor no null a una devolución de llamada.

En cualquiera de los casos anteriores, en la configuración predeterminada, la función deja de ejecutarse y el evento se descarta. Si deseas ejecutar de nuevo la función cuando ocurre un error, puedes configurar la propiedad "volver a intentar en caso de error" para cambiar la política de intentos reiterados predeterminada. Esto hace que se vuelva a ejecutar el evento reiteradamente por varios días hasta que la función se complete de manera correcta.

Inhabilita y habilita los reintentos

Con GCP Console

Puedes habilitar o inhabilitar los reintentos en GCP Console de la siguiente manera:

  1. Ve a la página de descripción general de Cloud Functions en Cloud Platform Console.

  2. Haz clic en Crear función. De forma alternativa, haz clic en una función existente para ir a la página de detalles y haz clic en Editar.

  3. Completa los campos obligatorios para la función.

  4. Asegúrate de que el campo Activador esté configurado para un tipo de activador de función en segundo plano, como Cloud Pub/Sub o Cloud Storage.

  5. Haz clic en Más para expandir la configuración avanzada.

  6. Marca o desmarca la casilla con la etiqueta Volver a intentar en caso de error.

Recomendaciones

Esta sección describe las recomendaciones para usar los intentos reiterados.

Usa los intentos reiterados para administrar errores temporales

Dado que la función se vuelve a intentar de manera reiterada hasta que se ejecute correctamente, las fallas permanentes, como los errores, se deben eliminar del código mediante pruebas antes de habilitar los intentos reiterados. Lo mejor es usar estos intentos para administrar fallas intermitentes o temporales con una alta probabilidad de resolverse en un intento reiterado, como un extremo de servicio o un tiempo de espera inestables.

Configura una condición de finalización para prevenir los bucles de intentos reiterados infinitos

Se recomienda proteger tu función contra los bucles continuos cuando usas los intentos reiterados. Para esto, puedes incluir una condición de finalización bien definida antes de que la función comience a procesarse. Un enfoque simple y eficaz es descartar eventos con marcas de tiempo que superen un tiempo definido. Esto permite evitar ejecuciones excesivas cuando las fallas son persistentes o duran más de lo esperado.

Por ejemplo, este fragmento de código descarta todos los eventos que superen los 10 segundos de antigüedad:

const eventAgeMs = Date.now() - Date.parse(event.timestamp);
const eventMaxAgeMs = 10000;
if (eventAgeMs > eventMaxAgeMs) {
  console.log(`Dropping event ${event} with age[ms]: ${eventAgeMs}`);
  callback();
  return;
}

Usa catch con promesas

Si tu función tiene habilitados los reintentos, cualquier error no administrado activará un reintento. Asegúrate de que tu código capture cualquier error que no debería dar como resultado un reintento.

Este es un ejemplo de lo que se debe hacer:

return doFooAsync().catch((err) => {
    if (isFatal(err)) {
        console.error(`Fatal error ${err}`);
    }
    return Promise.reject(err);
});

Haz idempotentes a las funciones en segundo plano cuya ejecución se puede intentar nuevamente

Deben ser idempotentes las funciones en segundo plano cuya ejecución se pueda volver a intentar. A continuación, te mostramos algunos lineamientos generales para crear una función idempotente en segundo plano:

  • Muchas API externas (como Stripe) te permiten proporcionar una clave de idempotencia como parámetro. Si usas una API de este tipo, debes usar el ID de evento como la clave de idempotencia.
  • La idempotencia funciona bien con la entrega "al menos una vez", ya que permite que los intentos reiterados sean seguros. Por lo tanto, una recomendación general para escribir un código confiable es combinar la idempotencia con los intentos reiterados.
  • Asegúrate de que tu código sea idempotente de forma interna. Por ejemplo:
    • Asegúrate de que puedan ocurrir mutaciones más de una vez sin que cambie el resultado.
    • Consulta el estado de la base de datos en una transacción antes de mutar el estado.
    • Asegúrate de que todos los efectos secundarios sean idempotentes en sí.
  • Debes imponer una verificación transaccional fuera de la función y que sea independiente del código. Por ejemplo, conserva el estado en algún lugar que registre si ya se procesó un ID de evento determinado.
  • Administra las llamadas de función duplicadas fuera de banda. Por ejemplo, implementa un proceso de limpieza independiente que borre las llamadas de función duplicadas.