giftee Tech Blog

ギフティの開発を支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信しています。

月間 200 万回アクセスされる Rails アプリのエラー通知に立ち向かう

はじめに

こんにちは、ギフティでエンジニアをしている @megane42 といいます。社内では、主に giftee Campaign Platform という Rails アプリの開発と運用に携わっています。

giftee Campaign Platform は、例えば「Twitter でフォロー & リツイートをすると抽選でeギフトが当たる」のような、ギフティのeギフトを活用したキャンペーンをかんたんに実施することができるプロダクトです。おかげさまで非常に多くのお客様にご活用していただいており、2020 年 8 月は 1 ヶ月間だけで 200 万回以上のキャンペーン参加が行われました。分間リクエスト数が 1 万回を超えることも珍しくありません。

しかし、利用者数が増えるのに伴って、どうしてもアプリエラーの発生件数も増加してしまいます。現状だと、大まかに毎日 2, 3 件ほどのエラーが発生しています。そこで今回は、私達のチームが日々のアプリエラーとどう向き合っているかを紹介できればと思っています。

Sentry を導入する

giftee Campaign Platform では、エラー管理に Sentry を活用しています。Sentry の導入方法については以前自分が書いたブログがあるので、そちらをご参照ください。

Rails アプリのエラーを Sentry で管理する & Slack 通知する

ここで大事なのは、最後のエラー通知の設定です。上記ブログでも書いたとおり、私達のチームでは Slack への通知設定を以下のようにしています。

  • Every time any of these conditions are met:
    • An issue is first seen
    • An issue changes state from resolved to unresolved
    • An issue changes state from ignored to unresolved

こうすることで、「その種のエラーが初めて起きたとき」「Resolve したはずのエラーが再発したとき」「Ignore する閾値を超えてエラーが起きたとき」に、1 度だけ通知が飛びます。以下の説明はこの設定にしている前提で書いています。

エラーを管理する

私達のチームでは、毎朝デイリースクラムをやっており、その中で Sentry の未解決エラー一覧をチェックしています。やることは下記のとおりです。

  • ステップ 1 : エラーの内容を見る
  • ステップ 2 : エラーの原因を考える
  • ステップ 3 : エラーの対応を考える

ステップ 1 と 2 は特に説明は不要だと思うので、ステップ 3 について解説します。エラーの原因を考えたあと、私達が取る対応は大きく分けて 3 つのパターンに分かれます。

  • パターン 1 : エラーを解消する修正コードをデプロイし、エラーを resolve する
  • パターン 2 : エラーを ignore する
  • パターン 3 : エラーを resolve して様子を見る

パターン 1 : エラーを解消する修正コードをデプロイし、エラーを resolve する

このパターンを選ぶのは、エラーの原因が自分達のコーディングミスによるものだったときです。

この場合、Sentry 上のエラーは一旦そのままにしておきつつ、なるべく早くコードを修正してデプロイしましょう。本番デプロイまで終わったら、Sentry 上で「Resolve」ボタンを押してエラーを解決済みステータスにします。もしその修正でエラーが直っていなかったときは、エラーが再発したときに再度 Sentry から通知が飛んできます。

パターン 2 : エラーを ignore する

このパターンを選ぶのは、エラーの原因が自分達の手の届く範囲外にあり、かつ影響範囲が比較的小さいときです。

例えば、外部 API を利用するアプリで、その外部 API がまれに短時間ダウンしてしまうとしましょう。外部 API のエラーを自分たちの努力で減らすことはできませんし、絶対にダウンしない外部 API など存在しないので、こういったエラーが起きてしまうのはある程度避けることができません。このような場合は、Sentry 上で「Ignore」ボタンを押して、通知が飛んでこないようにしてしまいましょう。そうしないと、本当に重要なエラーが雑多なエラーに埋もれてしまいかねません。

ただし、まれに数件起きる程度なら無視できるけれども、急にそのエラーが数百件単位で起きていたら、それは何か別の問題が起きていそうなので検知したいです。そこで、Sentry 上で単に Ignore を押すのではなく、Ignore -> Until this occurs again... -> 10 times -> per hour をクリックします。こうすることで、基本的には ignore しつつ、急激に発生件数が増えたときだけはキャッチアップできるようになります。ちなみに私達のチームではこの対応のことを「10 per (テンパー) する」と呼んでいます。

なお、エラーの原因が外部にあったとしても、そのエラーがあまりにも定常的に起きるのであれば、設計や利用 API の見直しをした方がよいでしょう。「原因が外部なら即 ignore」と安直に考えないようにしましょう。

パターン 3 : エラーを resolve して様子を見る

このパターンを選ぶのは、エラーの影響範囲が比較的小さく、かつどうしても発生原因がわからないときです。

誤解を恐れずに言うと、エラー内容だけを見ても、どうしても原因がわからないことはよくあります。もしかしたらエンドユーザーが妙な Web ブラウザを使っていたのかもしれません。あるいはエンドユーザーがブラウザの開発者ツールを使ってパラメータを手で弄ったのかもしれません。そんなことまではエラーログだけでは特定できません。

どうしてもエラー原因がわからないときは、その頻度や影響範囲を考えます。それらが十分小さいと判断できるなら、そのエラーは Resolve ボタンを押してしまって、一旦様子を見るという決断も重要です。時間は有限なので、どうしても特定できないエラーの調査に時間を使いすぎてしまうと、かえってよくありません。

まとめ

今回は、私達のチームで Sentry のエラー通知をどう管理しているかを紹介しました。3 パターンの対処法を適切に使い分けることで、効率よくバグを直しつつ、重要なエラーが雑多な情報に埋もれないようにしています。エラーリストが原則として空になっている状態での運用は非常に気持ちがいいので、是非試してみてください。

さて、Sentry によるエラー管理にはもう 1 つ難しい問題があります。「一見すると同じエラーなのに、なぜか Sentry が別種のエラーとして扱ってしまう問題」です。これについては設定をチューニングすることで多少改善させることができるのですが、長くなってしまったので別の記事にしようかと思います。続編をお楽しみに!