다음을 통해 공유


지속성 함수의 오류 처리(Azure Functions)

Durable Function 오케스트레이션은 코드로 구현되며, 프로그래밍 언어의 기본 제공 오류 처리 기능을 사용할 수 있습니다. 오케스트레이션에 오류 처리 및 보정을 추가하는 데 필요한 새로운 개념은 없습니다. 그러나 알고 있어야 하는 몇 가지 동작이 있습니다.

참고

Azure Functions용 Node.js 프로그래밍 모델 버전 4가 일반 공급됩니다. 새로운 v4 모델은 JavaScript 및 TypeScript 개발자에게 보다 유연하고 직관적인 환경을 제공하도록 설계되었습니다. 마이그레이션 가이드에서 v3과 v4의 차이점에 대해 자세히 알아봅니다.

다음 코드 조각에서 JavaScript(PM4)는 새로운 환경인 프로그래밍 모델 V4를 나타냅니다.

작업 함수 및 하위 오케스트레이션의 오류

Durable Functions에서는 작업 함수 또는 하위 오케스트레이션 내에서 throw된 처리되지 않은 예외가 표준화된 예외 형식을 사용하여 오케스트레이터 함수로 다시 마샬링됩니다.

예를 들어 두 계정 간에 자금 이체를 수행하는 다음 오케스트레이터 함수를 고려합니다.

Durable Functions C# in-process에서는 처리되지 않은 예외가 FunctionFailedException으로 throw됩니다.

예외 메시지는 일반적으로 오류를 발생시킨 작업 함수 또는 하위 오케스트레이션을 식별합니다. 자세한 오류 정보에 액세스하려면 속성을 검사합니다 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
            });
    }
}

참고

이전 C# 예제는 Durable Functions 2.x에 대한 것입니다. Durable Functions 1.x의 경우 IDurableOrchestrationContext 대신 DurableOrchestrationContext를 사용해야 합니다. 버전 간 차이점에 대한 자세한 내용은 Durable Functions 버전 문서를 참조하세요.

첫 번째 CreditAccount 함수 호출이 실패하면 오케스트레이터 함수는 자금을 원본 계정에 다시 크레딧하여 보정합니다.

엔터티 함수의 오류

엔터티 함수에 대한 예외 처리 동작은 Durable Functions 호스팅 모델에 따라 다릅니다.

C# In Process를 사용하는 Durable Functions에서는 엔터티 함수에서 throw된 원래 예외 형식이 오케스트레이터에 직접 반환됩니다.

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

    // ...
}

참고

이전 C# 예제는 Durable Functions 2.x에 대한 것입니다. Durable Functions 1.x의 경우 IDurableOrchestrationContext 대신 DurableOrchestrationContext를 사용해야 합니다. 버전 간 차이점에 대한 자세한 내용은 Durable Functions 버전 문서를 참조하세요.

이전 예제의 작업 함수 호출은 자동 재시도 정책을 구성하기 위한 매개 변수를 사용합니다. 자동 재시도 정책을 사용자 지정하기 위한 몇 가지 옵션이 있습니다.

  • 최대 시도 횟수: 최대 시도 횟수입니다. 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;
        }
    }
}

참고

이전 C# 예제는 Durable Functions 2.x에 대한 것입니다. Durable Functions 1.x의 경우 IDurableOrchestrationContext 대신 DurableOrchestrationContext를 사용해야 합니다. 버전 간 차이점에 대한 자세한 내용은 Durable Functions 버전 문서를 참조하세요.

참고

이 메커니즘은 실제로 진행 중인 활동 함수 실행을 종료하지 않습니다. 대신 단순히 오케스트레이터 함수를 사용하여 결과를 무시하고 계속 진행할 수 있습니다. 자세한 내용은 타이머 설명서를 참조하세요.

처리되지 않은 예외

처리되지 않은 예외로 인해 오케스트레이터 함수가 실패하면 예외 정보가 자세히 기록되고 인스턴스가 Failed 상태로 완료됩니다.

FailureDetails(.NET Isolated)에 대한 사용자 지정 예외 속성 포함

.NET 격리 모델에서 지속성 작업 워크플로를 실행하는 경우 작업 실패가 FailureDetails 개체로 자동으로 직렬화됩니다. 기본적으로 이 개체에는 다음과 같은 표준 필드가 포함됩니다.

  • ErrorType - 예외 형식 이름
  • 메시지 - 예외 메시지
  • StackTrace - 직렬화된 스택 추적
  • InnerFailure – 재귀 내부 예외에 대한 중첩된 FailureDetails 개체

Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0부터 IExceptionPropertiesProvider( v1.16.1패키지부터 Microsoft.DurableTask.Worker에 정의됨)를 구현하여 이 동작을 확장할 수 있습니다. 이 공급자는 FailureDetails.Properties 사전에 포함할 예외 형식 및 해당 속성 중 어느 것을 정의합니다.

참고

  • 이 기능은 .NET 격리 에서만 사용할 수 있습니다. Java에 대한 지원은 향후 릴리스에서 추가될 예정입니다.
  • Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0 이상을 사용하고 있는지 확인합니다.
  • Microsoft.DurableTask.Worker v1.16.1 이상을 사용하고 있는지 확인합니다.

예외 속성 공급자 구현

사용자 지정 IExceptionPropertiesProvider를 구현하여 관심 있는 예외에 대해 선택한 속성을 추출하고 반환합니다. 반환된 사전은 일치하는 예외 형식이 throw될 때 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
        };
    }
}

공급자 등록

.NET 격리 작업자 호스트에 사용자 지정 IExceptionPropertiesProvider를 등록합니다(일반적으로 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 출력

공급자의 구성과 일치하는 예외가 발생하면 오케스트레이션은 다음과 같이 serialize된 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
    }
  }
}

다음 단계