オーケストレーター関数コードの制約

Durable Functions は、ステートフル アプリの構築を可能にする、Azure Functions の拡張機能です。 オーケストレーター関数を使用すると、関数アプリ内の他の持続的関数の実行を調整できます。 オーケストレーター関数はステートフルで信頼性が高く、場合によっては実行時間が長いことがあります。

オーケストレーター コードの制約

オーケストレーター関数では、イベント ソーシングを使用して信頼性の高い実行を確保し、ローカル変数の状態を維持します。 オーケストレーター コードの再生動作によって、オーケストレーター関数に記述できるコードの種類に制約が生まれます。 たとえば、オーケストレーター関数は決定論的である必要があります。オーケストレーター関数は複数回再生されますが、毎回同じ結果を出す必要があります。

決定論的な API の使用

このセクションでは、コードが決定論的であることを確保するための簡単なガイドラインを示します。

オーケストレーター関数は、ターゲット言語で任意の API を呼び出すことができます。 ただし、オーケストレーター関数では決定論的な API のみを呼び出すことが重要です。 "決定論的な API" とは、呼び出しのタイミングや頻度に関係なく、同じ入力に対して常に同じ値を返す API です。

以降のセクションでは、決定論的 "ではない" ために回避する必要がある API とパターンに関するガイダンスを提供します。 これらの制限は、オーケストレーター関数にのみ適用されます。 その他の関数の種類には、このような制限はありません。

Note

いくつかの種類のコード制約について以下で説明します。 この一覧は残念ながら包括的なものではなく、一部のユース ケースが扱われていません。 オーケストレーター コードを記述するときに考慮すべき最も重要なことは、使用している API が決定論的かどうかです。 このような考え方に慣れると、どの API が安全に使用でき、どれがそうでないかを、このドキュメントの一覧を参照することを必要とせずに簡単に理解できます。

日付と時刻

現在の日付または時刻を返す API は非決定論的であり、オーケストレーター関数では使用しないようにする必要があります。 これは、オーケストレーター関数を再生するたびに異なる値が生成されるためです。 代わりに、Durable Functions と同等の API を使用して現在の日付または時刻を取得する必要があります。これは、再生間で一貫性が維持されます。

現在の時刻を取得するために、DateTime.NowDateTime.UtcNow、または同等の API を使用しないでください。 Stopwatch などのクラスも避ける必要があります。 .NET インプロセス オーケストレーター関数の場合は、IDurableOrchestrationContext.CurrentUtcDateTime プロパティを使用して現在の時刻を取得します。 .NET 分離オーケストレーター関数の場合は、TaskOrchestrationContext.CurrentDateTimeUtc プロパティを使用して現在の時刻を取得します。

DateTime startTime = context.CurrentUtcDateTime;
// do some work
TimeSpan totalTime = context.CurrentUtcDateTime.Subtract(startTime);

GUID と UUID

ランダムな GUID または UUID を返す API は、再生のたびに異なる値が生成されるため、非決定論的です。 使用する言語によっては、決定論的な GUID または UUID を生成するための組み込み API を使用できる場合があります。 それ以外の場合は、ランダムに生成された GUID または UUID を返すアクティビティ関数を使用します。

ランダム GUID を生成するために、Guid.NewGuid() などの API を使用しないでください。 代わりに、コンテキスト オブジェクトの NewGuid() API を使用して、オーケストレーターの再生に安全なランダム GUID を生成してください。

Guid randomGuid = context.NewGuid();

Note

オーケストレーション コンテキスト API で生成される GUID は、バージョン 5 の UUID です。

ランダムな数値

オーケストレーター関数に乱数を返すには、アクティビティ関数を使用します。 アクティビティ関数の戻り値は、オーケストレーション履歴に保存されるため、いつでも安全に再生できます。

別の方法として、固定シード値を指定した乱数ジェネレーターをオーケストレーター関数で直接使用できます。 この方法は、オーケストレーションの再生ごとに同じ一連の数値が生成される限り安全です。

バインド

オーケストレーション クライアントエンティティ クライアントのバインドを含め、バインドをオーケストレーター関数で使用しないようにする必要があります。 常にクライアントまたはアクティビティ関数内から入力と出力のバインドを使用してください。 オーケストレーター関数が複数回再生されて、外部システムとの I/O が非決定論的になり、重複する可能性があるため、これは重要です。

静的変数

静的変数は、時間の経過と共に値が変化し、結果として非決定論的なランタイム動作が生じる可能性があるため、オーケストレーター関数では使用しないでください。 代わりに、アクティビティ関数には定数を使用するか、静的変数の使用を制限してください。

Note

オーケストレーター関数の外部でも、Azure Functions で静的変数を使用すると、複数の関数の実行にわたって静的な状態が保持される保証がないため、さまざまな理由で問題が発生する可能性があります。 アクティビティまたはエンティティの関数でのベスト エフォートのメモリ内キャッシュなどの非常に特定のユースケースを除き、静的変数は避ける必要があります。

環境変数

オーケストレーター関数では環境変数を使用しないでください。 これらの値は時間の経過と共に変化し、結果として非決定論的なランタイム動作が生じる可能性があります。 オーケストレーター関数に環境変数で定義された構成が必要な場合は、構成値をアクティビティ関数の入力として、または戻り値としてオーケストレーター関数に渡す必要があります。

ネットワークと HTTP

送信ネットワーク呼び出しを行うには、アクティビティ関数を使用します。 オーケストレーター関数から HTTP 呼び出しを行う必要がある場合は、持続的 HTTP API を使用できます。

スレッド ブロック API

"sleep" などのブロック API は、オーケストレーター関数のパフォーマンスやスケールに関する問題を発生させる可能性があるため、回避する必要があります。 Azure Functions 従量課金プランでは、不要な実行時間の料金が発生する可能性もあります。 使用可能な場合は、ブロック API に代わる手段を使用してください。 たとえば、持続的タイマーを使用して、安全に再生でき、オーケストレーター関数の実行時間にカウントされない遅延を作成します。

非同期 API

オーケストレーション トリガーのコンテキスト オブジェクトで定義されているものを除き、オーケストレーター コードで非同期操作を開始しないでください。 たとえば、.NET では Task.RunTask.DelayHttpClient.SendAsync を、JavaScript では setTimeoutsetInterval を使用しないでください。 オーケストレーター関数でアクティビティ関数のスケジュールなどの非同期作業のスケジュールに使用するのは、Durable SDK API のみにする必要があります。 他の種類の非同期呼び出しは、アクティビティ関数内で実行する必要があります。

非同期 JavaScript 関数

JavaScript オーケストレーター関数は、常に同期ジェネレーター関数として宣言してください。 非同期関数が決定論的であることが Node.js ランタイムでは確保されないため、JavaScript オーケストレーター関数を async として宣言しないでください。

Python コルーチン

Python オーケストレーター関数をコルーチンとして宣言しないでください。 つまり、Python オーケストレーター関数を async キーワードで宣言しないでください。コルーチン セマンティクスが Durable Functions 再生モデルと一致しないためです。 Python オーケストレーター関数は、常にジェネレーターとして宣言する必要があります。つまり、context API は awaitではなく yield を使用すると想定する必要があります。

.NET スレッド API

Durable Task Framework では、1 つのスレッドでオーケストレーター コードが実行され、他のスレッドと対話することはできません。 ワーカー プール スレッドで非同期継続を実行すると、オーケストレーションの実行によって非決定論的な実行またはデッドロックが発生する可能性があります。 このため、オーケストレーター関数では、スレッド API をほぼいつでも使用しないようにする必要があります。 たとえば、オーケストレーター関数で ConfigureAwait(continueOnCapturedContext: false) を使用しないでください。 これにより、オーケストレーター関数の元の SynchronizationContext で、タスクの実行が継続されます。

Note

Durable Task Framework では、オーケストレーター関数で非オーケストレーター スレッドを不注意に使用していることを検出しようとします。 違反が検出されると、フレームワークは、NonDeterministicOrchestrationException例外をスローします。 ただし、この検出動作ではすべての違反をキャッチできないため、これに頼りすぎないようにしてください。

バージョン管理

持続的なオーケストレーションは、日、月、年、さらには永続的に継続して実行できます。 Durable Functions アプリに対して、未完了のオーケストレーションに影響を与えるコードの更新が加えられた場合、オーケストレーションの再生動作が中断されることがあります。 そのため、コードを更新するときは慎重に計画することが重要です。 コードをバージョン管理する方法の詳細については、バージョン管理に関する記事を参照してください。

持続的なタスク

Note

このセクションでは、Durable Task Framework の内部実装について詳しく説明します。 この情報は、Durable Functions を使用するうえで必ずしも必要とは限りませんが、 再生動作を理解するうえでは役に立ちます。

オーケストレーター関数で安全に待つことができるタスクは、"持続的なタスク" と呼ばれることがあります。 これらのタスクは、Durable Task Framework によって作成および管理されます。 たとえば、.NET オーケストレーター関数で、CallActivityAsyncWaitForExternalEvent、および CreateTimer から返されるタスクです。

このような持続的なタスクは、.NET の TaskCompletionSource オブジェクトの一覧によって内部的に管理されます。 再生時に、これらのタスクはオーケストレーター コードの実行の一部として作成されます。 これらは、対応する履歴イベントがディスパッチャーによって列挙されると完了します。

これらのタスクは、すべての履歴が再生されるまで、1 つのスレッドを使用して同期的に実行されます。 持続的なタスクで、履歴の再生の終了までに完了しなかったものについては、適切なアクションが実行されます。たとえば、アクティビティ関数を呼び出すために、メッセージがエンキューされることがあります。

このセクションのランタイム動作の説明を参照すると、オーケストレーター関数では、非持続的なタスクに awaityield を使用できない理由がわかります。 理由として、ディスパッチャー スレッドではタスクの完了を待機できないことと、そのタスクによるコールバックによってオーケストレーター関数が追跡状態でなくなる可能性があることの 2 点が挙げられます。 これらの違反を検出できるように、いくつかのランタイム チェックが行われます。

Durable Task Framework がオーケストレーター関数を実行する方法の詳細については、GitHub の持続的なタスクのソース コードを確認してください。 特に、TaskOrchestrationExecutor.csTaskOrchestrationContext.cs を参照してください。

次のステップ