2009年11月29日日曜日

ListCampの使い方をまとめてみた

個人でWebサービスを作ってて何が面倒って、自分ででヘルプページとかプライバシーポリシーを用意しなければならないところです。

面倒だなあと思いつつも軽くまとめたのがこちら

ListCampの使い方
http://jp.listcamp.com/list/aghsaXN0Y2FtcHIMCxIETGlzdBjXhwUM/

リストの作成

わざわざWikiを用意するまでもないし、別ページを作るのもなんだかなあという感じだったので、せっかくだからListCampのリストでまとめてみました。


こういうヘルプページって、書かなければいけないことがいくらでもあるような気がして作る側としては気が滅入りますね。

Google App Engineで不要なインデックスを削除する方法

Google App Engineのストレージシステム(Datastore)は一般的なRDBMSのようにインデックスを貼ることが可能です。
(というか、インデックスを貼っていないクエリを本番環境で発行すると例外が投げられます)

以下、Python Datastore APIの話


インデックスの設定はきわめて簡単で、基本的にはすべて自動で行われます。

開発 Web サーバーを使用している場合、インデックス設定は容易に実行できます。必要なインデックスが存在しないクエリを実行すると、操作は失敗しませんが、逆に開発 Web サーバーがクエリを成功させるために必要なインデックス設定を生成します。アプリケーションのローカルテスト時に、アプリケーションが作成する可能性のあるクエリ(種類、祖先、フィルタ、並び替え順序のすべての組み合わせ)をすべて呼び出す場合、生成されたエンティティはインデックスの完全なセットを表します。テストですべての可能性のあるクエリ形式を実行しない場合、アプリケーションをアップロードする前にインデックス設定を見直し、調整することもできます。
クエリとインデックス - Google App Engine

例えば下のような場合
from google.appengine.ext import db

class Person(db.Model):
nickname = db.StringProperty()
age = db.IntegerProperty()
query = Person.all()
query.filter('nickname =', 'Foo')
query.('age =', 30)
query.fetch(10)

開発環境で上のコードを実行すると、index.ymlに次のようなインデックスが自動で設定されます。
- kind: Person
properties:
- name: age
- name: nickname

上のモデルにaddress(住所)を追加するとどうなるでしょう
class Person(db.Model):
nickname = db.StringProperty()
age = db.IntegerProperty()
address = db.StringProperty()

query = Person.all()
query.filter('nickname =', 'Foo')
query.('age =', 30)
query.('address =', 'Tokyo')
query.fetch(10)

index.ymlに新しいインデックスが追加されます。
- kind: Person # (1)
properties:
- name: age
- name: nickname


- kind: Person # (2)
properties:
- name: address
- name: age
- name: nickname

では、最初に設定したインデックス(1)を削除するにはどうすればいいでしょう?

index.ymlから該当部分を消しただけでは、デプロイしてもインデックスが削除されることはありません。
index.yaml からインデックスを変更または削除する場合、元のインデックスは App Engine から自動的には削除されません。そのため、新しいインデックスの作成中に古いバージョンのアプリケーションを実行させることや、新しいバージョンで問題が発生した場合、すぐに古いバージョンに戻すことができます。
使用していないインデックスの削除

index.ymlから使わなくなったインデックスを消去して、appcfg.pyを叩けばインデックスを削除することができます。
古いインデックスが不要になった場合は、次のコマンドを使用して App Engine から削除できます。
appcfg.py vacuum_indexes myapp/

このコマンドは、最近アップロードされたバージョンの index.yaml で指定されていない、すべてのアプリケーションのインデックスを削除します。
使用していないインデックスの削除

appcfg.py vacuum_indexesを叩くと削除候補のインデッックスを本当に削除するか確認してくるので、削除する場合は「y」を入力。
DashboardのDatastore - indexesのステータスがdeletingに変わります。


インデックスを削除してしまうとバージョンの互換性が失われる恐れがあります。
(先述したように、インデックスのないクエリを発行すると例外が投げられる)

ですのでインデックスを削除する際は十分に注意する必要があります。


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

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にするなど。

2009年11月25日水曜日

ListCamp リニューアルしました

一年以上前に作ってそれから放置していたサービスに久しぶりに手を入れてみました。



ListCamp
http://jp.listcamp.com

いろんな情報をまとめてリスト化できるサービスです。

作れるリストは例えばこんな感じ
パワーポイントやPDFを共有できるWebサービスまとめ | ListCamp


新しく追加/修正したもの

OpenIDに対応
画像の指定が簡単にできるようになった
YouTube ニコニコ動画などのURLが指定された場合は自動でembed
コメントの許可/不許可設定ができるようになった



このサービスを作っている当初はAmazonのUnSpun(すでに閉鎖)みたいにラインキングをフィーチャーしたリスト作成サービスみたいなものを目指してましたが、それだと「人が集まってなんぼ」なんですよね。
そういうわけで、ランキングもあるけれどシンプルに「情報をまとめられるツール」っていう方向で行こうと思います (漠然としすぎてますが)。

ソーシャルブックマークで管理するとおさまりがつかなくなるけどまとめておきたい情報 (好きな映画のレビュー記事とか) をコンパクトにまとめられるサービスができたらいいな。
TwitterのつぶやきをまとめられるTogetterの汎用版みたいな感じで。

2009年11月9日月曜日

Jetpackが熱い!Greasemonkey終わったな

数ヶ月前にローンチされてから音沙汰がなかった(わけではないけれど、Googleで検索して出てくるのは当時のニュースやブログばかり)Jetpackですが、先日リリースされたversion0.6の解説ビデオを見て興味を持ったのでご紹介

Jetpack Menu API Tutorial from Aza Raskin on Vimeo.




軽く触ってみた感想ですが、こりゃ作り心地も使い勝手もいいです!

FirefoxのURLバーにabout:jetpackと打てばそのまま実行環境ができるし、Firebugとの連携ばっちし。
なによりjQueryを使えるってのが心強い。
インストールする時も再起動する必要ないのがうれしい。

ブラウザをちょっと便利にしたいな〜って時に重宝しそうです。



ちなみに、Jetpackは現在Firefox3.5のみに対応するアドオンとして提供されていますが、Firefoxの次期バージョンには正式に搭載されるそうです。

Mozilla Labsのユーザーエクスペリエンスチーフに聞く--FirefoxとWebの未来


Mozilla Labs Jetpack

https://jetpack.mozillalabs.com/