Gestion des erreurs dans Fonctions durables (Azure Functions)

Les orchestrations de fonctions durables sont implémentées dans du code et peuvent utiliser les fonctionnalités de gestion des erreurs intégrées dans le langage de programmation. Vous n’avez pas vraiment besoin d’apprendre de nouveaux concepts pour ajouter la gestion des erreurs et les compensations dans vos orchestrations. Toutefois, vous devez tenir compte de certains comportements.

Remarque

La version 4 du modèle de programmation Node.js pour Azure Functions est en disponibilité générale. Le nouveau modèle v4 est conçu pour offrir une expérience plus flexible et intuitive pour les développeurs JavaScript et TypeScript. En savoir plus sur les différences entre v3 et v4 dans le guide de migration.

Dans les extraits de code suivants, JavaScript (PM4) désigne le modèle de programmation V4, la nouvelle expérience.

Erreurs liées aux fonctions d’activité

Toute exception levée dans une fonction d’activité est marshalée en retour vers la fonction d’orchestrateur et levée en tant qu’élément FunctionFailedException. Vous pouvez écrire du code de gestion des erreurs et de compensation qui correspond le mieux à vos besoins dans la fonction d’orchestrateur.

Par exemple, considérez la fonction d’orchestrateur suivante, qui transfère des fonds d’un compte à un autre :

[FunctionName("TransferFunds")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var transferDetails = context.GetInput<TransferOperation>();

    await context.CallActivityAsync("DebitAccount",
        new
        {
            Account = transferDetails.SourceAccount,
            Amount = transferDetails.Amount
        });

    try
    {
        await context.CallActivityAsync("CreditAccount",
            new
            {
                Account = transferDetails.DestinationAccount,
                Amount = transferDetails.Amount
            });
    }
    catch (Exception)
    {
        // Refund the source account.
        // Another try/catch could be used here based on the needs of the application.
        await context.CallActivityAsync("CreditAccount",
            new
            {
                Account = transferDetails.SourceAccount,
                Amount = transferDetails.Amount
            });
    }
}

Notes

Les exemples C# précédents portent sur Durable Functions 2.x. Pour Durable Functions 1.x, vous devez utiliser DurableOrchestrationContext au lieu de IDurableOrchestrationContext. Pour en savoir plus sur les différences entre les versions, consultez l’article Versions de Durable Functions.

Si le premier appel à la fonction CreditAccount échoue, la fonction d’orchestrateur compense en recréditant les fonds sur le compte source.

Nouvelle tentative automatique en cas d’échec

Lorsque vous appelez les fonctions d’activité ou les fonctions de sous-orchestration, vous pouvez spécifier une stratégie de nouvelle tentative automatique. L’exemple suivant tente d’appeler une fonction jusqu’à trois fois, et attend 5 secondes avant chaque nouvelle tentative :

[FunctionName("TimerOrchestratorWithRetry")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var retryOptions = new RetryOptions(
        firstRetryInterval: TimeSpan.FromSeconds(5),
        maxNumberOfAttempts: 3);

    await context.CallActivityWithRetryAsync("FlakyFunction", retryOptions, null);

    // ...
}

Notes

Les exemples C# précédents portent sur Durable Functions 2.x. Pour Durable Functions 1.x, vous devez utiliser DurableOrchestrationContext au lieu de IDurableOrchestrationContext. Pour en savoir plus sur les différences entre les versions, consultez l’article Versions de Durable Functions.

L'appel de fonction de l'activité de l'exemple précédent utilise un paramètre pour configurer une stratégie de nouvelles tentatives automatiques. Il existe plusieurs options de personnalisation de la stratégie de nouvelle tentative automatique :

  • Nombre maximal de tentatives: le nombre maximal de tentatives. Si la valeur est 1, aucune nouvelle tentative ne se produit.
  • Intervalle avant première nouvelle tentative : Temps d’attente avant la première nouvelle tentative.
  • Coefficient d’interruption : Coefficient permettant de déterminer le taux d’augmentation de l’interruption. La valeur par défaut est de 1.
  • Intervalle maximal entre deux nouvelles tentatives : Durée maximale de l’attente entre deux nouvelles tentatives.
  • Délai de nouvelle tentative : Temps que passe le système à effectuer de nouvelles tentatives. Par défaut, le système effectue ces nouvelles tentatives indéfiniment.

Descripteurs de nouvelle tentative personnalisés

Quand vous utilisez .NET ou Java, vous avez également la possibilité d’implémenter des descripteurs de nouvelle tentative dans le code. Cela est utile lorsque les stratégies de nouvelle tentative déclaratives ne sont pas suffisamment expressives. Pour les langages qui ne prennent pas en charge les descripteurs de nouvelle tentative personnalisés, vous avez toujours la possibilité d’implémenter des stratégies de nouvelle tentative à l’aide de boucles, de gestion des exceptions et de retardateurs pour injecter des retards entre les nouvelles tentatives.

RetryOptions retryOptions = new RetryOptions(
    firstRetryInterval: TimeSpan.FromSeconds(5),
    maxNumberOfAttempts: int.MaxValue)
    {
        Handle = exception =>
        {
            // True to handle and try again, false to not handle and throw.
            if (exception is TaskFailedException failure)
            {
                // Exceptions from TaskActivities are always this type. Inspect the
                // inner Exception to get more details.
            }

            return false;
        };
    }

await ctx.CallActivityWithRetryAsync("FlakeyActivity", retryOptions, null);

Délais d’expiration des fonctions

Vous souhaiterez peut-être abandonner un appel de fonction au sein d’une fonction d’orchestrateur s’il prend trop de temps. Actuellement, la méthode adéquate pour cela consiste à créer un retardateur durable avec un sélecteur de tâche « any », comme dans l’exemple suivant :

[FunctionName("TimerOrchestrator")]
public static async Task<bool> Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    TimeSpan timeout = TimeSpan.FromSeconds(30);
    DateTime deadline = context.CurrentUtcDateTime.Add(timeout);

    using (var cts = new CancellationTokenSource())
    {
        Task activityTask = context.CallActivityAsync("FlakyFunction");
        Task timeoutTask = context.CreateTimer(deadline, cts.Token);

        Task winner = await Task.WhenAny(activityTask, timeoutTask);
        if (winner == activityTask)
        {
            // success case
            cts.Cancel();
            return true;
        }
        else
        {
            // timeout case
            return false;
        }
    }
}

Notes

Les exemples C# précédents portent sur Durable Functions 2.x. Pour Durable Functions 1.x, vous devez utiliser DurableOrchestrationContext au lieu de IDurableOrchestrationContext. Pour en savoir plus sur les différences entre les versions, consultez l’article Versions de Durable Functions.

Notes

Ce mécanisme ne termine pas réellement l’exécution des fonctions d’activité en cours. Il permet simplement à la fonction de l’orchestrateur d’ignorer le résultat et de continuer. Pour plus d’informations, voir la documentation Minuteurs.

Exceptions non prises en charge

Si une fonction d’orchestration échoue avec une exception non prise en charge, les détails de cette dernière sont enregistrés et l’instance s’exécute avec un état Failed.

Étapes suivantes