Menangani kesalahan dan pengulangan dalam orkestrasi

Anda menerapkan orkestrasi Durable Functions dalam kode, sehingga Anda menggunakan fitur penanganan kesalahan bawaan bahasa Anda. Penanganan kesalahan dan kompensasi tidak memerlukan konsep baru, tetapi beberapa perilaku orkestrasi perlu diketahui.

Nota

Model pemrograman Node.js versi 4 untuk Azure Functions umumnya tersedia. Model v4 dirancang untuk memberikan pengalaman yang lebih fleksibel dan intuitif bagi pengembang JavaScript dan TypeScript. Untuk informasi selengkapnya tentang perbedaan antara v3 dan v4, lihat panduan migrasi.

Dalam cuplikan kode berikut, JavaScript (PM4) menunjukkan model pemrograman v4, pengalaman baru.

Aplikasi yang menggunakan layanan cloud perlu menangani kegagalan, dan percobaan ulang sisi klien adalah bagian penting dari desain. SDK Tugas Tahan Lama mencakup dukungan untuk penanganan kesalahan, percobaan ulang, dan batas waktu untuk membantu Anda membangun alur kerja yang kuat.

Menangani kesalahan dalam fungsi aktivitas dan sub-orkestrasi

Dalam Durable Functions, pengecualian yang tidak tertangani yang terjadi dalam aktivitas fungsi atau sub-orkestrasi diarahkan kembali ke fungsi orkestrator menggunakan tipe pengecualian standar.

Fungsi orkestrator berikut mentransfer dana antara dua akun:

Model pekerja terisolasi

Dalam Durable Functions C# Terisolasi, pengecualian yang tidak tertangani muncul sebagai TaskFailedException.

Pesan pengecualian biasanya mengidentifikasi fungsi aktivitas atau sub-orkestrasi mana yang menyebabkan kegagalan. Untuk mengakses informasi kesalahan yang lebih rinci, periksa properti FailureDetails .

[FunctionName("TransferFunds")]
public static async Task Run(
    [OrchestrationTrigger] TaskOrchestrationContext context, TransferOperation transferDetails)
{
    await context.CallActivityAsync("DebitAccount",
        new
        {
            Account = transferDetails.SourceAccount,
            Amount = transferDetails.Amount
        });

    try
    {
        await context.CallActivityAsync("CreditAccount",
            new
            {
                Account = transferDetails.DestinationAccount,
                Amount = transferDetails.Amount
            });
    }
    catch (TaskFailedException)
    {
        // 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
            });
    }
}

Nota

  • Pesan pengecualian biasanya mengidentifikasi fungsi aktivitas atau sub-orkestrasi mana yang menyebabkan kegagalan. Untuk mengakses informasi kesalahan yang lebih rinci, periksa properti FailureDetails tersebut.
  • Secara default, FailureDetails menyertakan jenis kesalahan, pesan kesalahan, pelacakan tumpukan, dan pengecualian berlapis (masing-masing direpresentasikan sebagai objek rekursif FailureDetails ). Untuk menyertakan properti pengecualian kustom dalam output kegagalan, lihat Sertakan Properti Pengecualian Kustom untuk FailureDetails (.NET Terisolasi).

Penting

Catatan migrasi (dalam proses untuk diisolasi): Dalam model dalam proses, FunctionFailedException.InnerException berisi objek pengecualian asli yang dilemparkan oleh aktivitas, yang dapat Anda transmisikan dan periksa secara langsung. Dalam model pekerja yang terisolasi, TaskFailedExceptiontidak menyertakan pengecualian asli sebagai InnerException. Sebaliknya, detail kesalahan hanya tersedia melalui FailureDetails properti , yang menyediakan properti berbasis string (ErrorType, , ErrorMessageStackTrace). Anda tidak dapat melemparkan atau mengakses objek pengecualian asli secara langsung. Gunakan FailureDetails.IsCausedBy<T>() untuk memeriksa jenis pengecualian asli.


Model dalam proses

Dalam C# Durable Functions in-process, pengecualian yang tidak tertangani dilemparkan sebagai FunctionFailedException.

Pesan pengecualian biasanya menyertakan fungsi aktivitas atau sub-orkestrasi yang gagal. Untuk detailnya, periksa 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
            });
    }
}

Nota

Contoh C# sebelumnya menggunakan Durable Functions 2.x. Untuk Durable Functions 1.x, gunakan DurableOrchestrationContext alih-alih IDurableOrchestrationContext. Untuk perbedaan versi, lihat artikel versi Durable Functions.

Jika panggilan fungsi CreditAccount pertama gagal, fungsi orkestrator mengkompensasi dengan memberi kredit dana kembali ke akun sumber.

Dalam Durable Task SDK, pengecualian yang tidak tertangani yang terjadi dalam aktivitas atau sub-orkestrasi diarahkan kembali ke orkestrator menggunakan tipe TaskFailedException. Properti pengecualian FailureDetails memberikan informasi terperinci tentang kegagalan tersebut.

using Microsoft.DurableTask;

[DurableTask(nameof(TransferFundsOrchestration))]
public class TransferFundsOrchestration : TaskOrchestrator<TransferOperation, string>
{
    public override async Task<string> RunAsync(
        TaskOrchestrationContext context, TransferOperation transfer)
    {
        await context.CallActivityAsync(
            nameof(DebitAccountActivity),
            new AccountOperation { Account = transfer.SourceAccount, Amount = transfer.Amount });

        try
        {
            await context.CallActivityAsync(
                nameof(CreditAccountActivity),
                new AccountOperation { Account = transfer.DestinationAccount, Amount = transfer.Amount });
        }
        catch (TaskFailedException ex)
        {
            // Log the failure details
            var details = ex.FailureDetails;

            // Compensate by refunding the source account
            await context.CallActivityAsync(
                nameof(CreditAccountActivity),
                new AccountOperation { Account = transfer.SourceAccount, Amount = transfer.Amount });

            return $"Transfer failed: {details.ErrorMessage}. Compensation completed.";
        }

        return "Transfer completed successfully";
    }
}

Jika aktivitas CreditAccount gagal, orkestrator menangkap pengecualian dan mengimbangi dengan mengkreditkan dana kembali ke akun sumber.

Menangani kesalahan dengan beberapa panggilan aktivitas (fan-out/fan-in)

Saat Anda menggunakan Task.WhenAll untuk menjalankan beberapa panggilan aktivitas secara paralel (pola fan-out/fan-in) dan satu atau beberapa aktivitas gagal, await hanya melemparkan pengecualian pertama. Untuk mengakses semua kegagalan, periksa properti Exception pada objek Task yang dikembalikan oleh Task.WhenAll.

Model pekerja terisolasi
var tasks = new[]
{
    context.CallActivityAsync("Activity1", input1),
    context.CallActivityAsync("Activity2", input2),
    context.CallActivityAsync("Activity3", input3),
};

var allTask = Task.WhenAll(tasks);
try
{
    await allTask;
}
catch (TaskFailedException)
{
    // 'await' rethrows only the first exception. To inspect all failures,
    // check allTask.Exception, which is an AggregateException.
    if (allTask.Exception != null)
    {
        foreach (var inner in allTask.Exception.InnerExceptions)
        {
            if (inner is TaskFailedException taskFailed)
            {
                // Use taskFailed.FailureDetails to inspect error details
                var errorType = taskFailed.FailureDetails.ErrorType;
                var errorMessage = taskFailed.FailureDetails.ErrorMessage;
            }
        }
    }
}

Model dalam proses
var tasks = new[]
{
    context.CallActivityAsync("Activity1", input1),
    context.CallActivityAsync("Activity2", input2),
    context.CallActivityAsync("Activity3", input3),
};

var allTask = Task.WhenAll(tasks);
try
{
    await allTask;
}
catch (FunctionFailedException)
{
    // 'await' rethrows only the first exception. To inspect all failures,
    // check allTask.Exception, which is an AggregateException.
    if (allTask.Exception != null)
    {
        foreach (var inner in allTask.Exception.InnerExceptions)
        {
            if (inner is FunctionFailedException funcFailed)
            {
                // Use funcFailed.InnerException to access the original exception
            }
        }
    }
}

Menangani kesalahan dalam fungsi entitas

Penanganan pengecualian dalam fungsi entitas tergantung pada model hosting Durable Functions:

Model pekerja terisolasi

Dalam Durable Functions C# terisolasi, runtime membungkus pengecualian fungsi entitas dalam EntityOperationFailedException. Untuk mendapatkan detail pengecualian asli, cek properti FailureDetails.

[Function(nameof(MyOrchestrator))]
public static async Task<List<string>> MyOrchestrator(
   [Microsoft.Azure.Functions.Worker.OrchestrationTrigger] TaskOrchestrationContext context)
{
    var entityId = new Microsoft.DurableTask.Entities.EntityInstanceId(nameof(Counter), "myCounter");
    try
    {
        await context.Entities.CallEntityAsync(entityId, "Add", 1);
    }
    catch (EntityOperationFailedException ex)
    {
        // Add your error handling
    }

    return new List<string>();
}

Model dalam proses

Dalam Durable Functions dengan C# dalam proses, fungsi entitas mengembalikan jenis pengecualian aslinya ke orkestrator.

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

Pengulangan otomatis pada kegagalan

Saat Anda memanggil fungsi aktivitas atau fungsi sub-orkestrasi, pastikan untuk menetapkan kebijakan pengulangan otomatis. Contoh berikut memanggil fungsi hingga tiga kali dan menunggu lima detik di antara percobaan ulang:

Model pekerja terisolasi
[FunctionName("TimerOrchestratorWithRetry")]
public static async Task Run([OrchestrationTrigger] TaskOrchestrationContext context)
{
    var options = TaskOptions.FromRetryPolicy(new RetryPolicy(
        maxNumberOfAttempts: 3,
        firstRetryInterval: TimeSpan.FromSeconds(5)));

    await context.CallActivityAsync("FlakyFunction", options: options);

    // ...
}

Model dalam proses
[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);

    // ...
}

Nota

Contoh C# sebelumnya adalah untuk Durable Functions 2.x. Untuk Durable Functions 1.x, Anda harus menggunakan DurableOrchestrationContext alih-alih IDurableOrchestrationContext. Untuk informasi selengkapnya tentang perbedaan antar versi, lihat artikel versi Durable Functions .

Panggilan fungsi aktivitas dalam contoh sebelumnya menggunakan parameter untuk mengonfigurasi kebijakan coba lagi otomatis. Sesuaikan kebijakan dengan opsi ini:

  • Max number of attempts:Jumlah maksimal dari upaya yang dicoba. Jika diatur ke 1, tidak ada percobaan ulang yang terjadi.
  • Interval percobaan pertama: Jumlah waktu untuk menunggu sebelum upaya coba lagi pertama.
  • Koefisien backoff: Koefisien yang digunakan untuk menentukan nilai peningkatan dari backoff. Secara default ke 1.
  • Interval coba lagi maksimum: Jumlah waktu maksimum untuk menunggu di antara upaya coba lagi.
  • Batas waktu percobaan ulang: Waktu maksimum untuk mencoba kembali. Secara default, percobaan ulang berlanjut tanpa batas waktu.

SDK Durable Task mencakup metode penjadwalan alternatif yang mengulangi aktivitas yang gagal berdasarkan strategi yang disediakan. Metode ini berguna untuk aktivitas yang membaca data dari layanan web atau melakukan penulisan idempotensi ke database.

using Microsoft.DurableTask;

[DurableTask(nameof(OrchestratorWithRetry))]
public class OrchestratorWithRetry : TaskOrchestrator<string, string>
{
    public override async Task<string> RunAsync(
        TaskOrchestrationContext context, string input)
    {
        // Configure retry policy
        var retryPolicy = new RetryPolicy(
            maxNumberOfAttempts: 3,
            firstRetryInterval: TimeSpan.FromSeconds(5),
            backoffCoefficient: 2.0,
            maxRetryInterval: TimeSpan.FromMinutes(1),
            retryTimeout: TimeSpan.FromMinutes(5));

        var options = TaskOptions.FromRetryPolicy(retryPolicy);

        // Call activity with automatic retry
        string result = await context.CallActivityAsync<string>(
            nameof(UnreliableActivity), input, options);

        return result;
    }
}

Opsi kebijakan pengulangan adalah:

  • Jumlah maksimum upaya: Jumlah maksimum upaya coba lagi. Jika diatur ke 1, tidak ada percobaan ulang yang terjadi.
  • Interval percobaan pertama: Jumlah waktu untuk menunggu sebelum upaya coba lagi pertama.
  • Koefisien backoff: Koefisien yang digunakan untuk menentukan nilai peningkatan dari backoff. Secara default ke 1.
  • Interval coba lagi maksimum: Jumlah waktu maksimum untuk menunggu di antara upaya coba lagi.
  • Coba lagi waktu habis: Jumlah maksimum waktu yang harus dihabiskan untuk melakukan pengulangan.

Penangan ulang khusus

Dalam .NET dan Java, terapkan penangan ulang dalam kode saat kebijakan ulang deklaratif kurang ekspresif. Dalam bahasa lain, terapkan logika coba lagi dengan menggunakan perulangan, penanganan pengecualian, dan timer untuk menunda antara percobaan ulang.

Model pekerja terisolasi
TaskOptions retryOptions = TaskOptions.FromRetryHandler(retryContext =>
{
    // Don't retry anything that derives from ApplicationException
    if (retryContext.LastFailure.IsCausedBy<ApplicationException>())
    {
        return false;
    }

    // Quit after N attempts
    return retryContext.LastAttemptNumber < 3;
});

try
{
    await ctx.CallActivityAsync("FlakeyActivity", options: retryOptions);
}
catch (TaskFailedException)
{
    // Case when the retry handler returns false...
}

Model dalam proses
RetryOptions retryOptions = new RetryOptions(
    firstRetryInterval: TimeSpan.FromSeconds(5),
    maxNumberOfAttempts: int.MaxValue)
{
    Handle = exception =>
    {
        // Return true to handle and retry, or false to throw.
        if (exception is TaskFailedException failure)
        {
            // Exceptions from task activities are always this type. Inspect the
            // inner exception for more details.
        }

        return false;
    }
};

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

Penangan ulang khusus

Di .NET dan Java, terapkan penangan ulang dalam kode untuk mengontrol logika pengulangan. Pendekatan ini berguna ketika kebijakan deklaratif untuk upaya ulang tidak cukup ekspresif.

using Microsoft.DurableTask;

[DurableTask(nameof(OrchestratorWithCustomRetry))]
public class OrchestratorWithCustomRetry : TaskOrchestrator<string, string>
{
    public override async Task<string> RunAsync(
        TaskOrchestrationContext context, string input)
    {
        // Custom retry handler with conditional logic
        TaskOptions retryOptions = TaskOptions.FromRetryHandler(retryContext =>
        {
            // Don't retry if it's a validation error
            if (retryContext.LastFailure.IsCausedBy<ArgumentException>())
            {
                return false;
            }

            // Retry up to 5 times for transient errors
            return retryContext.LastAttemptNumber < 5;
        });

        try
        {
            return await context.CallActivityAsync<string>(
                nameof(UnreliableActivity), input, retryOptions);
        }
        catch (TaskFailedException)
        {
            // All retries exhausted
            return "Operation failed after all retries";
        }
    }
}

Batas waktu fungsi

Jika panggilan fungsi memerlukan waktu terlalu lama, tetapkan batas waktu dalam fungsi orkestrator. Buat timer tahan lama dengan pemilih any tugas, seperti dalam contoh berikut:

Model pekerja terisolasi
[Function("TimerOrchestrator")]
public static async Task<bool> Run([OrchestrationTrigger] TaskOrchestrationContext 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;
        }
    }
}

Model dalam proses
[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;
        }
    }
}

Nota

Contoh C# sebelumnya adalah untuk Durable Functions 2.x. Untuk Durable Functions 1.x, Anda harus menggunakan DurableOrchestrationContext alih-alih IDurableOrchestrationContext. Untuk informasi selengkapnya tentang perbedaan antar versi, lihat artikel versi Durable Functions .

Nota

Mekanisme ini tidak mengakhiri eksekusi fungsi aktivitas yang sudah berlangsung. Ini memungkinkan fungsi orkestrator mengabaikan hasilnya dan melanjutkan. Untuk informasi selengkapnya, lihat Timer.

Batas waktu aktivitas

Apabila panggilan aktivitas memakan waktu terlalu lama, Anda bisa berhenti menunggunya. Buat timer tahan lama dan adu kecepatan dengan tugas aktivitas.

using Microsoft.DurableTask;
using System;
using System.Threading;
using System.Threading.Tasks;

[DurableTask(nameof(OrchestratorWithTimeout))]
public class OrchestratorWithTimeout : TaskOrchestrator<string, bool>
{
    public override async Task<bool> RunAsync(
        TaskOrchestrationContext context, string input)
    {
        TimeSpan timeout = TimeSpan.FromSeconds(30);
        DateTime deadline = context.CurrentUtcDateTime.Add(timeout);

        using var cts = new CancellationTokenSource();
        Task activityTask = context.CallActivityAsync(nameof(SlowActivity), input);
        Task timeoutTask = context.CreateTimer(deadline, cts.Token);

        Task winner = await Task.WhenAny(activityTask, timeoutTask);
        if (winner == activityTask)
        {
            // Activity completed in time - cancel the timer
            cts.Cancel();
            return true;
        }
        else
        {
            // Timeout occurred
            return false;
        }
    }
}

Nota

Mekanisme ini tidak mengakhiri eksekusi aktivitas yang sudah berlangsung. Ini memungkinkan orkestrator mengabaikan hasilnya dan melanjutkan. Untuk informasi selengkapnya, lihat dokumentasi Timer.

Pengecualian yang Tidak Tertangani

Jika fungsi orkestrator gagal dengan pengecualian yang tidak tertangani, runtime mencatat detail pengecualian, dan instans selesai dengan Failed status.

Sertakan properti pengecualian kustom untuk FailureDetails (.NET Terisolasi)

Dalam alur kerja Tugas Tahan Lama yang menggunakan model .NET Terisolasi, kegagalan tugas diserialisasikan ke objek FailureDetails. Secara default, objek menyertakan bidang ini:

  • ErrorType—Nama jenis pengecualian
  • Message—Pesan pengecualian
  • StackTrace—Pelacakan tumpukan berseri
  • InnerFailure—Objek bertingkat FailureDetails untuk pengecualian internal

Dimulai dengan Microsoft. Azure. Functions.Worker.Extensions.DurableTask v1.9.0, Anda dapat memperluas perilaku ini dengan menerapkan IExceptionPropertiesProvider (ditentukan dalam paket Microsoft.DurableTask.Worker mulai dari v1.16.1). Penyedia ini menentukan jenis dan properti pengecualian yang akan disertakan dalam kamus FailureDetails.Properties.

Nota

  • Fitur ini hanya tersedia di .NET Isolated . Dukungan untuk Java belum tersedia.
  • Pastikan Anda menggunakan Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0 atau yang lebih baru.
  • Pastikan Anda menggunakan Microsoft.DurableTask.Worker v1.16.1 atau yang lebih baru.

Menerapkan penyedia properti pengecualian

Terapkan kustom IExceptionPropertiesProvider untuk mengekstrak dan mengembalikan properti yang dipilih untuk pengecualian yang Anda pedulikan. Kamus yang dikembalikan diserialisasikan ke Properties bidang FailureDetails ketika jenis pengecualian yang cocok dilemparkan.

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

Mendaftarkan penyedia

Di Program.cs, daftarkan IExceptionPropertiesProvider kustom Anda di host pekerja terisolasi .NET Anda:

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

Setelah Anda mendaftarkan penyedia, pengecualian apa pun yang cocok dengan jenis yang ditangani secara otomatis menyertakan properti yang dikonfigurasi dalam .FailureDetails

Output Detail Kegagalan Sampel

Ketika terjadi pengecualian yang cocok dengan konfigurasi penyedia Anda, orkestrasi menerima objek berseri FailureDetails seperti ini:

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

Pengecualian yang Tidak Tertangani

Jika orkestrator gagal karena pengecualian yang tidak tertangani, runtime mencatat detail pengecualian, dan instans selesai dengan Failed status. TaskFailedException memiliki FailureDetails properti yang menyertakan jenis kesalahan, pesan, dan jejak tumpukan.

Langkah berikutnya

Mendiagnosis masalah