カスタム オーケストレーションの状態を設定してクエリを実行する

カスタム オーケストレーションの状態を使用すると、任意の JSON メタデータを実行中のオーケストレーション インスタンスにアタッチして、外部クライアントがいつでもクエリを実行できるようにします。 次の操作を行う必要がある場合は、カスタム状態を使用します。

  • 処理中に進行状況を報告 する — オーケストレーションが完了するのを待たずに、どのステップに達したかを UI に表示します。
  • 動的データを呼び出し元に返します。オーケストレーションの実行中に、推奨事項、割引情報、または次の手順を示します。
  • 外部システムと連携 する - 他のサービスや人間のオペレーターがポーリングして操作できる状態を共有します。

Warnung

カスタム 状態ペイロードは、UTF-16 JSON テキストの 16 KB に制限されています。 より大きなペイロードが必要な場合は、外部ストレージを使用し、代わりに参照 (BLOB URL など) をカスタム状態に格納します。

Azure Functionsでは、この状態は、オーケストレーション クライアント オブジェクトの HTTP GetStatus API または同等の SDK API を介して使用できます。

Durable Task SDK では、この状態は、DurableTaskClient のオーケストレーション状態クエリ API (たとえば、.NET の GetInstanceAsync、Java の getInstanceMetadata) で使用できます。

Important

現在、PowerShell Durable Task SDK は使用できません。

カスタムオーケストレーション状態の活用事例のサンプル

次の表は、一般的なパターンをまとめたものです。 ユース ケースを選択して、対応する例にジャンプします。

利用シーン 説明
オーケストレーションの進行状況を視覚化する クライアントが進行状況インジケーターを表示できるように、各アクティビティの後に文字列またはオブジェクトを更新します。
動的メタデータをクライアントに返す カスタム サーバー側エンドポイントを必要とせずにクライアントがレンダリングする構造化データ (推奨事項など) を設定します。
アクション可能なデータをクライアントに提供する 予約 URL、割引情報、またはクライアントが実行する次のステップの指示を、オーケストレーションが外部イベントを待機している間に表示します。
カスタム状態を照会する HTTP API または SDK 呼び出しを使用して、クライアントからカスタム状態値を読み取ります。

オーケストレーションの進行状況を視覚化する

このパターンでは、オーケストレーターは、各アクティビティが完了した後に SetCustomStatus (または言語で同等のもの) を呼び出し、最後に完了した都市の名前で状態を更新します。 クライアントは、状態エンドポイントをポーリングし、現在の値を読み取り、UI の進行状況インジケーターを更新します。

次の例では、Durable Functions HTTP 状態エンドポイントを使用した進行状況の共有を示します。

これらの例は、Durable Functions 2.x 用に記述されており、Durable Functions 1.x と互換性がありません。 バージョン間の相違点の詳細については、Durable Functions のバージョンに関する記事を参照してください。

[FunctionName("E1_HelloSequence")]
public static async Task<List<string>> Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var outputs = new List<string>();

    outputs.Add(await context.CallActivityAsync<string>("E1_SayHello", "Tokyo"));
    context.SetCustomStatus("Tokyo");
    outputs.Add(await context.CallActivityAsync<string>("E1_SayHello", "Seattle"));
    context.SetCustomStatus("Seattle");
    outputs.Add(await context.CallActivityAsync<string>("E1_SayHello", "London"));
    context.SetCustomStatus("London");

    // returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
    return outputs;
}

[FunctionName("E1_SayHello")]
public static string SayHello([ActivityTrigger] string name)
{
    return $"Hello {name}!";
}

次のサンプルは、Durable Task SDK クライアント API を使用した進行状況の共有を示しています。

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

public class HelloCities : TaskOrchestrator<object?, string>
{
    public override async Task<string> RunAsync(TaskOrchestrationContext context, object? input)
    {
        string result = "";

        result += await context.CallActivityAsync<string>("SayHello", "Tokyo") + ", ";
        context.SetCustomStatus("Tokyo");

        result += await context.CallActivityAsync<string>("SayHello", "London") + ", ";
        context.SetCustomStatus("London");

        result += await context.CallActivityAsync<string>("SayHello", "Seattle");
        context.SetCustomStatus("Seattle");

        return result;
    }
}

クライアントはオーケストレーション メタデータをポーリングし、 CustomStatus フィールドが "London"に設定されるまで待機できます。

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

string instanceId = await client.ScheduleNewOrchestrationInstanceAsync("HelloCities");

OrchestrationMetadata metadata = await client.WaitForInstanceStartAsync(instanceId, getInputsAndOutputs: true);
while (metadata.SerializedCustomStatus is null || metadata.ReadCustomStatusAs<string>() != "London")
{
    await Task.Delay(200);
    metadata = await client.GetInstanceAsync(instanceId, getInputsAndOutputs: true) ?? metadata;
}

次のクライアント コードは、オーケストレーションの状態をポーリングし、 CustomStatus"London" に設定されるまで待機してから応答を返します。

[FunctionName("HttpStart")]
public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req,
    [DurableClient] IDurableOrchestrationClient starter,
    string functionName,
    ILogger log)
{
    // Function input comes from the request content.
    dynamic eventData = await req.Content.ReadAsAsync<object>();
    string instanceId = await starter.StartNewAsync(functionName, (string)eventData);

    log.LogInformation($"Started orchestration with ID = '{instanceId}'.");

    DurableOrchestrationStatus durableOrchestrationStatus = await starter.GetStatusAsync(instanceId);
    while (durableOrchestrationStatus.CustomStatus.ToString() != "London")
    {
        await Task.Delay(200);
        durableOrchestrationStatus = await starter.GetStatusAsync(instanceId);
    }

    HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new StringContent(JsonConvert.SerializeObject(durableOrchestrationStatus))
    };

    return httpResponseMessage;
  }
}

動的メタデータをクライアントに返す

カスタム オーケストレーションの状態を使用すると、個別のエンドポイントを構築することなく、構造化されたデータ (パーソナライズされた推奨事項など) をクライアントに返すことができます。 オーケストレーターは入力に基づいてカスタム状態を設定し、クライアントは標準状態 API を介してそれを読み取ります。 これにより、すべてのロジックがサーバー側に留まりますが、クライアント側コードのジェネリックが維持されます。

[FunctionName("CityRecommender")]
public static void Run(
  [OrchestrationTrigger] IDurableOrchestrationContext context)
{
  int userChoice = context.GetInput<int>();

  switch (userChoice)
  {
    case 1:
    context.SetCustomStatus(new
    {
      recommendedCities = new[] {"Tokyo", "Seattle"},
      recommendedSeasons = new[] {"Spring", "Summer"}
     });
      break;
    case 2:
      context.SetCustomStatus(new
      {
                recommendedCities = new[] {"Seattle", "London"},
        recommendedSeasons = new[] {"Summer"}
      });
        break;
      case 3:
      context.SetCustomStatus(new
      {
                recommendedCities = new[] {"Tokyo", "London"},
        recommendedSeasons = new[] {"Spring", "Summer"}
      });
        break;
  }

  // Wait for user selection and refine the recommendation
}
using System.Threading.Tasks;
using Microsoft.DurableTask;

public class CityRecommender : TaskOrchestrator<int, object?>
{
    public override Task<object?> RunAsync(TaskOrchestrationContext context, int userChoice)
    {
        switch (userChoice)
        {
            case 1:
                context.SetCustomStatus(new
                {
                    recommendedCities = new[] { "Tokyo", "Seattle" },
                    recommendedSeasons = new[] { "Spring", "Summer" },
                });
                break;
            case 2:
                context.SetCustomStatus(new
                {
                    recommendedCities = new[] { "Seattle", "London" },
                    recommendedSeasons = new[] { "Summer" },
                });
                break;
            case 3:
                context.SetCustomStatus(new
                {
                    recommendedCities = new[] { "Tokyo", "London" },
                    recommendedSeasons = new[] { "Spring", "Summer" },
                });
                break;
        }

        // Wait for user selection and refine the recommendation
        return Task.FromResult<object?>(null);
    }
}

アクション可能なデータをクライアントに提供する

このパターンでは、オーケストレーターはカスタム状態を通じて時間の機密情報 (割引、予約 URL、タイムアウトなど) を表示し、一時停止して外部イベントを待機します。 クライアントは、カスタムステータスを読み取ってオファーを表示し、ユーザーが行動したときに確認イベントをオーケストレーターに送り返します。

[FunctionName("ReserveTicket")]
public static async Task<bool> Run(
  [OrchestrationTrigger] IDurableOrchestrationContext context)
{
  string userId = context.GetInput<string>();

  int discount = await context.CallActivityAsync<int>("CalculateDiscount", userId);

  context.SetCustomStatus(new
  {
    discount = discount,
    discountTimeout = 60,
    bookingUrl = "https://www.myawesomebookingweb.com",
  });

  bool isBookingConfirmed = await context.WaitForExternalEvent<bool>("BookingConfirmed");

  context.SetCustomStatus(isBookingConfirmed
    ? new {message = "Thank you for confirming your booking."}
    : new {message = "The booking was not confirmed on time. Please try again."});

  return isBookingConfirmed;
}
using System.Threading.Tasks;
using Microsoft.DurableTask;

public class ReserveTicket : TaskOrchestrator<string, bool>
{
    public override async Task<bool> RunAsync(TaskOrchestrationContext context, string userId)
    {
        int discount = await context.CallActivityAsync<int>("CalculateDiscount", userId);

        context.SetCustomStatus(new
        {
            discount,
            discountTimeout = 60,
            bookingUrl = "https://www.myawesomebookingweb.com",
        });

        bool isBookingConfirmed = await context.WaitForExternalEvent<bool>("BookingConfirmed");
        context.SetCustomStatus(isBookingConfirmed
            ? new { message = "Thank you for confirming your booking." }
            : new { message = "The booking was not confirmed on time. Please try again." });

        return isBookingConfirmed;
    }
}

カスタム オーケストレーションの状態を照会する

前の例では、オーケストレーター コードからカスタム状態を設定する方法を示します。 このセクションでは、外部クライアントがその値を読み取る方法について説明します。

オーケストレーターが SetCustomStatus を呼び出した後、外部クライアントは組み込みのDurable Functions HTTP API を使用して値を照会できます。 例えば次が挙げられます。

GET /runtime/webhooks/durabletask/instances/instance123

応答には、ランタイム メタデータと共に customStatus フィールドが含まれます。

{
  "runtimeStatus": "Running",
  "input": null,
  "customStatus": { "nextActions": ["A", "B", "C"], "foo": 2 },
  "output": null,
  "createdTime": "2019-10-06T18:30:24Z",
  "lastUpdatedTime": "2019-10-06T19:40:30Z"
}

オーケストレーション クライアント SDK を使用して、プログラムでカスタム状態のクエリを実行することもできます。 完全なリファレンスについては、「 クエリ インスタンス」を参照してください。

Durable Task SDK では、組み込みの HTTP 状態エンドポイントは提供されません。 代わりに、 DurableTaskClientのオーケストレーション インスタンス メタデータ API を使用して、プログラムでカスタム状態を照会します。

using Microsoft.DurableTask.Client;

OrchestrationMetadata? metadata = await client.GetInstanceAsync(instanceId, getInputsAndOutputs: true);
string? customStatusJson = metadata?.SerializedCustomStatus;

Warnung

カスタム 状態ペイロードは、UTF-16 JSON テキストの 16 KB に制限されています。

次のステップ