Ce document explique comment demander aux fonctions d'arrière-plan asynchrones (non HTTPS) de réessayer en cas d'échec.
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 les cas énoncés ci-dessus, la fonction cesse d'être exécutée et renvoie une erreur. Les déclencheurs d'événements qui génèrent les messages disposent de stratégies de nouvelle tentative que vous pouvez personnaliser pour répondre aux besoins de votre fonction.
Sémantique d'une répétition de tentative
Cloud Functions assure 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. La manière dont vous configurez les nouvelles tentatives dépend de la façon dont vous avez créé votre fonction:
- Les fonctions créées dans la console Google Cloud ou avec l'API Admin Cloud Run nécessitent de créer et de gérer séparément les déclencheurs d'événements. Les déclencheurs ont des comportements de nouvelle tentative par défaut que vous pouvez personnaliser en fonction des besoins de votre fonction.
- Les fonctions créées avec l'API Cloud Functions V2 créent implicitement les déclencheurs d'événements nécessaires, par exemple des sujets Pub/Sub ou des déclencheurs Eventarc. Par défaut, les nouvelles tentatives sont désactivées pour ces déclencheurs et peuvent être réactivées à l'aide de l'API Cloud Functions v2.
Fonctions basées sur les événements créées avec Cloud Run
Les fonctions créées dans la console Google Cloud ou avec l'API d'administration Cloud Run nécessitent de créer et de gérer séparément les déclencheurs d'événement. Nous vous recommandons vivement de consulter le comportement par défaut de chaque type de déclencheur:
- La règle de nouvelle tentative Eventarc a une durée de conservation des messages par défaut de 24 heures avec un intervalle exponentiel entre les tentatives. Consultez la documentation Eventarc sur les événements de nouvelle tentative.
- Pub/Sub utilise par défaut la règle de nouvelle diffusion immédiate pour tous les abonnements. Consultez la documentation Pub/Sub sur la gestion des échecs de message et les requêtes de nouvelle tentative.
Fonctions basées sur les événements créées avec l'API Cloud Functions v2
Les fonctions créées à l'aide de l'API Cloud Functions v2 (par exemple, à l'aide de gcloud CLI Cloud Functions, de l'API REST ou de Terraform) créent et gèrent des déclencheurs d'événements en votre nom. 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 les nouvelles tentatives sur une fonction basée sur des événements, Cloud Functions tente d'appeler la fonction défaillante jusqu'à ce qu'elle aboutisse ou que la fenêtre de nouvelle tentative arrive à expiration.
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.
Activer ou désactiver les nouvelles tentatives
Configurer les nouvelles tentatives à partir de la console
Si vous créez une fonction :
- Dans l'écran Créer une fonction, sous Déclencheur, choisissez le type d'événement devant servir de déclencheur pour votre fonction.
- Cochez la case Réessayer après échec pour activer les nouvelles tentatives.
Si vous mettez à jour une fonction existante :
- Sur la page Cloud Functions Overview (Présentation), cliquez sur le nom de la fonction que vous mettez à jour pour ouvrir l'écran Function details (Détails de la fonction), puis sélectionnez Edit (Modifier) dans la barre de menu pour afficher le volet Trigger (Déclencheur).
- Cochez ou décochez la case Réessayer après échec pour activer ou désactiver les nouvelles tentatives.
Configurer les nouvelles tentatives à partir du code de votre fonction
Avec Cloud Functions for Firebase, vous pouvez activer les nouvelles tentatives dans le code d'une fonction. Pour ce faire pour une fonction d'arrière-plan telle que functions.foo.onBar(myHandler);
, utilisez runWith
et configurez une stratégie d'échec:
functions.runWith({failurePolicy: true}).foo.onBar(myHandler);
Définir true
comme indiqué configure une fonction pour qu'elle soit réessayée en cas d'échec.
Fenêtre de nouvelle tentative
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 créées 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. Elle n'est pas appliquée rétroactivement aux fonctions existantes qui ont été déployées avant l'entrée en vigueur des modifications décrites dans cette note de version, même si vous redéployez les fonctions.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. Les tentatives sont particulièrement utiles pour gérer les échecs intermittents/temporaires qui présentent une probabilité élevée de résolution à mesure des nouvelles tentatives, par exemple lorsqu'un point de terminaison de service ou un délai d’inactivité est instable.
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 doivent 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
En fonction des besoins de votre fonction, vous pouvez configurer directement 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 :
- Écrivez une fonction HTTP.
- 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 Pub/Sub sur la gestion des échecs pour en savoir plus sur la configuration directe de Pub/Sub.