この記事はコインチェック株式会社(以下、コインチェック)のアドベントカレンダー8日目の記事です。
自己紹介
私はぐんたまといいます。
日々コツコツとAndroid開発をしています。ある朝いつもどおりリモートワーク*1していますと、優秀なiOSエンジニアがクラッシュ報告を教えてくれました。
Vitalsの謎のエラー
報告元は Android Vitals。
不具合調査において、そこを見ることはほとんどありません。なぜなら、たいていの場合は Firebase Crashlytics が詳細なレポートを上げてくれるからです。
しかし今回は違いました。
どうやら Crashlytics 自体が起動に失敗して落ちているようなのです。クラッシュを監視する仕組みが、クラッシュしている。どうりで Firebase Crashlytics が沈黙しているわけです。
一体何が起こっているのだろう
Vitals のクラッシュログを読んでみます。しかしこれも簡単ではありませんでした。
普段は Crashlytics を使っているため、Play Console 側の難読化マップは設定していません*2。
仕方なく、同じビルド番号の mapping.txt を引っ張り出してきてひたすら find。目を細めてスタックを追ってみます。極めて難解な上、少なく得られる情報は決して多くはありません。
ログは二種類出ており、コールスタック上にfirebase:perf-pluginとpicassoの名前がありました。どちらも利用者の多い有名ライブラリです。ここが不具合出ていればGithubのIssueやSNSですぐに気づくはずですが、特にそれらしい情報は出てきませんでした。
手がかりを探す
クラッシュログ以外にもヒントはあります。Android Vitals では発生時刻や端末情報が見ることができ、以下の傾向がわかりました。
- 朝7時の定時通知送信時に発生しがち
- あるバージョン以降で多発
- 端末や Android バージョンには偏りなし
奇妙なことに、かなり多発しているのにユーザーからの報告はゼロ。手元環境でも不具合らしい不具合もない。リグレッション試験も通っている。
アプリは静かに困っていました。
リバートを試す
原因がわからないときは、まずリバートが定石です。
しかし発生バージョンの前後で、大きな変更はありません。しいて挙げればライブラリのバージョンアップ程度。
となるとライブラリバージョンを戻すのですが、やみくもにライブラリバージョンを戻してもあまり意味はありません。結局どのライブラリが発生源かわからなくなるためです。
そこで、クラッシュログのコールスタックに発見した以下のライブラリからバージョンを下げてみることにしました。
com.squareup.picasso:picasso
com.google.firebase:perf-plugin
市場で試すのは大事なユーザを巻き込む実験のようで気が進みませんが、手元で再現がとれない以上、他に手はありませんでした。
しかし結果から言うと、この実験は失敗に終わりました。
コールスタックを読む限り、この2つのライブラリの初期化で落ちているように見えました。他に要因があるにせよ、これらをリバートしさえすれば少なくともクラッシュはなくなるはずでした。しかし依然としてクラッシュはなくなるどころか減少すらしません。どうしたことでしょう。
ここで気づいたのです。私たちは見落としていました。コールスタックのいちばん上を。
まさかのOkHttp?
コールスタックの一番上には、OkHttp が呼ばれていた痕跡がありました。私たちは「まさか」と思いました。
あの OkHttp によって不具合が起こるなんて、考えもしなかったのです。
いえ、正確に言えば、 OkHttp はエラー時のコールスタックで実によく目にします。しかしそれは、アプリ側の通信不具合が原因でエラーが発生し、その結果として OkHttp がスタックに積まれている、というケースがほとんどでした。
私たちの脳は、OkHttpという名前を“ノイズ”として処理するように適応進化していたのです。そのせいで今回のクラッシュの核心を見逃していました。
改めてクラッシュ発生当時の build.gradle.kts のコミット履歴を確認すると、そこにはしっかりと OkHttp のメジャーアップデート が含まれていました。
4系から5系への移行。
ここに問題の種が仕込まれていたのです。
結論
OkHttp のリバートでようやく Vitals も収まり平和が戻ってきました。アプリのUXに直接影響のないエラーではありましたが、Vitalsに指摘されるとASOに影響する可能性があります。また、Firebase Crashlyticsも使用不可能のまま放置していると監視もままなりません。
リバートに並行し行った調査*3で Firebase 側の issue にて下記を発見しました。
Firebase では OkHttp の5系のバージョンは対応していないということです。派手なクラッシュやANRを観測しないためにどこにも関連情報がなく、解決に苦労しました。
同じように悩む Android エンジニアの方の助けになれば幸いです。