*

GAEでよくあるエラーの発生原因と対策1

公開日: : 最終更新日:2016/02/02 投稿者: GAE , ,

GAE上で動くWebアプリケーションに特有の例外について、弊社での運用の事例からいくつか特徴的なものなどをピックアップしてご報告します。

今後追加して行きますので。第一弾です。

Java 7, SDK 1.8.8の環境です。

TooManyResultsException

発生要因

DatastoreからPreparedQuery.asSingleEntity()を利用して1エンティティを取得しようとしたとき、 複数のエンティティが取得された場合に発生するエラー。

原因例

1件だけのはずが複数返却されてきています。原因はいくつかありますが、

  1. データが論理矛盾を起こしている
  2. 設計を理解せず複数取得されるのが当然のクエリに対して、asSingle()をしてしまっている

あたりが主な原因です。

対策案

原因が1番の状況では、データをユニークにすることが一番初めに検討するべき対策となるでしょう。
データをユニークにする一番簡単な方法は、ユニークにするべき値(例えば○○IDやアカウント名など)をKeyオブジェクトにセットしてしまう方法でしょう。
Keyオブジェクトが同じentityは、登録する時に必ず一つになります。

ですが、そうもいかないケースもあり得ます。

特にユーザに関するレコードは、メールアドレスをキーにユニーク性を持たることはよくあるケースです。

単純にメールアドレスをKeyにしてしまえばよさそうなものですが、メールアドレスは変更可能ですので、 Keyには別のIdを振って(自動採番など)、なんらか別のロジックでメールアドレスによる一意制約を担保する必要があります。

原因が2番の状況では、データの設計をちゃんと共有することが、なにより重要です。あまりにも当然ですが。

DeadlineExceededException

発生要因

リクエストを60秒以内に処理できなかった場合に発生するGAE特有のエラー。 同一インスタンス上の他のリクエスト諸共Killされることもあるので、非常に厄介である。

原因例

大量にデータを処理する場合や、他システムとの通信を行う場合に発生します。

たまたまDatastoreやMemcacheからのレスポンスに時間がかかったりすると意外と簡単に発生します。

対策案

方法1:TaskQueueやModulesを積極的に利用する。

大量データを処理する場合は、TaskQueue(10分)、Modulesを利用するようにします。

特に大量に処理することがわかっている場合は、何度でも繰り返し実行可能なような仕組みを考える必要があります。

順次処理をするのであれば、件数をこなすために、繰り替えし繰り替えし実行するしか手段がありません。

そうではなく、同時並行で処理することが可能であれば、そちらのほうがより効果的です。

同時処理でも問題ないか見極めた上で、 1つのタスクをなるべく5分以内に処理ができるようにし、TaskQueueにどんどん登録してしまいましょう。

TaskQueueで同時実行数制限が可能なので、とにかくTaskQueueに放り込んでしまい、あとはお好きな並行数で勝手に処理させるのがCloudっぽくてオススメです。

方法2:Timeoutはリトライをするよう心がける。

他システムとの通信の中には、Googleの他のサービスへのアクセスも含まれるので注意が必要です。

Googleの他のサービスとは、Oauth認証、Drive、メール送信などが分かりやすいと思います。

この場合はDeadLineExceededの前にSocketTimeoutExceptionになることも多いので合わせて注意しましょう。

またMemcacheサービスでたまに応答時間が長くなってしまう事象が起きたりしても結果的にこの例外になりますので、例外を適切にハンドリングしたり、リトライをするようにコーディングしましょう。

特にError Code 104 が発生した場合、そのインスタンスで動いているリクエストが全て中断され、 インスタンスがリセットされるので、FrontEndはとにかく60秒制限を意識して実装する工夫が必要となります。

SocketTimeoutException

発生要因

Oauth認証、DriveなどでURLConnectionを使ってreadする場合に起きやすい。

原因例

ネットワーク越しに処理をする場合は、GAEに限らず必ずTimeoutを意識しましょう。

防げるものではないので発生することを前提としてコーディングするのが鉄則です。

その場合、Connectionの開放もれなどを防ぐために、try-with-resources Statementを積極的に用いるといいです。

対策案

いずれの場合も数秒のTimeout時間を設定し何回かリトライをするように実装します。

あまり多くの時間を設定したり、リトライ回数を無限にするのは控えましょう。 前述の時間制限があることを忘れないようにしてください。

タイムアウトには大きくは2種類あります。

1.connect timeout
connect timeoutは接続にかかった時間で、setConnectTimeoutでタイムアウトする時間を設定できます。

引数はミリ秒で指定し、0を指定すると無限になります。

繰り替えしになりますが、GAEのFront Endの場合は数秒程度が望ましいです。

2.read timeout
read timeoutはデータ取得にかかった時間で、setReadTimeoutでタイムアウトする時間を設定できます。

引数はミリ秒で指定し、0を指定すると無限になります。

一節によると以下のメソッドで発生するらしいので気をつけましょう。

  • getContent
  • getHeaderFields
  • getInputStream
  • getResponseCode
  • getResponseMessage

リトライサンプルコード

以下、Java 7でtry-with-resources Statementを使った例です。

このコードがメソッドにあたり、この上位でfor文によるリトライをします。

URLConnection connection = new URL(BASE_URL).openConnection();
connection.setConnectTimeout(3000);
connection.setReadTimeout(3000);

try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"))) {
    StringBuilder line = new StringBuilder();

    String inputLine;
    while ((inputLine = in.readLine()) != null) {
        line.append(inputLine);
    }
}

以上、如何でしたでしょうか。今後も追加出来るものができたら追加して行きます。

この記事を書いた人

kimura
kimura
Webデザイナー。本サイトの管理・デザイン担当をしております。
 

関連記事

Prediction API入門(後編)

今回はPrediction API on GAE/J みなさん、こんにちは。Prediction

記事を読む

たったの15分でできるGAE/GO入門 標準APIその1

知っておけば必ず開発が楽になる! GAE/Go入門の本連載ですが、前回は第一弾として「PaaS最前

記事を読む

PageSpeedの動作を検証

皆さん、こんにちは。 突然ですが、皆さんはPageSpeedというサービスをご存知でしょうか?

記事を読む

GAE負荷テスト その2「無料で何PVまで表示できるのか試してみた」

その1のアップからだいぶ日が経ってしまい、いつの間にか年すら変わっていましたね。。。 " GA

記事を読む

GAEのスケーリング 後編 <最適化の実践>

この記事では、こちらの公式ドキュメントをもとに、GAEのスケーリングの仕組みと最適化のやり方について

記事を読む

GCP愛を語る

RGCP(GCP好き以外お断り) 技術的な話ばかりの中のAdventCalendarですが、本

記事を読む

静的HTMLをGAE上で公開する

みなさんこんにちは。 本日は、静的HTMLをGAEで公開する手順を紹介します。 本サイトを運営し

記事を読む

Search APIの「Faceted Search」を使ってみた

Search API詳細解説シリーズは完結しましたが、2015年2月19日のAppEngine SD

記事を読む

Google App Engine Modules のScalingを試す

みなさんこんにちは。 前回の記事では、GAE Modulesの簡単なサンプルを配備し、動作を確

記事を読む

Search API詳細解説 Part6「Search API 詳細 限界値編」

みなさん、こんにちは。 前回はSearch APIの反映速度について調べてみましたが、いかがだった

記事を読む

PAGE TOP ↑