2009年11月28日土曜日

Google App Engineアプリでモデルを変更してデプロイするとエラーになることがある

Google App Engineの何がいいって、デプロイがめちゃくちゃ簡単なところです。
GoogleAppEngineLauncherを使っていればボタン1発で本番環境にアップロードできます。

でも、いざ本番にアップしても500 Internal Server Errorが出る時があります。

個人的にちょくちょく遭遇するパターンがあるのでまとめてみます。
(Python版のGoogle App Engineの話です)


原因はログを確認すれば一発で判明します。(註1)

no matching index found.
This query needs this index:
- kind: ***
properties:
- name: ***
- name: ***

マッチするindexがないよと言われています。
App Engine はデフォルトでいくつかのシンプルなクエリのインデックスを構築します。その他のクエリに対しては、アプリケーションは必要なインデックスを index.yaml という名前の設定ファイルに指定する必要があります。App Engine で実行しているアプリケーションが、(デフォルトによって提供されるか index.yaml で記述されるかした)対応するインデックスがないクエリを実行しようとすると、クエリは失敗となります。
クエリとインデックス - Google App Engine

indexの指定は自動でやってくれるはずなのですが...
すべてのデータストア クエリはインデックスを使用します。インデックスは、希望する順に並べられたクエリ結果を含むテーブルです。App Engine アプリケーションは、index.yaml という名前の設定ファイルでインデックスを定義します。 開発用 Web サーバーは、インデックスが設定されていないクエリが発生すると、このファイルに自動的に候補を追加します。アプリケーションをアップロードする前に、手動でファイルを編集して、インデックスを調整できます。
クエリとインデックス - Google App Engine


DashboardのDatastore - indexes を確認してみます。



おそらくステータスが下のようになっているindexがあるはずです。



このページに詳しい説明があります。
インデックス作成の仕組み - Google App Engine
新しいインデックスを定義し、インデックスを作成するようユーザーが指示すると、次の手順が実行されます:

1. インデックス定義のメタデータをアプリケーションに追加します。
2. インデックス メタデータに Building のマークを付けます。これにより、データストアは、すべての書き込みでインデックスを更新するように、ただし、クエリではまだそのインデックスを使用しないように指示されます。
3. 既存のエンティティをマッピングし、インデックスに追加します。
4. インデックスに Serving のマークを付けます。これにより、クエリでインデックスを使用できることがデータストアに通知されます。

質問: 長い間インデックスの状態が Building または Deleting のままなのはなぜですか?
回答: 昔はこの現象がよく発生していました。ワーカー シャード が大きすぎて、リース期間内に完了できなかったからです。この問題に対処するには、まず、リース期間を増やします。次に、個々のタブレットをシャードに分割します。引き続きインデックス作成に時間がかかることもありますが、以前に比べるとかなり速くなっています。

かなり速くなっていますといっても或る程度データを蓄積している場合は相当な時間(1時間〜)かかることがあるようです。
そんな時はあわてずあせらずしばらく待ってみましょう、というか、待つ以外に解決方法はなさそうです。



回避方法

indexの構築時間中はどうしようもなさそうです。
根本的な解決策ではないのですが、個人的に実践している方法を紹介しておきます。


Versionを活用する


例えばversion 2 で稼働しているアプリケーションの場合
# app.yml
application: myapp
version: 2
runtime: python
api_version: 1

デプロイする際にversionを上げます。
application: myapp
version: 3
runtime: python
api_version: 1


indexesを確認してbuildingservingに変わったらVersion3にチェックをいれてMake Defaultをクリック。

これでユーザーに対して500 Internal Server Errorを出さなくてすみます。

デプロイするたびにversion番号を上げたくない!という場合はversion: 2-0-testのように指定するといいかもしれません。


註1:
Dashboard でログを確認しようとしてもリアルタイムで表示されないことがあります。
なのでエラー情報などを直接ブラウザで確認できるように設定する必要があるかもしれません。
Djangoを使っている場合はsettings.pyのDebugをTrueにするなど。

0 件のコメント: