PubSub Function でピタゴラスイッチを作り安定稼働させることができたので、作る前から気をつけていたことや実際に動かしてみて気づいたことなどを羅列しておく。
全体
安易に例外で死んではいけない
これは一般的な Web アプリと一緒と言えば一緒。
- 手元の開発では例外で死ぬと PubSub に ack が返らず retry されるが、例外で死ぬのをくり返すだけ
- production (GCP) の場合は PubSub には nack は返らないので PubSub からは retry されない。これを retry するには Function そのものの設定で retry するしかないが、無条件 retry は上と同じ問題を抱えている
「例外」だけど例外的にではなく計算して死ぬ、くらいの気持ち。
cf. バックグラウンド関数の再試行 | Google Cloud Functions に関するドキュメント
機能をできるだけバラす
あくまで Function なので一つ一つの機能はできるだけシンプルに。なんらかのストレージや PubSub を利用しつつ次の Function を call する程度に留めるようにしておく。
バラしたFunctionを個別に実行、スキップできるようにしておく
何らかの仕組みを作っていると、
ここまでの処理は OK だけどここからが NG
みたいな状況は非常によくあるので、常に最初から全部を実行するしかない、という状況は避けておくと開発効率は比較的よくなる。
APIアクセス専用のclassだらけにする
上のバラし方はシーケンスでの分解だけど、もう一つのバラし方の基本はレイヤーで分けること。Function を書く際には OS で提供されている機能、言語内の標準機能だけを使うケースは稀で、ほとんどのケースで GCP の用意した API や外部の API を叩く処理が多く含まれる。ということは言い方を変えると API アクセス部分で死ぬ可能性だらけである。
この部分をそれぞれ専用の class に分割しておくとよい。
- これらの class で専用の retry の処理を仕込む
- 失敗の拾い方を個別に決めておく
- 例えば HTTP GET で 404 が「あり得る」なら「成功」として処理するなど
- mock や emulator が利用できるなら積極的に利用できるようにする
- できるだけ TDD を回す
API 頼みのコードを そのまま mock / emulator なしで書くと production 環境依存になりやすい。production 環境依存だと 1) 書いて、2) 実行して、3) テスト、デバッグする、までのサイクルが大きく重たくなってしまうので、開発速度が上がりにくく保守性も悪くなりやすいので、これを避ける。
外部APIだらけのコードをできるだけTDDっぽく作った話 - あーありがち(2020-03-01) なんかも参照してもらえれば。
必要なリソースをできるだけ早期に確認しておく
周知の通り Function にはメモリにも処理時間にも制限がある。そしてこれらの制限に引っかかった場合の timeout などの異常終了はアプリケーションレベルでは捕捉できず、GCP 内部の status では ok 以外の情報として取得できても log は debug level のものしかなく、PubSub 側でも異常を検知できない。
そこで Function の Execution time や Memory usage をちゃんと確認しておく必要がある。もちろんデータ量が増えた際に変わってくる部分はあるにせよ、ある程度設定可能なリソースの計画を立てておく必要があるし、場合によっては設計の変更も行う。
監視重要
Cloud Functions や AWS Lambda などのサーバレスサービスはとにかく「めちゃくちゃ簡単にプログラムが動かせる」かのようなイメージを抱いてもらおうと頑張っているが、サーバレスでは伝統的なサーバと違ってほとんとすべての情報が /var/log 以下から辿れるといったことはなく、後から何かを知ろうにも「何を使ったら何を知ることができるのか」を分かっていないといけない。
そこで Stackdriver のサービス群への習熟が極めて重要になる。
- Stackdriver Monitoring で ok 以外の status が発生していないか
- 同じく Memory usage や Execution times の数値が異常になっていないか
- 実行頻度、実行回数に異常がないか
- 多すぎる場合もそうだけど、例えば1日1回の定期実行が動いていません、も異常。
これらを適切に監視できるよう設定しておくと安心。
Configuration :: The Manual :: 1.1 Collection :: The Cookbook
世の CakePHP アプリのどの程度が 1.1 で動いていてどの程度が 1.2 で動いているのか知らないけど、1.1 は実際には default 以外の DB Config が使いものにならない。
/trunk/cake/1.1.x.x/cake/libs/session.php - CakePHP : The Rapid Development Framework for PHP - Trac
を読むと session は必ず default DB に書き込むように
ハードコードされている。
は?
config/database.php の意味全然ないじゃん。
仕方ないのでこんなものをでっち上げた。CakePHP 自身の config の切り換えの機能は無視した。
1.2 では問題なさげに見えるけど、1.1 はもうこのままなんじゃないかなぁ。Cake を推してる PHPer の人、こういうの気にしたことないのかしら。ぱっと調べた範囲ではこういう情報見ないから、そんなに有名な話でもないっぽい。てことはだ。
CakePHP 使いは default 以外の DB 使ってないんじゃないの?
1.1 は DB migration もテストも統合されてないし、けっこう微妙なんだなぁ。
ちょこちょこお世話になっていた http://www.bookshelf.jp/ の中の人が本を出されたようです。サポートページが PukiWiki でけっこう見にくいですが(^^;、便利なサイトだったのできっと本もよい感じになっているのではないかと期待。
なんとか落として起動してみた。しばらく自分のマシンでの起動にだけ失敗していた。なんでだ。X が立ち上がる前に一度止まるのが実に「らしい」。startx して XFCE がくるところがニクイ。
ちょっとハマりそう。日本語の通る terminal がないので実際には何もできないに等しいけど、freesbie-jp みたいなの、できないかな。
一応動作していた。あとはタスクスケジューラ経由の起動にコケさえしなければ ok. 明日が楽しみだ。
ヘッドラインの間隔が広くなり、リンクの色も落ち着いていい感じ。