先週は、UnrealEngine にて、C++クラスの追加方法について紹介しました。
今週は、これまでの記事とは少し趣旨を変えて、不具合の根本原因を特定する方法について、説明致します。
■不具合が起きた!
プログラムを作り、試験をしている時。
もうリリースを行った後で、問題が起きた時。
様々なタイミングで、不具合が発生してしまいます。
不具合にもいくつか種類があり、
・ロジック自体に誤りがある
・正しいロジックではあるけれど、性能面で問題がある
・正しいロジックで、性能面でも問題はないけれど、ハードウェアがその負荷に耐えられない
その不具合の内容に応じて、どう解決するのか、も変わってきます。
・正しいロジックに作り変える
・より性能面で優れているロジックに変更する
・ハードウェア構成を変更する
今回は、不具合の中でも、「ロジック自体に誤りがある」に焦点をあてて、
どのように原因を特定していくのかを、ご紹介します。
■切り方に関するイメージ
実は、不具合の特定方法について、私に最初に教えてくれたのは、
プログラムを書く仕事をしていた人ではなく、
サーバの運営業務を行っていた人でした。
もう10年以上も前のことになります。
この時、その先輩に教えてもらったのは、
「プログラムの不具合箇所の特定方法」ではなく、
「問題が起きた部分は、全体のシステムのどこなのか、の特定方法」でした。
対象となるものは違うのですが、プログラムの問題特定の時も、
ほとんど同じ考え方になると思いますので、その方に教えてもらったことを簡単にまとめてみます。
当時、私が携わっていたシステムは、
(実際にはもっと複雑でしたが)下図のような構成でした。

図1 当時私が携わっていたシステムの概要図
このシステムは、各サーバの状態を監視し、何らかの問題が発生した場合に
即座に検知する、というものでした。
通常は、この図でいうと、A~Dシステムに問題が生じた場合に、
全システムの状態を示す、表示サーバに状態が表示されて、
オペレータ様が認識し、各サーバの運用担当者に連絡が行く、
というような流れになっています。
ところが、当時はこのシステム自体に問題が発生することが度々ありました。
その場合、極端に言うと、オペレータ様が見ている画面上では、
A~Dのすべてのサーバに問題がある! というような表示になってしまい、
本当に各サーバに問題があっても、正しく判断できないという状態になってしまうのです。
そこで、私の先輩が教えてくれたのは、
「どこで障害が起きているのかを特定する場合は、まず大枠で切れ」
ということです。
先ほどの図に、注釈を入れてみます。

図2 このシステムの場合の、大枠の切り方
このように、いったいどこで問題が起きているのか? を真っ先に特定することが大事です。
ここがはっきりしないと、調査対象が広すぎて、問題の解決に非常に時間を要してしまいます。
■システム開発では、どうなるのか?
システムにもいろいろな形がありますが、ここでは、仮に以下のようなシステムについて、
例にとって説明します。

図3 システム開発だとどうなるのか、の例となる構成図
この図は、Android/iOSのアプリケーション+Webアプリケーションの構成です。
このような形の場合、どこで不具合が発生していたとしても、
それを検知するのは末端である、Android/iOSのアプリケーション部分です。
しかし、その不具合がどこ原因で発生しているのかによって、
対応方法が変わってきます。
不具合が発生した時、まず行うのは、図3のどこが、根本的な原因になっているのかを特定することです。
■一番最初に切り分けるのは、「Webサーバ側」なのか「クライアント側」なのか
まず、「Webサーバ側」か、「クライアント側」かを特定します。
この時利用するものは、以下です。
1)クライアント(Android、iOS)からWebサーバに対して行っている通信の、
URL、パラメータがどのようになっているのかを調べる。
この内容が、そもそもWebサーバ側の意図していないものであれば、
クライアント側の実装に問題があります。
2)1)で調べた内容で、実際にWebサーバに通信してみる。
Webサーバは、通信相手がクライアント(Android、iOS)であるかどうかは、あまり関係がありません。
テスト用のHTMLを用意して、1)と同じ通信をWindowsPCから行うことで、
より細かい情報を収集することが出来ます。
たとえば、GoogleChromeを使えば、通信URL、パラメータと、その応答で返却された内容を
すべて、視認することができます。
また、通信に要した時間も取得できます。
サーバ側に問題がある場合は、テスト用のHTMLを実行した結果、返却された内容が、
想定していたものと違うことになります。
3)それ以外の場合は、原因箇所が、単純なロジックミスではない、ということになります。
もし、そのようなケースがある場合は、一旦次項以降の調査を行います。
■もしWebサーバ側だと切り分けがついた場合
Webサーバ側だと切り分けがついた場合、次に実施するのは、
「Webサーバ側」か、「DBサーバが絡んでいるのか」です。
いずれにせよ、「Webサーバ側」が問題であることがほとんどですが、
「DBサーバが絡んでいる」=「データに問題がある」と考えてください。
1)Webサーバのプログラムのうち、どこを通っているのかを調べる
クライアントアプリ側でしたら、BreakPointを設定することで、
具体的に、プログラムのどこを通っているのかを簡単に特定できる場合が多いですが、
Webサーバのプログラムでは、そうはいきません。
このようなケースで、一番確実なのは、通っている場所を特定するために、
プログラムの各分岐点に、ログを仕込むことです。
もし、発生している不具合の内容で、ここが原因だという強い確証があるようなら
別ですが、そうではない場合、とにかく各分岐点に、不足なくログを仕込むのが良いです。
ログを仕込むこと自体には時間がかかりますが、一番確実です。
2)DBのログを確認する
DBに対して発行しているクエリに問題がある場合、DBのログに出力されます。
主キーに重複がある、クエリが遅いといった問題であれば、DBのログを確認することで
大凡の原因を特定できます。
3)Webサーバのログを確認する
Webサーバの代表格は、Apacheかと思います。
どのようなシステム構成であっても、Webサーバそのものを自作することはほとんどなく、
Apache や nginx といったものを利用することになります。
エラーが発生した場合、Webサーバのエラーログに、その内容が蓄積されてる可能性が高いです。
■もしクライアント側だと切り分けがついた場合
クライアント側だと切り分けがついた場合、
Webサーバ側の問題に比べると、原因の特定は容易であることが多いです。
1)たいていのクライアント開発の場合は、BreakPointを設定して、
Step実行を行うことが可能です。
この機能を用いて、実際にどんな処理を行っているのかを追跡することで、
原因箇所を大凡特定することが出来ます。
2)そうではない場合は、原因特定がかなり困難になります。
詳しくは後述します。
■クライアント/Webサーバ双方調べたけれど、単純な原因ではなかった場合
単純な原因ではない、という場合、以下のような原因が考えられます。
・タイミング問題
二名以上の人が、ほぼ同時にWebサーバにアクセスした
特定の時刻に、Webサーバにアクセスした
特定のタイミングで、Android/iOS端末を圏外にした
・手順の問題
特定の手順を踏まないといけない場合
・高負荷問題
システムに対する負荷が跳ね上がる時間帯がありますが、
その時にしか発生しない場合
・環境問題
特定の機種でしか起きない、特定のOSでしか起きない問題
この手の問題の可能性がある場合、大切なことは、
とにかく問題発生時の状況を収集することです。
自分達で事象を再現できない場合は、あらかじめプログラムの中にログを
仕込んでおいて、現象が起きた時に、そのログを確認する等、
どうすれば情報を集められるのかを検討します。
■そのほかの切り口
自分達で作ったシステムの、シーケンス図、状態遷移図(表)、関数フローを
書いてみて、どこかに穴がないかを確認することも、解決の糸口になります。
たとえば、シーケンス図というのは、UMLの1つとして組み込まれています。
ただ、問題が発生してしまった時の、原因特定の際にシーケンス図を書こうという場合には、
書き方(体裁)にとらわれることなく、現状をとにかく整理することが重要です。
UMLについては、20年も前に定められたものですので、
インターネット上にも、書籍にも、数多くの情報が出ておりますので、ここでは割愛します。
■さいごに
問題が発生した時に、原因を特定する場合で重要なことは、
原因と考えられる場所(範囲)を、効率的に小さくしていくことです。
検討もつかない不具合の場合でも、原因箇所を2分割、また2分割としていくことで
すべてを調べることなく、目的の個所に到達できます。
この考え方は、アルゴリズムの2分探索木と、似たイメージになっています。
原因を特定した後、その事象をどう解決するのか、には
様々なケースがあり、一度にご紹介することはできません。
今後、ご紹介できそうな内容が出てまいりましたら、別途記事にさせて頂きます。
最後までご覧いただき、ありがとうございました。
弊社では全国各地の請負い(ご自宅)で作業協力頂ける、フリーランスエンジニアの方を常時探しております。
ご興味ある方は、お気軽にお問い合わせ下さい。