こんにちは。FLINTERS エンジニアの塚本です。 この記事はFLINTERS設立10周年ブログリレーの77日目の記事になります。
はじめに
私が担当しているシステムではCloud Composerを用いて、ワークフローを管理しています。Cloud ComposerはGoogle CloudがマネージするApache Airflowです。AirflowのWorkflows as codeの理念通り、Pythonでワークフローを記述するので自由度が高く、連携用に提供されている機能も多機能でありがたいサービスです。一方、その複雑さ故にわかりにくいところもあります。この記事では、私がCloud Composerを使ったシステムに機能を追加するにあたって難しかったところを書き残す記事になります。
Airflowの時間について
ワークフローをスケジュール実行するにあたって、「実行日のデータを選択して取得する」といったことをするために、システムから「ワークフローの起動時刻」を取得したいことは多々あると思われます。
Data Interval
Airflow 2.2以降では、データの取得対象として"Data Interval"という概念が与えられています。時間を区間に区切り、ある区間が終了したとき、その区間内のデータを取得対象としたワークフローが動くという論理です。 この"Data Interval"の始まりと終わりを表す変数として、data_interval_startとdata_interval_endが与えられています。つまりdata_interval_endがスケジュール実行時においてはワークフローの起動時刻を表す変数として用いることが出来ます。
では手動実行時の取得対象データ区間はどうなるでしょうか。
手動実行時でもどうやら同じようにData Intervalが与えられ、それは既に完了している直近のData Intervalであるようです。
(ex. 毎日10:00にスケジュール実行されるDAGを、10/1 12:00に手動実行したときのData Intervalは9/30 10:00-10/1 10:00、この日時がdata_interval_start, data_interval_endに格納される。)
つまり、data_interval_endはスケジュール実行時においては、ワークフローの起動時刻を表す変数として使うことが出来ますが、手動実行時も含めてワークフローの起動時刻を正しく取得したいときには不適切そうです。
logical_date(旧: execution_date)
ところで、Airflowにはlogical_dateという変数もあります。この値はData Intervalが導入されるまでの旧名であったexecution_dateという名が表すように、一見、実行日を取るのに適切であるように思われます。この値は手動実行時においては、「実際にワークフローが起動された時間」を表し、目的に叶いそうです。しかし、この値はスケジュール実行時にはdata_interval_startと同じ値が入ります。なんと、スケジュール実行時と手動実行時で異なる紐づけの値が入ってしまいます。スケジュール実行時にもlogical_date=data_interval_end=ワークフロー起動時刻であればよかったのですが…。
つまり、data_interval_endとは反対に、手動実行時のみ「ワークフローの起動時刻」が取得出来るということですね。一般に、ワークフローはスケジュール実行することを前提に組んでいると思いますし、障害が発生したときなどに手動実行が必要になることも自然に考えられるでしょう。このexecution_dateの挙動の差がスケジュール実行時・手動実行時ともにまさにほしい値で無い限り、execution_dateを使うべきではなさそうです。
他にもDAGから取れそうな値を調べたのですが、ワークフローの起動時刻を常に取得する振る舞いの良さそうな変数はどうやら無さそうです。
結論
私の運用しているシステムでは、「実行日のデータを選択して取得する」目的のためにdata_interval_endを取得することにしました。 最初は「まさにワークフローが実行された日時」を取得したいと考えていたのですが、「実行日のデータを選択して取得する」ためには、Data Intervalの終端で良い(むしろ万が一早朝、まだデータが格納されきっていない時間に実行する可能性のことを考えるとむしろこちらのほうが安全)であることに気づき、「本質的に必要なもの」を考えるよい機会になりました。