[Advent Calendar 2017 Day1] Durable Functions ことはじめ

4 minute read

この記事は 旧 Japan Azure PaaS Support Blog からのコピーとなります。
記載されている内容は 2017 年 12 月 1 日 時点のものとなり、現時点におきましては変更されている可能性がありますので、ご注意下さい。

こちらの記事は、Qiita に掲載した Microsoft Azure Tech Advent Calendar 2017 の企画に基づき、執筆した内容となります。
カレンダーに掲載された記事の一覧は、 こちら よりご確認ください。

こんにちは。Azure CIE サポート 今村 です。
今回は、Azure Functions のプレビュー機能である Durable Functions について、まだあまり日本語の記事が多くないと思いましたので、2017 年のアドベント カレンダーの題材としてご紹介ができればと思います。

Durable Functions は、既存の関数を組み合わせて、長時間動作する関数をデプロイしたり、あるいは状態を持たせた関数を作成することができる機能です。
これまでの関数では、キュー ストレージ等外部サービスを利用することで、擬似的に状態をもたせたり、関数の間をチェーンしていましたが、Durable Functions を利用することで、このような仕組みをワンストップで実現することができるようになります。

1. Durable Task Framework について


Durable Functions は、旧来よりオープンソースで開発が行われていた Durable Task Framework という技術を Functions に適用した機能となります。Durable Task Framework は、もともとトランザクションの処理をソースコード側でコントロールすることを目的として、作られたフレームワークです。

"Problem Statement" :
https://github.com/Azure/durabletask/wiki#problem-statement

上記の記事の引用となりますが、例えば以下のようなシナリオを考えます。

(1) A というデータベースから、デビットによる出金処理を行う。
(2) もし (1) が成功したら、B というデータベースに入金処理を行う。
(3) (2) が失敗した場合、一定の回数、(2) を繰り返す。一定回数失敗した場合、(1) の処理の消込を行う。

これまでは、このようなビジネスロジックを実現するワークフローを構築するのに、MSDTC といった分散トランザクションの仕組みを利用していましたが、データベースやテーブルのロック処理は、クラウド上でデータベースをスケールさせるのに、ボトルネックとなりやすい処理でした。
また、クラウド上のデータベース自体が、分散トランザクションをサポートしていない場合があるという問題もありました。

そのため、よりシンプルに C# のコードの中で、このようなロジックを書くためのフレームワークが開発され、Durable Task Framework と名付けられました。

上記のワークフローを見ても分かる通り、Durable Task Framework は Azure Functions と非常に相性の良いサービスです。
そのため、Durable Functions として統合した形で、機能をリリースすることになりました。

2. Hello Durable Functions !


以前は Durable Functions を利用するには、Visual Studio 2017 からプリコンパイルした関数をデプロイするなど、一筋縄ではいかない部分が多くありました。
Azure Functions では、動作基盤となるランタイムで新たに Ver 2.0 の開発を進めており、こちらのバージョンのランタイムを利用することで、Azure ポータルからより簡単に Durable Functions を試すことが可能となりました。
試しに Hello World 相当の実装を試してみたいと思います。
(なお、Ver 2.0 のランタイムに関しては、Durable Functions と同様に 2017 年 12 月 1 日現在でプレビューの機能となります。)

ランタイムを変更するため、まずは Function App リソースの「Function App の設定」を開き、ランタイム バージョンを beta にします。
変更には少々時間を要する場合がありますので、beta に変わるまで待機します。

次に関数を作成します。
シナリオで Durable Functions を選択すると、Durable Functions のテンプレートが表示されますので、まずは orchestrator を選択しましょう。
最初に Durable Functions の関数を作成する際には、拡張機能のインストールを求められる場合がありますが、こちらも Azure ポータルから実施することが可能です。

作成すると、すでに既定の内容で run.csx が構成されています。
ここで重要な内容が、 DurableOrchestrationContext.CallActivityAsync メソッドです。
このメソッドは、第一引数に関数名を、第二引数には関数へ引き渡すパラメータを指定します。

orchestrator では既定で Hello という関数が想定されているため、サンプルにしたがい、Hello という関数を、Durable Functions の Activity として追加します。

こちらは 10,000 ミリ秒 Sleep するコードを追加して保存しておきます。

最後に、orchestrator を呼び出すための HTTP トリガーの関数を追加します。
これもテンプレートから作成が可能です。
こちらは特に内容を変更する必要はありません。

作成された関数のバインドを見ると、入力に Orchestration Client と呼ばれるバインドが追加されていることが特徴的です。

実装内容は既定でこちらが入力されています。
この DurableOrchestrationClient.StartNewAsync というメソッドにて、Orchestrator の実行を開始しています。

つまり、入力バインドとして登録されている Orchestration Client が、StartNewAsync メソッドにより Orchestrator を呼び出し、さらに Orchestrator が個々の Activity (関数) を呼び出すような仕組みになることが分かります。
Orchestration Client は、HTTP トリガーの関数の入力バインドとして登録されているものになるため、実際のトリガーは HTTP 以外、例えばキュー トリガーやタイマー トリガーでも実行が可能です。

これで Hello World 相当の Durable Functions が完成しました。

3. Durable Functions を実行する


それでは、Durable Functions を実行してみましょう。
今回はテンプレートから HTTP トリガーの関数をセットアップしましたので、呼び出す URL をコピーしておきます。

{functionName} には、Orchestrator となっている関数の名称が入ります。
この URL に対して、POST メソッドで HTTP 要求を発行すると、以下のようなレスポンスが返却されます。

このレスポンスの statusQueryGetUri に含まれる URL に対して、GET リクエストを送信すると、関数の実行状況が返却されます。
以下の例は、関数が完了した際の実行結果です。

4. 本当に "Durable" なのか?


今回、Hello という関数には 10,000 ミリ秒の Sleep を入れていますが、仮に各 Hello 関数の実行途中、例えば "Hello Seattle!" の実行途中で、何らかの起因により Function App が再起動されたらどうなるのでしょう?
検証のため、関数の実行途中で Function App を再起動させてみます。

再起動させたあと、関数の実行状況を確認します。
再起動中は少し繋がらない時間もありましたが、Running の状態が続いていることが分かります。

しばらく時間をおいてから再び実行状況を確認すると、無事に Completed となったことが分かります。
そのため、再起動の後も関数が問題なく実行できたことが確認できました。

Durable Functions は、このような関数のチェーンの他に、様々なデザイン パターンが考えられますが、まずはことはじめとして、Hello World 相当の関数を試してみました。
ご興味を持たれた方は、プレビューでの提供とはなりますが、ぜひ試してみてはいかがでしょうか。