Azure Functions のタイマートリガーについて

  • 関数を指定したスケジュールに従って実行するトリガー (Timer trigger for Azure Functions | Microsoft Docs)

  • スケジュールは、CRON 式 (Azure Functions のタイマー トリガー | Microsoft Docs) で指定する。CRON 式の一番左のフィールドは分ではなく秒なので、注意

  • CRON 式は Run メソッドに直接定義することも出来るが、%ScheduleAppSetting% のようにアプリケーション設定の設定名で指定することもできる。直接定義すると、スケジュールを変更したい場合は Visual Studio で値変更した後、再度発行する必要があるが、アプリケーション設定であれば Azure ポータルから簡単に変更できるので、特に大きな理由がない限りはアプリケーション設定での設定を推奨

  • スケジュール実行間隔は、通常のスケジュール実行を行いたい場合は 1 分以上を推奨。1 分未満の間隔の場合、実行頻度の問題でランタイム側で UseMonitor が強制的に false にされる)

  • runOnStartup プロパティは、true に設定するとインスタンスの開始時に必ず対象タイマートリガーを実行する。インスタンス再起動時やスケールアウト時などが該当するが、通常のスケジュール実行が目的の場合は true にはしない事。既定値は false

  • useMonitor プロパティは、false に設定するとスケジュール実行状態をストレージに保存しない。インスタンス再起動時やスケールアウト時などに前回実行状態を判断できなくなるので、通常のスケジュール実行が目的の場合は false にはしない事。既定値は true

  • インスタンス起動時に、インスタンス停止中にロストしたスケジュール実行がなかったかチェックが行われる。ロストしたスケジュール実行が存在する場合、isPastDue プロパティを true に設定した上で、即当該メソッドの実行を行う。インスタンスが常に実行し続けていれば関係のない処理だが、例えば Azure のメンテナンス等によりスケジュール実行時間のタイミングでインスタンスの再起動が発生していた場合、この状態になる。なお、ローカルでのデバッグ時はほとんどインスタンス停止している状態のようなものなので、デバッグ開始時は isPastDue 状態となりやすい。isPastDue 状態での実行だから実行継続する、しないなどは単純には判断できないことが多いと思うので、ScheduleStatus の状態を確認し、本来の実行時間より 3 分以内の実行であれば isPastDue でも実行するなど、当該トリガーに合わせたカスタマイズが必要になると思われる

  • TimerInfo.ScheduleStatus のプロパティは以下の値が格納されている。Last と LastUpdated は同じなのでは?とか思うかもしれないが、スケジュール外の時間にタイマーが起動したなど必ずしもメソッド実行されているとは限らないので、そのような場合は LastUpdated だけ更新されることもあり得る

    • Last: 前回メソッド実行日時
    • Next: 前回 ScheduleStatus 更新した時点での、次のスケジュール実行予定日時
    • LastUpdated: 前回 ScheduleStatus 更新した日時
  • デプロイされたインスタンスが複数存在する場合も、実行されるのはそのうちの 1 つのインスタンスでのみ

  • 使用するストレージは、複数インスタンス存在時に 1 つのタイマーのみ実行する為の制御や、前回実行時のスタータス情報の保存等に使用する。host.json の id がストレージ使用時に利用されるので、複数の Function アプリで同じストレージ アカウントを共有しても、問題ない (異なる Function アプリで同じ id を明示的に設定しなければの話)

ストレージファイルのロック f:id:poke_dev:20190506153621p:plain

前回実行時のステータス情報 f:id:poke_dev:20190506153636p:plain

  • Functions v1 + 従量課金プランの組み合わせでの確認だが、複数の異なるタイマートリガーが同時に起動した際、そのうちの一つのタイマートリガー関数が処理終了したタイミングで、他の実行中のタイマートリガー関数が例外の発生もなしにいきなり処理終了 (Application Insights 上も失敗は記録されない) し、その後 isPastDue 付きで再起動する現象が発生することを確認している。

    これの一番の問題は、例外なしにいきなりプロセスが落ちること。例外ハンドルしての対応が行えず、Application Insights でのエラー観測も出来ない。

    複数インスタンスでの同時実行を防ぐためのストレージファイルロックが関係しているようだが、詳細な原因がちょっと不明なため、少なくとも Functions v1 + 従量課金プランの組み合わせでは、複数タイマートリガーの実行が重ならないようなスケジュール設定にするのが、無難。

    例えば HTTP トリガーのコールドスタート対策として定期間隔でタイマートリガーを使用している場合、それを別の Function アプリに外出しするなどが考えられる。

  • ランタイムのフローチャート GitHub - Azure/azure-webjobs-sdk-extensions: Azure WebJobs SDK Extensions

WebJobs.Extensions TimerListener.cs f:id:poke_dev:20190511160925p:plain

f:id:poke_dev:20190512211610p:plain

f:id:poke_dev:20190512213142p:plain

WebJobs.Extensions ScheduleMonitor.cs f:id:poke_dev:20190512212149p:plain