※この記事は6年以上前の記事です。
現在は状況が異なる可能性がありますのでご注意ください。
どうも。
仕事でかなり危ないミスをしてしまいました・・・。
関係各所、そして、お客様、本当に申し訳ございません。。。
さて、その原因となった、whileについて。
whileってのはとても便利な構造文で、
何回繰り返しすればわからない時に、条件に当てはまる間は、実行を繰り返すというもので、
おおよそほぼ全てのプログラム言語に存在するものだ。
例えば、運動会に来る家族の中で、パパがその日誕生日の家族を割り出し、その家族より前に入場してきた家族で、対象家族の子供と同じクラスの子供がいる家族を5組割り出すなんて時。
(ややこしくてすみませんが、whileをわざわざ使う状況を作りたかった)
<?php // 運動会の日 $sportsDay = strtotime(date('Y-m-d')); $sportsMonth = date('m', $sportsDay); $sportsDay = date('d', $sportsDay) // 入場した家族を格納する $familys = array(); // 対象家族を入れる $targetFamily = array(); // 割り出した家族を格納する $matchingFamilyArr = array(); // 家族を入場させる $familys[] = array('papaBirthday' => '1988-01-06', 'childClass' => 'A'); $familys[] = array('papaBirthday' => '1985-05-03', 'childClass' => 'B'); $familys[] = array('papaBirthday' => '1979-03-16', 'childClass' => 'B'); $familys[] = array('papaBirthday' => '1980-11-18', 'childClass' => 'C'); $familys[] = array('papaBirthday' => '1975-02-16', 'childClass' => 'A'); // . // . // . // 対象がこの家族だとする $familys[] = array('papaBirthday' => '1990-12-26', 'childClass' => 'A'); // . // . // . $familys[] = array('papaBirthday' => '1982-07-11', 'childClass' => 'C'); // ここから繰り返し実行 foreach ($familys as $listNo => $famiValue) { // 誕生日を取得 $birsMonth = date('m', $famiValue['papaBirthday']); $birsDay = date('d', $famiValue['papaBirthday']); if ($birsMonth == $sportsMonth && $birsDay == $sportsDay) { //今日が誕生日のパパ $targetFamily = $famiValue; // ここからwhile $countPrev = 1; while (count($matchingFamilyArr) < 6) { // 対象となる家族が、五組になるまで繰り返す。 if (isset($familys[$listNo - $countPrev])) { if ($famiValue['childClass'] == $familys[$listNo - $countPrev]['childClass']) { $matchingFamilyArr[] = $familys[$listNo - $countPrev]; } } $countPrev++; } break; } }
今回は、PHPでの事故だったので、PHPで書いてみた。
(そして、サーバーサイドの言語の方が、事故の規模が大きいので・・・。)
while文で、こんな風にすれば、条件に見合う家族が、五組割り出せる。
しかし、気付いた人はいると思うが、
もうこの時点で、無限ループの可能性が大いにある。
39行目のwhile文の条件だと、
対象家族の前に入場した家族の子供と同じクラスの子供が、五組以下なら、
while文は止まらなくなるのだ・・・。
無限ループは、特にPHPやサーバーサイドの言語の場合、
サーバーにタイムアウトの嵐を呼びこみ、とんでもないことになる。
今回は、かなりの時間、ダウンさせてしまった・・・。
これを防ぐとっておきの方法を、上司から教えてもらった。
カウンターをつける、だ。
while文の部分を下記のように直す。
// 上記省略 // 上のスクリプトで定義している変数も使ってるので注意 // ここからwhile $stopperCount = 0; $countPrev = 1; while (count($matchingFamilyArr) < 6 && $stopperCount < 2000 && $stopperCount < count($familys) - (count($familys) - $listNo)) { // 対象となる家族が、五組になるまで繰り返す。 if (isset($familys[$listNo - $countPrev])) { if ($famiValue['childClass'] == $familys[$listNo - $countPrev]['childClass']) { $matchingFamilyArr[] = $familys[$listNo - $countPrev]; } } $countPrev++; $stopperCount++; } // 下記省略
そう、今回の条件なら、来た家族の組み数以上繰り返す必要はない。
つまり、その数を超えた回数を実行しようとしていたら、それは無限ループが発生しているってことだ。
なら、その回数以上になったら、whileを止める、って仕組みを作ればいい。
また、それプラス、万一のために、2000回を超えないようにしている。
こうすれば、よっぽどのことがない限り、バグはでない。
プログラマーとしては、常識なのかもしれませんが、
全然なってませんでした・・・。
今回はいい勉強になりました。
ほんと気をつけよう・・・。
コメントする