Azure Functions v2 の Breaking changes に合わせて Durable Functions v1.6 がリリースされました。
今回の Breaking changes は Run From Package で配布している場合に地味に大変でしたが、その話はまた今後どこかで書こうかと思いますが需要なさそう。
If you're running Durable Functions on top of Azure Functions v2.0, this rollout will impact you! Fortunately, we have an new v1.6.0 release which is compatible with these runtime changes. Release notes with all the usual details are here: https://t.co/6MGBjOb7iD https://t.co/Lmg8NWLbQI
— Chris Gillum (@cgillum) 2018年8月30日
オーケストレーションのパフォーマンスが改善してたりするみたいなので、とりあえずアップデートはしておいた方が良いでしょう。Azure Functions v2 を使ってる場合は必須ですよ。
さて、メインは新しい Functions Runtime への対応っぽいですが、リリースノートには非常に興味深い機能が追加されていると書かれていました。失敗したオーケストレーションをやり直す機能です。
Rewind failed orchestrations (preview, #301): A new API has been added which allows you to "rewind" failed orchestration instances back to their previously good state. This can be a life-saver when you have long-running orchestrations that fail in unexpected ways.
Release Durable Functions v1.6.0 Release · Azure/azure-functions-durable-extension · GitHub
まだプレビューなので不具合があるかも知れないですが、これは待望の機能です。
これまで Durable Functions は Event Sourcing で実行の履歴が全て管理されているのに、失敗した場合には新しく最初からやり直すしか方法が無い点に不満を持っていました。
ちなみにこの Rewind 機能を実装したのはインターンの方らしいです。素晴らしいコントリビューションとして言いようがないですね。
使い方はまだドキュメントが公開されてないですが、実装を見ればすぐにわかりました。普通にオーケストレーションを開始すると、レスポンスに rewindPostUri
が追加されています。
オーケストレーションが失敗した場合、このエンドポイントを POST で叩くと再実行されます。
とりあえず実際に動作を試して見ました。サンプルなので適当なオーケストレーターとアクティビティを用意して、2 つ目のアクティビティはわざと例外を投げて落ちるようにしています。
[FunctionName("Function1")] public static async Task RunOrchestrator([OrchestrationTrigger] DurableOrchestrationContext context) { var value = context.CurrentUtcDateTime.Ticks.ToString(); await context.CallActivityAsync("Function1_Task1", value); await context.CallActivityAsync("Function1_Task2", value); await context.CallActivityAsync("Function1_Task3", value); } [FunctionName("Function1_Task1")] public static void Task1([ActivityTrigger] string value, ILogger log) { log.LogInformation($"Task1 running: Input {value}."); } [FunctionName("Function1_Task2")] public static void Task2([ActivityTrigger] string value, ILogger log) { throw new Exception(); //log.LogInformation($"Task2 running: Input {value}."); } [FunctionName("Function1_Task3")] public static void Task3([ActivityTrigger] string value, ILogger log) { log.LogInformation($"Task3 running: Input {value}."); }
アクティビティが失敗して、オーケストレーションが Failed になったのを確認後、Rewind API を叩いて再実行をリクエストします。reason
には適当に理由を入れておけば、ログに出力されるので分かりやすいです。
実際にどのように実行されているかはログを見ないと理解しにくかったので、Application Insights でクエリを書いて分かりやすく並べました。まずは初回の失敗したログです。
Function1_Task2
が例外を投げたので、オーケストレーターも Failed していることが確認できますね。
次に Rewind API の実行後のログです。Instance Id は Rewind 後も変化しないので追跡が可能です。
オーケストレーターのステータスが Rewound に変化して、再実行が行われています。
前回の実行で成功した Function1_Task1
は再実行されずに Function1_Task2
から実行されていることが確認できます。もちろん状態も前回のままなのでオーケストレーターの作法に従って CurrentUtcDateTime
などを使っていれば、安全に再実行が行えます。
アクティビティのコードに不具合があった場合には、修正をデプロイ後 Rewind API を使えば失敗した処理から続きを実行出来るので、リカバリの選択肢が広がりそうです。もちろん外部の API を叩いている場合も同様に使えるでしょう。
個人的にはこれで Durable Functions は完成形になったのではないかと思っています。使わないと損します。