ジョブ設計についてのあれやこれや

ジョブ設計についてつぶやいていたらfrsyukiさんにいろいろアドバイスもらったのでジョブ設計についてのあれやこれや - Togetterにまとめておいた。

で、まあ、それをもとにしつつジョブ設計について書いてみようと思う。別にこうした方が良いとかそういう結論は無い。あしからず。

fluentdなりなんなりでログを収集してHiveでdaily, hourlyで集計してBIツールでレポーティングすることをイメージして書くが別にそれに限定されるわけではない、と思う。

前回ジョブが失敗したときに次回ジョブを実行するか?というトピックについて考えてみようと思う。

ジョブが冪等性を持つのが良いのはもちろんだが、前回ジョブが失敗したときに次回ジョブを実行していいかどうかはケースバイケースのように思う。

前日のデータをdailyで集計する場合、例えば8/1のデータを8/2に集計するようなケースを考える。

             jobA2-1
         /                \
jobA1--jobA2---jobA3

のようなjobnetAが実行されるとしよう。jobA1が失敗したらjobA2, jobA2-1, jobA3が実行されないのはもちろんだが、もし8/2にjobnetAが失敗したときに8/3にjobnetAを実行していいかどうかという話だ。

jobnetAが単に前日のPV, UUを集計するようなジョブで、前日のデータ以外には依存しないならば、実行していいと思う。
失敗したジョブはあとで再実行すればいいし、再実行するのになんの依存関係もない。つまり8/3のjobnetA実行後に8/2のjobnetAを実行してもかまわない。

しかし、jobnetAが前日までの累積合計だったり、新規ユーザ数だったり、要は前日のデータ以外に依存するようなジョブだったら、8/3のjobnetA実行後に8/2のjobnetAを実行してはいけない。

実行してはいけないのだが、jobnetAのうち前日のデータに依存しているのがjobA2-1だけの場合は別の策もありうる。
8/3にjobnetA自体は実行するけれどもjobA2-1にはなんらかのバリデーション(8/1のデータの有無など)をつけておき、失敗した8/2のジョブがリカバリーされない限りは失敗するようにするのである。

こうしておけばjobA1 -> jobA2 は実行できるので、その分はレポーティングできる訳である。

ジョブの依存関係だけでジョブを組もうとすると色々と無理が出てくるので、バリデーションも必要だと思う次第である。

ただし上記のやり方は結局のところ再実行は手動なので、もうちょっとスマートなやり方があるかというとあることはある。僕が思いついたわけではなく教えてもらっただけだけど。


解1:前日の結果に関わらず毎日実行する。
つまり、8/2には8/1のデータを集計するジョブを実行、8/3には8/1のデータを集計するジョブおよび8/2のデータを集計するジョブを実行、8/4には8/1-8/3の3つのジョブを実行、、、という感じである。
明らかに期間が長くなると厳しい。もっとも成功したジョブに関してはスキップするという手もある。
例えば8/2のジョブが成功した場合はAというhiveテーブルのyyyymmddというパーティションに20140801を突っ込むというふうにしておく。
8/3のジョブは20140801のパーティションがあるので8/2のジョブが成功したと判断して8/2のジョブはスキップするというふうにするのである。
パーティションチェックだけなら高速なのでこれでそこそこいけるとは思うがそれでも過去にさかのぼる期間には限度があると思う。


解2:最後のジョブ成功時点から最新日付までの分だけジョブ実行する。
8/3が最後にジョブ成功した日で8/10にジョブを実行する場合は8/4-8/10分だけジョブを実行するというものだが、クエリが複雑になる可能性が高い。


解3: 毎日2,3日分の結果を連続して集計するようにしておき、1,2日分が失敗しても翌日に自動的に再集計されるようにする
これは解1の派生パターンだが、カレンダーテーブルなどを用意しておいて2,3日前の日付を取得できるようにしてそれとjoinしてやる感じ。まあ、これもクエリが複雑になる可能性が高い。


他にもなんか書こうと思ったけど、フットサルの時間なのでここで終了。