しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

あえて Durable Functions でオーケストレーターの完了まで待つ Web API を作る

通常 HttpTrigger で起動する Durable Functions を作成すると CreateCheckStatusResponse を使って、インスタンス管理用のエンドポイントを含んだレスポンスを返すと思います。

テンプレートから作成したコードも以下のようになっているはずです。

[FunctionName("Function1_HttpStart")]
public static async Task<HttpResponseMessage> HttpStart(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")]HttpRequestMessage req,
    [OrchestrationClient]DurableOrchestrationClient starter,
    ILogger log)
{
    // Function input comes from the request content.
    string instanceId = await starter.StartNewAsync("Function1", null);

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

    return starter.CreateCheckStatusResponse(req, instanceId);
}

実際にこの API を実行すると 202 Accepted が返ってきて、後は statusQueryGetUri を使って完了したか、それとも失敗したかを確認します。

f:id:shiba-yan:20181209161147p:plain

すぐにレスポンスが返ってきて、その後は非同期で処理を待てば良いのでスケーリングで有利な面が多いですが、これがブラウザから実行しようとする場合には正直面倒です。

Long-running なオーケストレーターの場合は厳しいですが、数秒で完了するようなものは API のレスポンスとしてオーケストレーターの実行結果を返せた方が便利ですよね。ちゃんとその為のメソッドが Durable Functions には用意されていました。

コードの変更自体は非常に簡単で、これまで呼び出されていた CreateCheckStatusResponse の代わりに WaitForCompletionOrCreateCheckStatusResponseAsync を実行するようにすると、引数で指定したインスタンスの完了を待つようになります。

処理としては定期的にインスタンスの状態をチェックしているみたいなので、用意されているオーバーロードのメソッドを使って調整する必要はあると思います。

[FunctionName("Function1_HttpStart")]
public static async Task<HttpResponseMessage> HttpStart(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")]HttpRequestMessage req,
    [OrchestrationClient]DurableOrchestrationClient starter,
    ILogger log)
{
    // Function input comes from the request content.
    string instanceId = await starter.StartNewAsync("Function1", null);

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

    return await starter.WaitForCompletionOrCreateCheckStatusResponseAsync(req, instanceId);
}

このように書き換えて実行すると、オーケストレーターの実行結果がレスポンスとして返ってきます。

f:id:shiba-yan:20181209161200p:plain

これで挙動としては極めて一般的な Web API と同じようになりました。結果としては同期 API と同じ挙動となりますが、ちゃんとオーケストレーターやアクティビティは非同期で動作し続けます。

そしてオーケストレーターが失敗した場合には以下のようなエラーレスポンスが返ります。

f:id:shiba-yan:20181209233316p:plain

デフォルトのタイムアウト時間である 10 秒を過ぎると、通常と同じようなインスタンス管理用のエンドポイントが返るようですが、ちゃんと動いていない気もしました。

処理時間が大体わかっている場合はタイムアウトを長くしても問題ないでしょう。

今回はブラウザから実行する必要があった API だったので、Durable Functions の特徴と既存のアクティビティを活かしながら、シンプルな Web API を素早く用意出来ました。