Réessayer les fonctions asynchrones

Ce document explique comment demander aux fonctions d'arrière-plan asynchrones (non HTTPS) de réessayer en cas d'échec.

Sémantique d'une répétition de tentative

Cloud Functions garantit l'exécution de type "au moins une fois" d'une fonction basée sur des événements pour chaque événement émis par une source d'événement. Par défaut, si un appel de fonction se termine par une erreur, la fonction n'est plus appelée et l'événement est supprimé. Lorsque vous activez la répétition des tentatives Cloud Functions relance l'appel de fonction en échec jusqu'à elle se termine avec succès ou la fenêtre de nouvelle tentative expire.

Pour les fonctions de 2e génération, cette fenêtre de nouvelle tentative expire au bout de 24 heures. Pour les fonctions de 1re génération, elle expire au bout de 7 jours. Cloud Functions tente d'appeler les fonctions basées sur des événements qui ont été récemment définies en appliquant une stratégie d'intervalle exponentiel entre les tentatives, cet intervalle étant croissant et allant de 10 à 600 secondes. Cette stratégie est appliquée aux nouvelles fonctions la première fois que vous les déployez. Il n'est pas appliqué de manière rétroactive des fonctions qui ont été déployées pour la première fois avant que les modifications ces notes de version même si vous redéployez les fonctions.

Lorsque la répétition des tentatives n'est pas activée pour une fonction, ce qui est la valeur par défaut, la fonction indique toujours qu'elle a bien été exécutée, et les codes de réponse 200 OK peuvent apparaître dans ses journaux. Cela se produit même si la fonction a rencontré une erreur. Pour indiquer clairement lorsque votre fonction rencontre une erreur, veillez à signaler les erreurs de manière appropriée.

Pourquoi les fonctions basées sur des événements échouent-elles ?

Il peut arriver qu'une fonction se ferme prématurément en raison d'une erreur interne. Par défaut, cette fonction peut être relancée automatiquement ou non.

Le plus souvent, une fonction basée sur des événements peut échouer en raison d'erreurs générées dans le code même de la fonction. Les raisons peuvent être les suivantes :

  • La fonction contient un bug et l’environnement d'exécution renvoie une exception.
  • La fonction ne peut pas atteindre de point de terminaison de service, ou bien elle dépasse le délai en essayant d'y parvenir.
  • La fonction renvoie intentionnellement une exception (par exemple, lorsqu'un paramètre échoue à la validation).
  • Une fonction Node.js renvoie une promesse refusée ou transmet à un rappel une valeur qui n'est pas null.

Dans tous ces cas, la fonction cesse de s'exécuter par défaut et l'événement est supprimé. Pour relancer la fonction en cas d'erreur, vous pouvez modifier la stratégie de nouvelles tentatives par défaut en définissant la propriété "Réessayer après échec". L'événement est alors relancé de façon répétée jusqu'à ce que la fonction se termine ou que le délai avant expiration des nouvelles tentatives soit écoulé.

Activer ou désactiver les nouvelles tentatives

Configurer les nouvelles tentatives à partir de la console

Si vous créez une fonction :

  1. Dans l'écran Créer une fonction, sous Déclencheur, choisissez le type d'événement devant servir de déclencheur pour votre fonction.
  2. Cochez la case Réessayer après échec pour activer la répétition des tentatives.

Si vous mettez à jour une fonction existante :

  1. Sur la page Vue d'ensemble de Cloud Functions, cliquez sur le nom du que vous mettez à jour pour ouvrir l'écran Informations sur la fonction, puis Sélectionnez Modifier dans la barre de menu pour afficher le volet Déclencheur.
  2. Cochez ou décochez la case Réessayer après échec. de nouvelles tentatives.

Configurer les nouvelles tentatives à partir du code de votre fonction

Avec Cloud Functions for Firebase, vous pouvez activer la répétition des tentatives dans le code pour une . Pour une fonction d'arrière-plan telle que functions.foo.onBar(myHandler);, utiliser runWith et configurez une stratégie d'échec:

functions.runWith({failurePolicy: true}).foo.onBar(myHandler);

En définissant true comme indiqué, vous configurez une fonction qui effectuera une nouvelle tentative en cas d'échec.

Bonnes pratiques

Cette section décrit les bonnes pratiques relatives à l'utilisation de la répétition des tentatives.

Utiliser la répétition pour faire face aux erreurs temporaires

Votre fonction est relancée en continu tant que son exécution n'est pas réussie. Vous devez donc éliminer de votre code les erreurs permanentes telles que les bugs par le biais de tests. Ce n'est qu'après cette étape que vous pourrez activer la répétition des tentatives. Il est préférable d'utiliser les nouvelles tentatives pour gérer les requêtes intermittentes ou temporaires d'échecs qui présentent une forte probabilité de résolution lors d'une nouvelle tentative, comme un point de terminaison de service instable ou un délai avant expiration.

Définir une condition de fin pour éviter les boucles infinies de répétition de tentatives

Il est recommandé de protéger votre fonction contre les boucles continues lors de l'utilisation de la répétition des tentatives. Pour ce faire, incluez une condition de fin bien définie avant le début du traitement de la fonction. Notez que cette technique ne fonctionne que si votre fonction démarre correctement et qu'elle est en mesure d'évaluer la condition de fin.

Une approche simple, mais efficace, consiste à ignorer les événements dont l'horodatage est antérieur à une certaine période. Cela permet d'éviter des exécutions excessives lorsque les échecs sont persistants ou plus longs que prévu.

Par exemple, l'extrait de code suivant supprime tous les événements de plus de dix secondes :

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;
}

Utiliser catch avec des promesses

Si la répétition de tentatives est activée pour votre fonction, toute erreur non gérée déclenche une nouvelle tentative. Assurez-vous que votre code capture toutes les erreurs qui ne devraient pas entraîner de nouvelle tentative.

Voici un exemple de ce que vous devez faire :

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

Rendre les fonctions déclenchées par des événements idempotentes

Les fonctions basées sur des événements qui peuvent être relancées doivent être idempotentes. Voici quelques consignes générales pour créer une telle fonction :

  • De nombreuses API externes (telles que Stripe) vous permettent de fournir une clé d'idempotence en tant que paramètre. Si vous utilisez une telle API, vous devez utiliser l'ID d'événement comme clé d'idempotence.
  • L'idempotence fonctionne bien avec une livraison de type "au moins une fois", car elle permet de répéter la tentative en toute sécurité. Une bonne pratique pour écrire du code fiable consiste donc à combiner l'idempotence à la répétition des tentatives.
  • Assurez-vous que votre code est idempotent en interne. Exemple :
    • Assurez-vous que les mutations peuvent se produire plus d'une fois sans en changer le résultat.
    • Interrogez l'état de la base de données dans une transaction avant de muter l'état.
    • Assurez-vous que tous les effets secondaires sont eux-mêmes idempotents.
  • Imposez un contrôle transactionnel en dehors de la fonction, indépendamment du code. Par exemple, conservez l'état quelque part en notant qu'un ID d'événement donné a déjà été traité.
  • Gérez les appels de fonction doubles hors bande. Par exemple, mettez en place un processus de nettoyage distinct qui se lance après les appels de fonction doubles.

Configurer la stratégie de nouvelle tentative

Selon les besoins de votre fonction, vous pouvez configurer la stratégie de nouvelle tentative. Cela vous permet de configurer n'importe quelle combinaison des éléments suivants :

  • Réduire la fenêtre de nouvelle tentative de sept jours une durée qui peut aller jusqu'à 10 minutes seulement.
  • Modifier le temps d'intervalle minimal et maximal pour la stratégie de nouvelle tentative avec intervalle exponentiel entre les tentatives.
  • Modifier la stratégie de nouvelle tentative pour réessayer immédiatement.
  • Configurer une file d'attente de lettres mortes.
  • Définir un nombre maximal et minimal de tentatives de livraison.

Pour configurer la stratégie de nouvelle tentative, procédez comme suit :

  1. Écrivez une fonction HTTP.
  2. Utilisez l'API Pub/Sub pour créer un abonnement Pub/Sub, en spécifiant l'URL de la fonction en tant que cible.

Consultez la documentation de Pub/Sub sur la gestion des échecs. pour en savoir plus sur la configuration directe de Pub/Sub.