Azure FunctionsとAzure Durable Functionsの違い
掲題について、GPT-o1さんに質問した回答です。
Azure FunctionsとAzure Durable Functionsの違い
Azure Functions はイベント駆動型のサーバーレスプラットフォームで、HTTP や Queue、Timer、Event Grid など様々なトリガーに応じてコードを実行するための仕組みです。一方で Azure Durable Functions は Azure Functions 上で複数の関数のオーケストレーションやステートフルなワークフローを構築するための拡張機能です。両者の主な違いは以下の通りです。
1. ステートフルなオーケストレーションの有無
-
普通の Azure Functions:
- 基本的にはステートレスで、関数の実行が終了すると状態は失われます。
- ワークフロー構築をするなら、外部のリソース(Storage, DB, もしくは Logic Apps など)を使って、関数間の連携を独自に管理する必要があります。
-
Durable Functions:
- 関数の実行状態や中間結果を自動的に管理しながら、一連のワークフローとして複数の Functions をオーケストレーションできる。
- Orchestrator 関数 がワークフローの実行順序や条件分岐を記述し、Activity 関数 が実際の処理ロジックを担当。
- 状態管理は内部で Azure Storage を用いて自動化されるため、開発者が明示的に永続化や再開ロジックを記述しなくても「再試行」「中断と再開」が可能になる。
2. 関数間の呼び出しパターン
-
普通の Functions:
- 関数を連携させる場合、HTTPリクエストやキュー、イベントなど外部メッセージを介して呼び出す必要がある。
- チェーン状に順番に呼び出すには、次の関数を呼ぶロジックを自前で書き、状態管理をする必要がある。
-
Durable Functions:
-
Orchestrator 関数 が
await context.CallActivityAsync(...)
のようにシンプルなコードで同期的な書き方を行いつつ、実際にはノンブロッキングでActivity関数を実行・再開する。 - これにより「関数A → 関数B → 関数C」といったチェーンや、Parallel Execution、Fan-out/Fan-inなどを手軽に実装できる。
-
Orchestrator 関数 が
3. 長時間の実行と自動再試行
-
普通の Functions:
- タイムアウト設定があり、長時間実行(数十分〜数時間以上)は現実的でない。
- リトライを実装するにはFunction Retries設定か、外部にロジックを組んで管理する必要がある。
-
Durable Functions:
- ワークフロー自体は短いタスク(=Activity 関数)に分割し、オーケストレーター関数が状態を保持しながら必要なときに再起動する仕組みがある。
- 設定により自動リトライも簡単に実装でき、長時間のワークフロー(例えば24時間以上の待機)にも対応可能。
4. 実行パターンの違い
-
普通の Functions でのパターン例:
- イベントドリブン(HTTP / Timer / Queue / Eventなど)で単発実行。
- 状態管理をしないシンプルなバッチ処理やWeb APIに向いている。
-
Durable Functions でのパターン例:
- Function Chaining: Step1 → Step2 → Step3 … と順番に実行。
- Fan-out/Fan-in: 大量のタスクを並列実行し、その完了を待って次の処理へ。
- Async HTTP APIs: リクエスト受け取り → 長時間処理 → 後で結果を返す。
- Aggregator (Monitor): 特定のイベントを監視し、必要に応じてリトライ・再開する。
5. 注意点
- Durable Functions のOrchestrator 関数内では、書き方に制約がある(I/Oや乱数を直接書くと問題が起きるなど)。
- 普通の Functions と比べ、Durable Functions は実装が複雑になりがちだが、その分高度なワークフローや状態管理が可能になる。
まとめ
- Azure Functions … シンプルな、ステートレスのイベント駆動実行。軽量なマイクロサービスやバックエンド処理に最適。
- Azure Durable Functions … Azure Functions 上でワークフローのオーケストレーションやステートフルなプロセスを扱いたいときに使う。複数のFunctionsを連携し、複雑なシーケンスや並列処理・リトライ・長時間タスクを簡易的に記述できる利点がある。
これが両者の主要な違いとなります。
さらに、チェーンとファンアウト/ファンインのパターンが多いかなと思ったので、実装の違いを聞いてみました。
チェーンとファンアウト/ファンインの実装上の違い
Durable Functions で「Function Chaining」と「Fan-out/Fan-in」を実装する場合、オーケストレーター関数(Orchestrator Function)内での関数呼び出しの方法に違いがあります。
以下、Python版Durable Functionsの例を中心に、両者の実装パターンを比較します。
1. Chaining (チェーン)
1-1. 特徴
-
順番に直列実行するワークフロー。
- 例: Step A → Step B → Step C と順番に呼び出し、BはA完了後、CはB完了後に実行される。
- 1つの結果が次のステップの入力になる構造をシンプルに記述可能。
1-2. 実装例 (Python)
# Sample Orchestrator (Chaining)
import azure.functions as func
import azure.durable_functions as df
def orchestrator_function(context: df.DurableOrchestrationContext):
# 1) Call Activity A
resultA = yield context.call_activity("ActivityA", "input A")
# 2) Call Activity B, using A's result
resultB = yield context.call_activity("ActivityB", resultA)
# 3) Call Activity C, using B's result
resultC = yield context.call_activity("ActivityC", resultB)
return resultC
main = df.Orchestrator.create(orchestrator_function)
ポイント
-
Stepごとに順番に
yield context.call_activity(...)
しており、直列実行が行われる。 - 次のステップは前のステップの完了を待って実行されるので、「A→B→C」が明確に保証される。
2. Fan-out/Fan-in
2-1. 特徴
- 同時並列に複数のActivityを呼び出して実行し、全タスクが完了したら結果を集約(Fan-in)する。
- 大量の並列処理や分割して並列化したいユースケースで使われる(例: バックアップ、画像変換、データ処理など)。
2-2. 実装例 (Python)
# Sample Orchestrator (Fan-out/Fan-in)
import azure.functions as func
import azure.durable_functions as df
def orchestrator_function(context: df.DurableOrchestrationContext):
# 1) Fan-out: start multiple tasks in parallel
parallel_tasks = []
for i in range(10):
parallel_tasks.append(context.call_activity("ActivityX", f"input {i}"))
# 2) Wait for all tasks to finish
results = yield context.task_all(parallel_tasks)
# 3) Do some aggregation if needed (Fan-in)
# For example, combine results or sum them, etc.
aggregated_result = sum(results) # as an example
return aggregated_result
main = df.Orchestrator.create(orchestrator_function)
ポイント
-
複数の
context.call_activity(...)
をリストに追加(parallel_tasks
) -
yield context.task_all(...)
で全タスクの完了を待機し、すべて揃ったら結果を受け取りFan-inの処理をする。 - 各タスクは同時並列で実行されるため、時間が大きく短縮できる可能性がある。
3. 実装上の主な違い
-
関数呼び出し順序
-
Chaining:
A → B → C
という直線的なフロー。 - Fan-out/Fan-in: 同時並列で複数の関数を呼び出してから、一括して結果を取得するフロー。
-
Chaining:
-
コードの書き方
-
Chaining:
yield context.call_activity(...)
をステップごとに記述する。順番通りに並べる。 -
Fan-out/Fan-in:
parallel_tasks = [ ... ]
をリストで作成 →results = yield context.task_all(parallel_tasks)
で一度に待機・回収。
-
Chaining:
-
戻り値の扱い
- Chaining: 前ステップの戻り値を次ステップに引き継げる。
- Fan-out: 全タスクの結果をリストや辞書などでまとめて受け取る。必要があれば集計・マージする。
-
同期/非同期
- Chaining: 1ステップ完了までは次に進まない(同期的に並べる)
- Fan-out: Activity 関数を並列に起動し、それらが完了するまで非同期に待ち、最後にまとめる。
4. Azure Docs リンクでの例
4-1. Chaining の例
-
Sequential chaining (Functions sequence) - Microsoft Docs (日本語)
- シンプルに「1→2→3」と呼び出し結果を引き継ぐ例が掲載。
4-2. Fan-out/Fan-in の例
-
Fan-out/fan-in pattern (Backup example) - Microsoft Docs (日本語)
- 複数の仮想マシンバックアップ処理などを並列実行し、最後にまとめて集約する例が紹介されている。
まとめ
- チェーン(chaining): 直列ステップを順番通り呼び出す。1つのActivity完了→次のActivity開始 というシンプルなフロー。
- ファンアウト/ファンイン(fan-out/fan-in): 複数のActivityを並列実行し、すべて完了したら結果を一括集約する。
-
実装の違いは、オーケストレーター関数内で**
call_activity
を直列で呼ぶか、リストに格納してtask_all
で待機するか**という点に集約されます。
いずれもDurable Functions のオーケストレーションパターンであり、状況に合わせて使い分けるのがポイントです。
再試行処理も違うとのことだったので、何が違うのか聞きました。
通常のFunctionsとDurable Functionsのエラーハンドリングの違い
Durable Functions における「再試行 (リトライ)」と、通常の Azure Functions が持つ「再試行」とでは、仕組みや設定可能な範囲が異なります。
以下に、両者の違い・Durable Functions 特有の再試行設定についてまとめます。
1. 通常の Functions の再試行
-
トリガー種別による制限
- Queue Trigger の場合、メッセージを削除しないと再度取得されるため、結果的に再試行が起きる。
- Timer Trigger の場合、指定時刻に繰り返し実行されるが、単発処理で例外が起きても自動再試行は無い。
- Event Grid / Service Bus Queue なども固有の再試行仕組みがある。
-
host.json による設定
- Functions v2 以降、host.json で「maxDequeueCount」(Queue) や「maxDeliveryCount」(Service Bus) を指定して一定回数失敗したらポイズンキューへ送るなどの動きが可能。
-
全般
- ステートレスのため、呼び出しのたびにイベント(トリガー)を受け、失敗すればトリガー側のリトライで再試行される仕組みが多い。
- コードから細かく「リトライ間隔を制御」する場合は、外部リソース(Queue, Database)などを使った状態管理を自前で実装する必要がある。
2. Durable Functions の再試行
Durable Functions では、オーケストレーションの仕組みを通じてActivity 関数の自動再試行やリカバリ処理を簡単に組み込むことができます。
2-1. Activity 関数の自動再試行
例: Python の場合、call_activity_with_retry
メソッドを使います。
import azure.durable_functions as df
import datetime
def orchestrator_function(context: df.DurableOrchestrationContext):
retry_options = df.RetryOptions(
first_retry_interval_in_milliseconds=5000,
max_number_of_attempts=3
)
retry_options.backoff_coefficient = 1.5
# Activity 関数を再試行設定付きで呼び出し
result = yield context.call_activity_with_retry(
"MyActivity",
retry_options,
input_data
)
return result
ポイント
-
RetryOptions
で最大試行回数や初回リトライ間隔、バックオフ係数などを細かく指定可能。 - Activity 関数が失敗 (例外スロー) すると、オーケストレーターが自動的に状態管理し、設定に従って再度呼び出す。
- これにより**ビジネスロジック上の「指定回数失敗でエラー」や「指数バックオフ」**を柔軟に実装できる。
2-2. Orchestrator 関数のステートフル動作
-
失敗後の状態保存
- Activity が失敗しても Orchestrator の状態はストレージに保存され、再実行時に失敗の直前からやり直せる。
-
他のパターンのエラー処理
- チェインの途中で失敗した場合、どこから再開するかなどをオーケストレーター関数のロジックで制御可能。
-
call_sub_orchestrator_with_retry
などを使ってサブオーケストレーションも再試行できる。
3. 違いのまとめ
特徴 | 通常の Azure Functions | Durable Functions |
---|---|---|
状態管理 | ステートレス(コード終了後に状態は消える) | Orchestratorがステートフルに管理し、中断/再開/再試行を自動サポート |
リトライ設定 | host.jsonやトリガー依存 (Queue, Service Bus, etc.)、外部リソース頼み |
call_activity_with_retry などを利用し、コードで細かく設定できる |
リトライ対象 | 基本的にトリガー単位 | Activity 関数ごとに個別設定可 |
バックオフ係数/間隔制御 | トリガー側に簡易設定はあるが制限が多い |
RetryOptions で初回待機時間・最大回数・バックオフ係数など柔軟に設定 |
長時間の処理 | 不向き(Functionタイムアウト等) | 長時間ワークフローでも中断再開でき、リトライも含め柔軟な制御可能 |
Durable Functions は Activity 関数単位の細かい再試行制御やワークフローのステートフル復元ができるため、高度な再試行ロジックを簡単に実装できる点で普通の Functions と異なります。
4. 参考リンク
以上が、Durable Functions における再試行と通常のAzure Functionsでの再試行の違いです。
最後に、コストの差があるか聞いてみました。
通常のFunctionsとDurable Functionsのコスト差
Azure Functions と Durable Functions はいずれも同じ「Azure Functions プラットフォーム」の上で動くため、従量課金モデルやプラン(Consumption/Premium/Dedicatedなど)自体は共通です。ただし、Durable Functions特有のオーケストレーション処理により、以下の点でコストに差が生じる可能性があります。
1. 基本的な課金モデルは同じ
1-1. 消費プラン (Consumption Plan)
- 実行時間 + 実行回数(リクエスト数) + Azure Storage トランザクションなどを含む形で従量課金。
- メモリ消費 と 実行時間 (GB・秒) での計算や、無料枠がある点などは通常のAzure Functionsと変わらない。
1-2. プレミアムプラン / 専用プラン
- 常時稼働するコンテナリソースを確保するため月額の固定費(割り当てvCPU数 + メモリ)、および実行回数などが加わる。
- こちらもDurable/通常Functionsの区別なく同一のプラン体系。
2. Durable Functions で追加されるコスト要素
-
オーケストレーションの管理 (Storage / Queue)
- Durable Functionsは内部でAzure Storageを使い、「オーケストレーター関数の状態」「Activity関数の待ち行列」「ヒストリーイベント」などを保存します。
- このため、ストレージトランザクション(読み書き回数)やストレージ容量が、通常のFunctionsより増加する可能性があります。
- 特に長時間稼働するワークフローやステップ数が多いオーケストレーションほど、ストレージの操作回数が増えコストに影響する。
-
オーケストレーター関数の起動回数
- Orchestrator 関数自体も再開や中断のたびに“実行”としてカウントされる。
- 例: Fan-out/Fan-in で大量の Activity 呼び出しを行うと、それに応じてオーケストレーションが複数回動き、実行リクエスト数が増える。
-
Activity 関数の件数
- 通常のFunctionsではイベントが来るごとに1回の関数実行だが、Durable Functionsでは1つのジョブ(オーケストレーション) の中でも多くのActivity関数を呼び出す場合がある。
- その分だけFunction実行回数が増え、微増のコストとなる可能性がある。
3. コストへの影響のまとめ
-
従来の Functions:
- 主に “関数が呼び出された回数 × 実行時間 (× メモリ消費量)” での課金。
- ストレージはトリガーに依存(Queue/EventGridなど)する場合には多少の追加コストあり。
-
Durable Functions:
- オーケストレータ関数とActivity関数の両方で実行時間・実行回数が加算される。
- さらにオーケストレーションのヒストリ管理やステータス更新で Azure Storage のトランザクションが発生。
- 長時間のワークフローや大量ステップの並列実行がある場合、通常のFunctionsよりもトランザクションが増えるため、総コストがやや高くなる可能性。
4. 実際の運用での考え方
- シンプルなワークフローや短時間処理なら、通常のFunctionsと大きく変わらないか、僅差程度。
- ステップ数が多い(大量Fan-out) or 長期実行で、ヒストリー記録や再開回数が増えるとストレージ操作が多くなり、コスト差が顕在化する。
- ただし、Durable Functionsを使わずに「自前でワークフロー管理」しても、やはりQueueやDB、あるいは別のツールで同様のトランザクションが発生するため、Durable Functionsのオーケストレーションコストが“追加”というより “隠しコストを表面化”しているとも言える。
5. 参考ドキュメント
- Azure Functionsの料金 - Microsoft Docs
-
Durable Functionsのエラー処理と再試行 - Microsoft Docs
- 料金について直接は書かれていませんが、再試行が増えるほど実行回数が増えるという点でコストに影響。
- Durable Functions のバックエンド - Microsoft Docs
まとめ
- 課金プラン自体は同じ (Consumption/Premiumなど) で、Durable Functions専用の追加月額費は無い。
- ただし、オーケストレーション管理のためにAzure Storageトランザクションや実行回数が増えるので、普通のFunctionsに比べ若干コストが上乗せされることがある。
- 特に長時間・多ステップ・大量並列処理のシナリオだと、Durable Functionsはストレージ書き込み + Activity呼び出しが多くなりやすく、結果としてコスト増に繋がる点に留意が必要です。
以上、Durable Functionsについてのメモでした。
Discussion