永続的オーケストレーション は、 continue-as-new API を使用して独自の履歴を定期的にリセットすることによって無期限に実行されるオーケストレーター関数です。 これは、アグリゲーター、定期的なバックグラウンド ジョブ、および無制限の履歴の増加を伴わない無限ループを必要とする Durable Functions シナリオに役立ちます。
continue-as-newしないと、永続的にループするオーケストレーターは、スケジュールされたタスクごとにオーケストレーション履歴を蓄積し、最終的にパフォーマンスの問題や過剰なメモリ使用量を引き起こします。 永遠のオーケストレーション パターンは、各イテレーションの履歴をリセットすることでこれを解決します。
注
永続的オーケストレーション コード サンプルは、C#、JavaScript、Python、およびJavaで使用できます。 PowerShell では、 continue-as-newはサポートされていません。
この記事の内容:
- 新しい状態で続行するしくみ — リセット メカニズム
- 考慮事項 - 例外の動作、不完全なタスク、および外部イベント
- 定期的な作業の例 — タイマーの重複を回避するクリーンアップ ループ
- 永続的オーケストレーションを開始する - 起動パターンとシングルトン パターン
- 永続的オーケストレーションからの終了 - グレースフル ストップと終了
永続的オーケストレーション は、 continue-as-new API を使用して独自の履歴を定期的にリセットすることによって無期限に実行されるオーケストレーションです。 これは、アグリゲーター、定期的なバックグラウンド ジョブ、無制限の履歴の増加を伴わない無限ループを必要とするシナリオに役立ちます。
continue-as-newしないと、永続的にループするオーケストレーションは、スケジュールされたすべてのタスクの履歴を蓄積し、最終的にパフォーマンスの問題と過剰なメモリ使用量を引き起こします。 永遠のオーケストレーション パターンは、各イテレーションの履歴をリセットすることでこれを解決します。
Important
現在、PowerShell Durable Task SDK は使用できません。
この記事の内容:
- 新しい状態で続行するしくみ — リセット メカニズム
- 考慮事項 - 例外の動作、不完全なタスク、および外部イベント
- 定期的な作業の例 — タイマーの重複を回避するクリーンアップ ループ
- 永続的オーケストレーションを開始する - 起動パターンとシングルトン パターン
- 永続的なオーケストレーションからの退出 - 丁寧な停止と終了
continue-as-new の動作原理
オーケストレーター関数は、無限ループを使用する代わりに、continue-as-newの メソッドを呼び出すことによって状態をリセットします。 このメソッドは、次のオーケストレーター関数生成の新しい入力となる JSON シリアル化可能なパラメーターを受け取ります。
continue-as-newを呼び出すと、オーケストレーション インスタンスは新しい入力値で自分自身を再起動します。 同じインスタンス ID は保持されますが、オーケストレーター関数の履歴はリセットされます。
オーケストレーションは、無限ループを使用する代わりに、オーケストレーション コンテキストで continue-as-new メソッドを呼び出すことによって状態をリセットします。 このメソッドは、次のオーケストレーション世代の新しい入力となる JSON シリアル化可能なパラメーターを受け取ります。
continue-as-newを呼び出すと、オーケストレーション インスタンスは新しい入力値で自分自身を再起動します。 同じインスタンス ID は保持されますが、オーケストレーションの履歴はリセットされます。
永続的オーケストレーションに関する考慮事項
オーケストレーションで continue-as-new メソッドを使用する場合は、次の点に注意してください。
continue-as-newメソッドを使用してオーケストレーター関数をリセットすると、Durable Task Framework は同じインスタンス ID を保持しますが、今後は内部的に新しい実行 ID を作成して使用します。 この実行 ID は外部に公開されませんが、オーケストレーションの実行をデバッグする場合に便利です。実行中にハンドルされない例外が発生すると、オーケストレーションは 失敗状態 になり、実行が終了します。
continue-as-newブロックからのfinallyの呼び出しは、キャッチされない例外の後にオーケストレーションを再起動しません。オーケストレーションが
continue-as-newを呼び出すと、不完全なタスクの結果は破棄されます。 たとえば、タイマーがスケジュールされ、タイマーが起動する前にcontinue-as-newが呼び出された場合、タイマー イベントは破棄されます。必要に応じて、
continue-as-newの再起動中に未処理の外部イベントを保持できます。 C# では、ContinueAsNewは既定で未処理のイベントを保持します。 Javaでは、continueAsNewも既定でイベントを保持します。 Pythonでは、continue_as_newは、save_events=Trueを除き、イベントを保持しません。 JavaScript では、continueAsNewでは、この動作を制御するためにsaveEventsパラメーター (trueまたはfalse) が必要です。
オーケストレーションで continue-as-new メソッドを使用する場合は、次の点に注意してください。
continue-as-newメソッドを使用してオーケストレーションがリセットされると、Durable Task SDK は同じインスタンス ID を維持しますが、今後は内部で新しい実行 ID を作成して使用します。 この実行 ID は外部に公開されませんが、オーケストレーションの実行をデバッグするときに便利です。実行中にハンドルされない例外が発生すると、オーケストレーションは 失敗状態 になり、実行が終了します。
continue-as-newブロックからのfinallyの呼び出しは、キャッチされない例外の後にオーケストレーションを再起動しません。オーケストレーションが
continue-as-newを呼び出すと、不完全なタスクの結果は破棄されます。 たとえば、タイマーがスケジュールされ、タイマーが起動する前にcontinue-as-newが呼び出された場合、タイマー イベントは破棄されます。必要に応じて、
continue-as-newの再起動中に未処理の外部イベントを保持できます。 .NETとJavaでは、continue-as-newは既定で未処理のイベントを保持します。 Pythonでは、continue_as_newは、save_events=Trueを除き、イベントを保持しません。 JavaScript では、continueAsNewでは、この動作を制御するためにsaveEventsパラメーター (trueまたはfalse) が必要です。 いずれの場合も、オーケストレーションが次にwaitForExternalEventまたはwait_for_external_eventを呼び出すと、未処理のイベントが配信されます。
定期的な作業の例
永続的オーケストレーションの一般的なユース ケースの 1 つは、クリーンアップ ジョブなどの定期的なバックグラウンド作業です。
タイマー トリガーを使用しないのはなぜですか? CRON ベースのタイマー トリガーは、前回の実行が完了したかどうかに関係なく、固定時間に実行されます。 永続的オーケストレーションは、次のイテレーションをスケジュールする前に作業が完了するのを待機するため、実行が重複することはありません。
| Approach | スケジュール (1 時間間隔、30 分ジョブ) | 重複リスク |
|---|---|---|
| タイマートリガー (CRON) | 1:00, 2:00, 3:00 | はい — ジョブが設定された間隔を超えた場合 |
| 永久のオーケストレーション | 1:00, 2:30, 4:00 | いいえ - 次の実行は完了を待機します |
[FunctionName("Periodic_Cleanup_Loop")]
public static async Task Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
await context.CallActivityAsync("DoCleanup", null);
// sleep for one hour between cleanups
DateTime nextCleanup = context.CurrentUtcDateTime.AddHours(1);
await context.CreateTimer(nextCleanup, CancellationToken.None);
context.ContinueAsNew(null);
}
public class PeriodicCleanupLoop : TaskOrchestrator<object?, object?>
{
public override async Task<object?> RunAsync(TaskOrchestrationContext context, object? input)
{
await context.CallActivityAsync("DoCleanup");
// sleep for one hour between cleanups
await context.CreateTimer(TimeSpan.FromHours(1), CancellationToken.None);
context.ContinueAsNew(null);
return null;
}
}
永遠のオーケストレーションを開始する
他のオーケストレーション機能と同様に、永続的なオーケストレーションを開始するには、 start-new または schedule-new 永続的クライアント メソッドを使用します。 一度に 1 つのインスタンスのみを実行するには、固定インスタンス ID を使用します。 詳細については、「 シングルトン オーケストレーション」を参照してください。
[FunctionName("Trigger_Eternal_Orchestration")]
public static async Task<HttpResponseMessage> OrchestrationTrigger(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage request,
[DurableClient] IDurableOrchestrationClient client)
{
string instanceId = "StaticId";
await client.StartNewAsync("Periodic_Cleanup_Loop", instanceId);
return client.CreateCheckStatusResponse(request, instanceId);
}
schedule-new クライアント メソッドを使用して、他のオーケストレーションと同様に、永続的オーケストレーションを開始します。 一度に 1 つのインスタンスのみを実行するには、固定インスタンス ID を使用します。 詳細については、「 シングルトン オーケストレーション」を参照してください。
string instanceId = "StaticId";
await client.ScheduleNewOrchestrationInstanceAsync(
"PeriodicCleanupLoop",
null,
new StartOrchestrationOptions { InstanceId = instanceId });
永遠のオーケストレーションから脱却する
オーケストレーター関数が最終的に完了する必要がある場合は、 continue-as-new を呼び出して関数を終了しないでください。
オーケストレーター関数が無限ループにあり、停止する必要がある場合は、オーケストレーション クライアント バインドの terminate API を使用して停止します。
await client.TerminateAsync(instanceId, "Cleanup no longer needed");
詳細については、「 インスタンス管理」を参照してください。
オーケストレーションが最終的に完了する必要がある場合は、 continue-as-new を呼び出してオーケストレーションを終了しないでください。
オーケストレーションが無限ループにあり、停止する必要がある場合は、永続的なタスク クライアントで terminate API を使用して停止します。
await client.TerminateInstanceAsync(instanceId, "Cleanup no longer needed");