次の方法で共有


Durable Functions のエラー処理 (Azure Functions)

Durable Function のオーケストレーションはコードで実装され、プログラミング言語の組み込みエラー処理機能を使用できます。 エラー処理と補正をオーケストレーションに追加するために学習する必要がある新しい概念は、実際にはありません。 ただし、注意する必要があるいくつかの動作があります。

Note

Azure Functions のNode.js プログラミング モデルのバージョン 4 が一般公開されています。 新しい v4 モデルは、JavaScript と TypeScript の開発者にとって、より柔軟で直感的なエクスペリエンスが得られるように設計されています。 v3 と v4 の違いの詳細については、移行ガイドを参照してください。

次のコード スニペットでは、JavaScript (PM4) は、新しいエクスペリエンスであるプログラミング モデル V4 を示しています。

アクティビティ関数とサブオーケストレーションのエラー

Durable Functions では、アクティビティ関数またはサブオーケストレーション内でスローされた未処理の例外は、標準化された例外タイプを使用してオーケストレーター関数に転送されます。

たとえば、2 つの口座間の資金振替を実行する次のオーケストレーター関数を考えてみましょう。

インプロセスの Durable Functions C# では、ハンドルされない例外が FunctionFailedException としてスローされます。

例外メッセージは、通常、エラーの原因となったアクティビティ関数またはサブオーケストレーションを識別します。 より詳細なエラー情報にアクセスするには、 InnerException プロパティを調べます。

[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 (FunctionFailedException)
    {
        // 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
            });
    }
}

Note

前述の C# の例は Durable Functions 2.x 用です。 Durable Functions 1.x の場合、IDurableOrchestrationContext の代わりに DurableOrchestrationContext を使用する必要があります。 バージョン間の相違点の詳細については、Durable Functions のバージョンに関する記事を参照してください。

最初の CreditAccount 関数の呼び出しが失敗した場合、オーケストレーター関数は、資金を送金元口座に戻すことで、これを補正します。

エンティティ関数のエラー

エンティティ関数の例外処理動作は、Durable Functions ホスティング モデルによって異なります。

C# インプロセスを使用する Durable Functions では、エンティティ関数によってスローされた元の例外型がオーケストレーターに直接返されます。

[FunctionName("Function1")]
public static async Task<string> RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    try
    {
        var entityId = new EntityId(nameof(Counter), "myCounter");
        await context.CallEntityAsync(entityId, "Add", 1);
    }
    catch (Exception ex)
    {
        // The exception type will be InvalidOperationException with the message "this is an entity exception".
    }
    return string.Empty;
}

[FunctionName("Counter")]
public static void Counter([EntityTrigger] IDurableEntityContext ctx)
{
    switch (ctx.OperationName.ToLowerInvariant())
    {
        case "add":
            throw new InvalidOperationException("this is an entity exception");
        case "get":
            ctx.Return(ctx.GetState<int>());
            break;
    }
}

エラー発生時の自動再試行

アクティビティ関数またはサブオーケストレーション関数を呼び出すときに、自動再試行ポリシーを指定できます。 次の例では、関数の呼び出しを最大 3 回試行し、次の再試行まで 5 秒間待機します。

[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);

    // ...
}

Note

前述の C# の例は Durable Functions 2.x 用です。 Durable Functions 1.x の場合、IDurableOrchestrationContext の代わりに DurableOrchestrationContext を使用する必要があります。 バージョン間の相違点の詳細については、Durable Functions のバージョンに関する記事を参照してください。

前の例のアクティビティ関数呼び出しでは、自動再試行ポリシーを構成するためのパラメーターを使用します。 自動再試行ポリシーをカスタマイズするために、次のようないくつかのオプションがあります。

  • 最大試行回数: 試行が行われる最大回数。 1 を設定した場合、再試行は行われません。
  • 1 回目の再試行の間隔: 1 回目の再試行を行う前の待機時間。
  • バックオフ係数: バックオフの増加率を決定するために使用される係数。 既定値は 1 です。
  • 最大再試行間隔: 次の再試行を実行する前に待機する最大時間。
  • 再試行タイムアウト: 再試行を実行するために費やす最大時間。 既定の動作は、無限の再試行です。

カスタムの再試行ハンドラー

.NET または Java を使用する場合は、コードで再試行ハンドラーを実装することもできます。 これは、宣言型再試行ポリシーが十分に表現できない場合に便利です。 カスタムの再試行ハンドラーをサポートしていない言語の場合でも、再試行間に遅延を挿入するためのループ、例外処理、タイマーを使用して再試行ポリシーを実装できます。

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

関数のタイムアウト

オーケストレーター関数内の関数呼び出しは、完了に時間がかかりすぎる場合は破棄できます。 現時点でこれを行う適切な方法は、次の例に示すように、"any" タスク セレクターを使用して持続的タイマーを作成することです。

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

Note

前述の C# の例は Durable Functions 2.x 用です。 Durable Functions 1.x の場合、IDurableOrchestrationContext の代わりに DurableOrchestrationContext を使用する必要があります。 バージョン間の相違点の詳細については、Durable Functions のバージョンに関する記事を参照してください。

Note

このメカニズムは、進行中のアクティビティ関数の実行を実際には終了しません。 オーケストレーター関数が、単に結果を無視して、次に進めるようにするだけです。 詳細については、タイマーに関するドキュメントをご覧ください。

ハンドルされない例外

オーケストレーター関数がハンドルされない例外で失敗した場合、例外の詳細がログに記録され、インスタンスは Failed 状態で完了します。

FailureDetails のカスタム例外プロパティを含める (.NET Isolated)

.NET 分離モデルで Durable Task ワークフローを実行すると、タスクエラーは自動的に FailureDetails オブジェクトにシリアル化されます。 既定では、このオブジェクトには次のような標準フィールドが含まれます。

  • ErrorType — 例外の種類名
  • Message — 例外メッセージ
  • StackTrace — シリアル化されたスタック トレース
  • InnerFailure – 再帰的な内部例外の入れ子になった FailureDetails オブジェクト

Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0 以降では、IExceptionPropertiesProvider ( v1.16.1パッケージから Microsoft.DurableTask.Worker で定義) を実装することで、この動作を拡張できます。 このプロバイダーは、どの例外の種類とそのプロパティを FailureDetails.Properties ディクショナリに含めるかを定義します。

Note

  • この機能は 、.NET Isolated でのみ使用できます。 Java のサポートは、今後のリリースで追加される予定です。
  • Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0 以降を使用していることを確認します。
  • Microsoft.DurableTask.Worker v1.16.1 以降を使用していることを確認します。

例外プロパティ プロバイダーを実装する

カスタム IExceptionPropertiesProvider を実装して、気になる例外の選択したプロパティを抽出して返します。 一致する例外のタイプがスローされると、返されたディクショナリは FailureDetails のプロパティフィールドにシリアル化されます。

using Microsoft.DurableTask.Worker;

public class CustomExceptionPropertiesProvider : IExceptionPropertiesProvider
{
    public IDictionary<string, object?>? GetExceptionProperties(Exception exception)
    {
        return exception switch
        {
            ArgumentOutOfRangeException e => new Dictionary<string, object?>
            {
                ["ParamName"] = e.ParamName,
                ["ActualValue"] = e.ActualValue
            },
            InvalidOperationException e => new Dictionary<string, object?>
            {
                ["CustomHint"] = "Invalid operation occurred",
                ["TimestampUtc"] = DateTime.UtcNow
            },
            _ => null // Other exception types not handled
        };
    }
}

プロバイダーを登録する

カスタム IExceptionPropertiesProvider を .NET Isolated worker ホスト (通常は Program.cs) に登録します。

using Microsoft.DurableTask.Worker;
using Microsoft.Extensions.DependencyInjection;

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(builder =>
    {
        // Register custom exception properties provider
        builder.Services.AddSingleton<IExceptionPropertiesProvider, CustomExceptionPropertiesProvider>();
    })
    .Build();

host.Run();

登録されると、処理された型のいずれかに一致する例外には、構成されたプロパティが FailureDetails に自動的に含まれます。

FailureDetails 出力のサンプル

プロバイダーの構成に一致する例外が発生すると、オーケストレーションは次のようなシリアル化された FailureDetails 構造体を受け取ります。

{
  "errorType": "TaskFailedException",
  "message": "Activity failed with an exception.",
  "stackTrace": "...",
  "innerFailure": {
    "errorType": "ArgumentOutOfRangeException",
    "message": "Specified argument was out of range.",
    "properties": {
      "ParamName": "count",
      "ActualValue": 42
    }
  }
}

次のステップ