ラベル Google App Engine の投稿を表示しています。 すべての投稿を表示
ラベル Google App Engine の投稿を表示しています。 すべての投稿を表示

2009年12月16日水曜日

bulkloader.py を使って Google App Engine の本番サーバーから開発サーバーにデータを移す

Google App Engine SDKの開発サーバーのデータストアはtmpディレクトリにデータを保存するため、マシンを再起動するとデータの中身が全部消えてしまいます。毎回テストデータを用意するのが面倒なので、本番サーバーからデータを移してくるための方法を調べてみました。するとbulkloader.pyというユーティリティを使うと、簡単に本番サーバーのデータをダウンロードして保存し後からリストアすることができることがわかりました。ということで早速試してみます。
参考にしたのは以下のサイト。
http://code.google.com/intl/en/appengine/docs/python/tools/uploadingdata.html

※2010/01/31追記:--db_filenameオプションの使い方を掲載しました。


■前準備
app.yamlに以下の設定を追加してから、appcfg.py updateで本番デプロイを行います。
- url: /remote_api
script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py
login: admin
これだけで準備ばっちりです。余談ですが、app-engine-patchを使うと、最初からこの設定がapp.yamlに含まれています。


■使用方法
本番サーバーからローカルにデータをダンプするときは以下のコマンドを実行します。
bulkloader.py --dump --kind=<kind> --url=http://<appname>.appspot.com/remote_api --filename=<data-filename>
たとえば私のアプリからhon_heroという名前のModelのデータをダンプしたいときには、
bulkloader.py --dump --kind=hon_hero --url=http://akisutesama.appspot.com/remote_api --filename=hon_hero.dump
などとするとうまくいきます。

ダンプしたデータを本番サーバーにリストアするときは以下のコマンドを実行します。
bulkloader.py --restore --kind=<kind> --url=http://<appname>.appspot.com/remote_api --filename=<data-filename>
--dumpが--restoreになっただけです。本番障害でデータが失われたときにリストアするためのコマンドなので、実行すると本番サーバーのデータがすべてダンプした地点のデータに置き換えられます。ご利用の際には細心の注意を!

ダンプしたデータをローカルサーバーに入れるときは以下のコマンドを実行します。
bulkloader.py --restore --kind=<kind> --url=http://localhost:8080/remote_api --filename=<data-filename> --app_id=<appname>
本番だけではなくてローカルサーバーにもリストアができます。ローカルサーバーにリストアする際の注意点として、
  • dev_appserver.pyでアプリケーションが動いている必要があります。
  • --urlオプションをローカルホストに書き直す必要があります。
  • --app_idオプションで、自分のアプリケーションのapp_idを指定してやる必要があります。
これでローカルテストがだいぶ楽になりました。

同様にして、本番からデータをダンプしてローカルに入れるだけでなく、ローカルからデータをダンプして本番に入れることもできます。新しく実装した箇所に最初からデータを入れたい場合などに使えると思います。


■log, progress, resultを賢く使う
bulkloader.pyを実行すると、デフォルトでは以下のような名前のファイルが自動的に生成されると思います。
bulkloader-log-20091211_145622.log
bulkloader-progress-20091211_145622.sql3
bulkloader-result-20091211_145622.sql3
これらはそれぞれ「ログファイル」「現在どのエンティティまでダンプされたかを格納するデータベース」「実際にダンプした内容を格納するデータベース」となっており、上手く使えば2回目以降のダンプ実行時間を劇的に短くすることができます。

ログファイルを指定する際には--log_fileオプションを、データベースを指定する際には--db_filenameと--result_db_filenameを、それぞれ指定してください。たとえば、以下のようなシェルスクリプトを用意しておくと便利です。
#!/bin/sh

# 前回の実行結果が残っているとエラーになる(上書きしてくれない)ので、まずいったん消す
if [ -e hon_hero.dump ]; then
rm hon_hero.dump
fi
# bulkloader.pyを実行する
# db_filenameとresult_db_filenameは一緒のファイルを指定してもかまいません。
# (その場合は、一つのsqlite3データベースに一緒に格納されます)
# db_filenameとresult_db_filenameは、db_fileやresult_db_fileのように短縮して書いても動作するみたいです
bulkloader.py --dump \
--url=http://akisutesama.appspot.com/remote_api \
--kind=hon_hero \
--filename=hon_hero.dump \
--log_file=bulkloader-log-hon_hero.log \
--db_file=bulkloader-progress-hon_hero.sql3 \
--result_db_file=bulkloader-progress-hon_hero.sql3
こうすることで、2回目以降はbulkloader-progress-hon_hero.sql3の中身とリモートのdatastoreの中身を比較して、追加更新のあったもののみをダウンロードし、それ以外はローカルに保存してあるデータベースの中身を利用するので、劇的に処理が速くなります。結果はこちら。
[INFO    ] Connecting to akisutesama.appspot.com/remote_api

[INFO ] Have 2610 entities, 2610 previously transferred
[INFO ] 2610 entities (974 bytes) transferred in 0.4 seconds
1回目のロードには130秒かかったのですが、2回目はすべてローカルに保存されているデータを使ったので、なんと0.4秒で済んでいます。

こうしてダンプしたデータをローカルサーバーでリストアするときは、たとえば以下のようなシェルスクリプトを使います。
#!/bin/sh

# --db_fileと--result_db_fileには先ほどのダンプ時に指定したものとは別のファイルが必要になります
# (先ほどのデータベースはappspot.com用として設定されるので、localhostで使用するとエラーになってしまいます)
# --db_fileと--result_db_fileには特別な名前としてskipを指定することができます
# この名前を使用するとデータベースへの書き込み/読み込みを行いません
bulkloader.py --restore \
--url=http://localhost:8000/remote_api \
--app_id=akisutesama \
--kind=hon_hero \
--filename=hon_hero.dump \
--log_file=bulkloader-log-restore.log \
--db_file=skip \
--result_db_file=skip


■注意点
appspot.com以外のサーバー(たとえばlocalhostなど)からデータをdumpしたりrestoreしたりする際には、必ず--app_idオプションを指定する必要があります。これを忘れていて詰まりました><

もし特定の条件を満たすデータのみをダウンロードしたいという場合などは、設定ファイルをPythonで書く必要がありますが、appcfg.py download_dataを使うと良いと思います。

2009年12月1日火曜日

Google App Engine SDK の dev_appserver.py が自動的に index.yaml を更新してくれない時の対処法

ときどきGoogle App Engine SDKのdev_appserver.pyが自動的にindex.yamlを更新してくれない時があるので、原因を調べてみました。


■いきなりネタバレ
犯人は改行文字


■前提条件
index.yamlは、以下の様な作りになっています。
indexes:

- kind: django_admin_log #手動で定義したインデックス
properties:
- name: content_type
- name: object_id
- name: action_time

# AUTOGENERATED

# This index.yaml is automatically updated whenever the dev_appserver
# detects that a new type of query is run. If you want to manage the
# index.yaml file manually, remove the above marker line (the line
# saying "# AUTOGENERATED"). If you want to manage some indexes
# manually, move them above the marker line. The index.yaml file is
# automatically uploaded to the admin console when you next deploy
# your application using appcfg.py.

- kind: hon_herostatistics #ここから下は、自動的にdev_appserver.pyが追加してくれるindex
properties:
- name: fetchCompleted
- name: hid
- name: datetimeCreate
direction: desc
このように、# AUTOGENERATEDコメントが存在すれば、その下に自動的に必要なindexを生成してくれるようになっているのですが、時々この自動生成がうまくいかない場合があります。


■症状
具体的には、以下のように余計な物をすべて消した状態のindex.yamlを用意しても、dev_appserver.pyが「indexが手動定義されているので、自動生成しないよ」とエラーを吐きます。
indexes:

# AUTOGENERATED
困りました。


■原因と対処
そこでよくよく調べてみるためにvimではないエディタでこのindex.yamlを開いてみたのですが、すると末尾の改行文字がCRLF(dos形式)になっていました。そこで私のMacの環境に合わせて改行文字をLF(unix形式)にしてみたところ、無事にdev_appserver.pyのindex自動生成が動作するようになりました。vimで操作するなら、
:set fileformat=unix
とすればよいです。

2009年5月2日土曜日

Google App Engineで、index.yamlに記述したインデックスが正しく生成されないときの対処法



Google App EngineでDatastoreのindexを利用したアプリケーションを作成していたのですが、運悪くindexの生成がいつまで経っても終わらない現象に見舞われてしまいました。データ量はたかだか1000件程度しかないのですが、3日経ってもindexの状態が"Building"のまま。Googleグループの書き込みを見ると(http://groups.google.co.jp/group/google-appengine/search?group=google-appengine&q=index)、indexの生成に失敗するというケースがたびたび発生しているようです。
  • index対象となるモデルの数は全く関係がない。数件しか保存されていないモデルに対してindexを生成しても発生する。
  • Googleの中の人曰く、indexはアプリ開発者全体で共有のindex作成クローラーによって行われているため、全体的に一度に利用する人が増えると不安定になる。

■まずはappcfg.pyを利用して、indexを消して再生成してみる
まずはindexをいったん消して再作成することにより、自分の手で解決できないかどうか試してみます。こちらの記事(http://osima.jp/blog/gae-index-in-trouble.html)を参考にさせていただきました。

まずは、index.yaml中のトラブルを起こしているindexをコメントアウトして削除し、
# Get a list of articles for "all" news page.
#- kind: Article
# properties:
# - name: is_duplicated
# - name: date_update
# direction: desc

ターミナルから以下のコマンドを実行。
appcfg.py --force vacuum_indexes /path/to/my/application

うまくいけば、管理者コンソールのIndexesメニューを開くと、対象のindexが"Deleting"という状態になっているはずですので、しばらく待って完全にindexが消えてからindex.yaml中で再度コメントアウトした記述を復活して、
appcfg.py update_indexes /path/to/my/application

を実行すれば、無事にindexが再作成されます。


■それでもだめなら、GoogleグループからGoogleの中の人にお願いして消してもらう
これでうまくいけば万事解決なのですが、ときどきこの方法ではうまくいかない場合があります。たとえば私の場合は、下記のようなエラーが出て上手くindexの削除が出来ませんでした。
akisute $ appcfg.py --force vacuum_indexes .
Fetching index definitions diff.
Deleting selected index definitions.
2009-05-02 12:48:33,476 WARNING appcfg.py:670 An index was not deleted. Most likely this is because it no longer exists.

kind: Article
properties:
- name: is_duplicated
- direction: desc
name: date_update

このような場合は、2009/05/02現在、米国本家のGoogle App Engineグループ(http://groups.google.co.jp/group/google-appengine)に直接お願いしてGoogleの中の人によってindexを削除してもらうしかないようです。以下の点に気をつけて新規ディスカッションを投稿すれば、スムーズにindexを削除してもらえると思います。
  • indexの生成がBuildingのまま進まない、24時間以上経っているのにうんともすんとも言わないことを明記する
  • appcfg.py vacuum_indexを試したけれどもうまくいかなかったことを明記する
  • 自分のアプリのApp IDを載せる
  • どのindexを削除してもらいたいかを載せる
参考までに、Google App Engineグループをindexで検索したときの結果(http://groups.google.co.jp/group/google-appengine/search?group=google-appengine&q=index)を載せてみました。同様の症状例がたくさん報告されていますので、それらの文面を適当にパクって参考にして投稿するのをおすすめします。
ただし、
Before posting, please read our Charter. Please note that due to recent spam activity, a member's first post will be moderated by one of the group's managers.
と注意書きがあるとおり、現在Spam対策として、最初の1回目の投稿はモデレータによってチェックされてしまい、すぐに投稿が反映されないみたいです。投稿されたとしても、いつindexの削除をしてくれるかはGoogleの中の人の気まぐれになってしまうので、解決には少々時間がかかります。


■英語が苦手なら、日本のコミュニティに助けてもらおう!
どうしても英語が苦手なら、日本のGoogle App Engineコミュニティ(http://groups.google.co.jp/group/google-app-engine-japan)に相談してみるとよいかもしれません。たとえば、こちらの投稿(http://groups.google.co.jp/group/google-app-engine-japan/browse_thread/thread/289b87a344715ea1/c9b5f8e394ac6a47)が参考になります。


■でも本当は
早くこんなトラブルが起こらないようなシステムになってほしいです><

2009年4月13日月曜日

Google App Engineのcronは、users.is_current_user_admin()では認証できない

先日作成したまとめの内容に間違いがありましたので、訂正してお詫びさせていただきます。

users.is_current_user_admin()を利用して、RequestHandler中にて起動ユーザーがcronかどうかを判定することが出来ると記事に掲載しておりましたが、
実際には出来ない模様です。

以下の方法にて検証を行いました。
1.まずはこのようなRequestHandlerを作る
class CronFetch(webapp.RequestHandler):
    def get(self):
        if not users.is_current_user_admin():
            logging.warn('CronFetch invoked by non-admin user:%s' % users.get_current_user())
            return
        logging.info('CronFetch begin')
        # この間に処理を記述
        logging.info('CronFetch end')

2.cron.yamlを記述する
cron:
- description: Test cron
  url: /cron/fetch
  schedule: every 1 hours
  timezone: Asia/Tokyo

3.本番環境にデプロイして、admin consoleから確認してみる


ごらんの通り、users.get_current_user()の結果がNoneになります。
おそらく2009/04/13現在では、Google公式のドキュメントにあるとおりapp.yamlで指定する以外にcron起動かどうかの認証を行う方法はなさそうです。

2009年4月9日木曜日

Google App Engineのcronサポートまとめ



http://code.google.com/intl/en/appengine/docs/python/config/cron.html

■注意
以下の情報は2009/04/09現在(Google App Engine 1.2.0.0)の情報です。皆様がごらんになっているときには古くなっている可能性があります。



■時間指定方法
cron.yamlを書く
本家cronとは記法が異なるが、本家cronとほぼ同様のスケジューリングが可能
起動タイムゾーンの指定も可能
cron:
- description: daily summary job
  url: /tasks/summary
  schedule: every 24 hours
- description: monday morning mailout
  url: /mail/weekly
  schedule: every monday 9:00
  timezone: Australia/NSW

■認証
cronによって起動されたURLはadminユーザーによって起動されるため、以下の
2種類の方法で認証が可能
app.yamlのhandlers要素にlogin: adminを指定する
application: hello-cron
version: 1
runtime: python
api_version: 1

handlers:
  - url: /report/weekly
    script: reports.py
    login: admin

2009/04/13追記、以下の方法は利用できません。詳しくはこちらの記事をご覧ください。ご迷惑をおかけいたしまして申し訳ありませんでした。
アプリケーションのRequestHandler内で、users.is_current_user_admin()を利用して判定する(こちらの方法は未確認)
from google.appengine.api import users

# ログインしていない場合やadmin以外の場合にはFalse。adminがログインしている場合のみTrue。
if users.is_current_user_admin():
    # cronジョブをここで実行
else:
    # エラー画面を表示するなど

さらにcronはURLを起動する際にHTTPリクエストヘッダーに以下の要素を付けるため、RequestHandler内でヘッダをチェックすることが出来る
X-AppEngine-Cron: true

■cronタスクの管理方法
cronタスクをアップロードするには、通常のアプリケーションをアップロードするときと同様に以下のコマンドを実行すれば、アプリケーションと一緒にアップロードされる
appcfg.py update

cronタスクだけをアップロードしたい場合場合は以下のコマンドを実行
appcfg.py update_cron

cronタスクを削除したい場合は、cron.yamlを以下のように書き換えてからアップロードすればよい
cron: 

以下のコマンドを実行すれば、現在のcronの設定状況を見ることが出来る(ただし、タイムゾーンを扱う場合はpytzライブラリが必要)
appcfg.py cron_info

■制限
  • 今のところPython版のみ(Java版はない)Java版もある。Java版はcron.xmlを記述する。http://code.google.com/intl/ja/appengine/docs/java/config/cron.html
  • タスクは20個まで
  • 最短実行間隔は1分
  • cron.yamlから起動できるのは自アプリのURLのみ。人のアプリを起動したり、全然関係ないURLを指定したり、シェルスクリプトを起動したりは出来ない
  • cronから対象URLを起動した場合のCPU時間・通信量もすべて従量対象になる

2009年2月15日日曜日

jweekyforecast近況報告(2009/02/15)


ようやく予報区のマスターメンテができるようになりました。
天気予報区のデータをアプリケーションに登録して、それをAPIを通じてiPhoneに引き渡すというような処理を行おうと思っています。


天気予報区には大きく分けて一次細分区域と二次細分区域がありますが、今のところ一次細分区域しか対応していません。二次細分区域を対応させようと思ったら親子関係を持たせないとなぁ。

何より問題はこのマスターメンテが死ぬほど面倒くさいことかなぁ・・・と思います ;(

さてさて、次はAPIを作らないとシステムとして成り立たないですね。

2009年2月12日木曜日

jweeklyforecast (server) のリポジトリを公開しました

http://github.com/akisute/jweeklyforecast/tree/master

先日話題にいたしました、天気予報用API提供用サーバーjWeeklyForecastのリポジトリを作成いたしましたので、取り急ぎご報告だけさせていただきます。


ちなみに中身はスッカスカです。まだGoogle App Engineの本番サーバーにもアップできていない状況なので、何とか今週末までにはアップしたいです!

2008年12月14日日曜日

第2回 Google App Engine Hackathon

  • 今回で第2回目
  • 主催は@tmatsuoさん
  • 4チーム(内部でさらに分かれるため合計6チーム)に分かれて作業をし、最後に発表する形式
  • 1回目よりも運営が格段にスムーズで素晴らしい時間を過ごせた
  • GDataの認証はすさまじく難しい。またGDataアクセス用のライブラリがあるが、これまた非常に機能が多く難しい
  • Google App Engine Oilは凄く良い、余計なことをしないし必要な者は全部作ってくれる
  • gaeogenはバージョン0.21の地点ではバグがあるためまともに機能しない
  • DropBoxを使ったグループ開発はすさまじいスピード感がある

http://groups.google.co.jp/group/google-app-engine-japan?hl=ja
ちょっと遅くなりましたが、2008年12月13日に開催されました第2回 Google App Engine Hackathonについてご報告です。

主催は前回に引き続きGoogleの認定エキスパートである@tmatsuoさん。
前回は好き勝手にGAEで物を作るだけという感じだったのですが、今回は趣向を変えて、
  • チュートリアル:RSS Reader作成
  • チュートリアル:Twitterもどき作成
  • GDataを用いたマッシュアップ:Bloggerなどとマッシュアップする組
  • GDataを用いたマッシュアップ:Picasaなどとマッシュアップする組
  • フレームワーク
  • データモデル
以上の6組に分かれてそれぞれ活動し、結果を最後に発表すると言う形式で行われました。
目標がはっきりしていたおかげで、最後にはものすごいアプリが次々飛び出しました。大成功だったと思います!
運営の皆さんありがとうございました!
前回に引き続きお弁当おいしかったです =)


詳細につきましてはHiiroさんのブログをご参照ください。
Hiiro_memo: GAEハッカソン参加感想&まとめ:Google凄いが周辺人も凄い


私はマッシュアップ班に参加いたしました。
ちなみに隣の席が世界に名だたるおっぱいプログラマー@technohippyさんでした。びっくりです。
Da変態な人かと思っていたら、意外と普通の人でした。Vistaに泣かされていましたけど。

今回のマッシュアップ班の開発の際には、DropBoxに全員のプロジェクトファイルを配置して、いつでもみんなのファイルを参照できるようにして作業しました。
コレが凄くいいです。誰かが一人難所をクリアできれば、すぐにみんながそのソースを参照して動かせるように出来たため、作業効率が格段に良かったです。
(共有する人はコミット作業すら必要ありません。全部自動的に同期してくれるから)

さっそくDropBoxが大のお気に入りになりました。Windows機にはあまり導入したくなかったのですが、
こんなに便利なら使うしかないですね。


また、噂のGoogle App Engine Oil(GAEO)も試してみました。
gaeo testproject

とすると、testprojectを作成してくれるのですが、コレが素晴らしい!
最初から絶対に必要になるjs, img, cssフォルダを作成してくれたり、faviconの設定もしてくれたり、かゆいところに手が届く感じです。
RequestHandlerの実装も非常に簡単になっていて、
class WelcomeHandler:
  def index(self):
      #get All models from DataStore
      models = AModel.all().fetch()
      #set models to self object to use it when rendering the template
      self.models = models

たったのこれだけで、テンプレートのレンダリングまで全部自動でやってくれます。

本当はgaeogenというrailsのscaffoldのような機能があるのですが、こちらは0.21の段階ではバグがあるようでまともに機能しませんでした。
(ソースを参照したところ、argvに対してgetメソッドを呼び出したりしていました。
Pythonのargvはlist型だからdict型にしか使えないgetメソッドは利用できないです)
でもgaeoだけでも十分すぎるほど便利ですから導入する価値は有りだと思います。