Showing posts with label GAE/J. Show all posts
Showing posts with label GAE/J. Show all posts

2015-08-05

GAE の Datastore を Master/Slave から High Replication (HRD)へ移行する方法

管理しているウェブアプリ Japanese Bloggers Info が動かなくなっていたので、ようやく重い腰を上げて、データストアの移行に乗り出した。それが昨日(2015-08-04)のこと。確認してみると、3 年も前から Master/Slave Datastore は非推奨になっていたのね。そりゃ、アプリも止まるわ、と。 GAE ダッシュボードからリンクをたどってドキュメントを見てみると、衝撃のタイムテーブルを発見。
August 3, 2015: You will no longer be able to re-enable your application. However you will still be able to migrate to HRD.
(拙訳:2015-08-03 で、もうアプリケーションは有効にできなくなります。HRD への移行はできますが。 )
無効になっている M/S アプリは、もう有効にできないとのこと。M/S から HRD への移行には、アプリの複製を作って、そこにプログラムをデプロイ、移行ツールを使って元のアプリからデータをコピー、エイリアスを設定という手順を取るのだけれど、肝心のアプリの複製を作る前提として、そのアプリが有効でないといけない。実際に、無効になっているアプリの複製を取ろうとして失敗したのが、次のスクリーンショット。


Sorry, that operation is not allowed while the application is disabled. This Master/Slave application is disabled.
「アプリケーションが無効になってるから無理です」っていうメッセージと、押せない「再度有効に」のボタン。これは八方ふさがりですね…。

で結局、最後の神頼みとて、GAE のサポートにリクエストを送ることにした。 名前とメールアドレス、電話番号、GAE のプロジェクト ID、リクエスト内容を入力して、送信ボタン押下。リクエスト内容は、「I want to duplicate application settings. Please re-enable my application.」と打ってみたけど、まあご参考に。

すると 1 時間もしないうちに、返信メールが届いた。
Hi,
We have re-enabled your app "japanese-bloggers". Please perform the migration as soon as possible. Feel free to get back to us should you have any questions.
Also, I will forward this case to my colleagues in Tokyo if you need any assistance during Japan business hours.
(拙訳:あなたのアプリケーションを再度有効にしました。できるだけ早く移行してください。質問があればご自由にどうぞ。日本の営業時間中に援助が必要な場合、日本の同僚にこのケースを転送します。)
なんと優しいお言葉!で、本来の移行の手順をたどって、無事 Japanese Bloggers Info が復活したというわけ。移行の参考にしたのが、次のブログ(ありがとうございます!)。説明が分かりやすいので、間違えることはないはず。 ただ一点、「Activate Read-only」の段では、データストア移行に時間をかけられないので「Launch Incremental Copy(増分コピーを起動)」を飛ばして、すぐさま「Activate Read-only」にするのがいいかと。すると、移行自体は 1 時間もかからずに終了。なぜこれをサボっていたのかという感じもしたり…。

ということで、僕みたいにズボラな人がそんなにいるとも思えないプラス、同じようにやってできるかも分からないけれど、一応参考にしてもらえたらと。

ちなみに、前掲タイムテーブルの最後には、2015-08-10 に M/S アプリケーションが完全停止し、アクセスできなくなるということが書かれているので、移行作業はぜひお早めに。


追記(2015-08-05):

その後、GAE サポートから再びメール「移行作業は成功しているけど、課金(Billing)が有効になっていないせいで、QUOTA オーバーを起こしているよ」と。なんと親切なご指摘。エイリアスを設定することで、閲覧者はアプリケーションの変更を意識しないで済むものの、基本的には別のアプリケーションなため、新たな課金設定がいるみたい。元々 GAE の無料枠に収まっていなかったという人は、要注意。課金有効化と「Daily Budget(日ごとの課金上限)」設定をお忘れなく。

2013-06-05

Blogger フィードを取ってくる GAE/J アプリで RuntimeException

最近あまり手を付けていなかった GAE/J アプリ Japanese Bloggers Info で、$1.0 / day を超えて 503 Over Quota(サービス停止)が頻発するようになった。

503 Over Quota on GAE/J

タスクキューで呼ばれた Blogger ブログのフィードの確認用サーブレットが何度も止まり、キューがいっぱいになるのが原因らしい。スタックトレースを見るとこんな感じ。

java.lang.RuntimeException: Unable to complete the HTTP request
at com.google.apphosting.utils.security.urlfetch.URLFetchServiceStreamHandler$Connection.getHeader
at com.google.gdata.client.http.HttpGDataRequest.isOAuthProxyErrorResponse
at com.google.gdata.client.http.HttpGDataRequest.checkResponse
at com.google.gdata.client.http.HttpGDataRequest.execute
at com.google.gdata.client.http.GoogleGDataRequest.execute
at com.google.gdata.client.Service.getFeed
at com.google.gdata.client.GoogleService.getFeed

何で、急にこんな例外が出るようになったのかは不明だけれども、とりあえず、

http://example.blogspot.com/feeds/posts/summary?max-results=1&redirect=false

とブログ直下のフィードにアクセスさせていたのを、

http://www.blogger.com/feeds/BLOGID/posts/summary?max-results=1&redirect=false

と、Blogger にブログ ID を使って問い合わせてフィードを得る方法に変えてみたら(参考)、RuntimeException が出なくなった。ブログを登録してもらったときに、フィード URL のほかにブログ ID も保存していてよかったーと思った瞬間。…で、気になるお値段、Billing History もこの通り。

Billing History on GAE/J

日々の課金がみるみる減って、今は元の水準の $0.15 以内で推移している。どうせ最低でも週に $2.10 はかかる仕組みなので(参考)、日に $0.30 以内は許容範囲。釈然としないものをかかえつつも、とりあえずはめでたしと。

2012-02-09

GAE/J で Task Queue の Enforced Rate が極端に低くなる場合

GAE で Task Queue の Enforced Rate が Maximum Rate より極端に下がって、タスクが溜ってしまうときには、サーブレットがマルチスレッドで動くように設定してやると直るかも。

appengine-web.xml で
<threadsafe>true</threadsafe>
とするだけ。 まあ騙されたと思って、一度やってみるよろし。

2011-12-25

GAE、 Billing History に謎の Charge Sent

前回の投稿で、GAE/J の新料金体系への対策をつらつら書いた。できるだけ出費を抑えようと、涙ぐましい努力をしているときに、Billing History で見つけた文字列「Charge Sent $0.51」。なんだこりゃ。

GAE の Charge Sent

12/18 は $0.17 に加えて $0.51?と思って、振り返ってみると、毎週 Charge Sent がかかっているみたい。週の課金累計が $2.70 のときは $0.00、累計が $1.67 のときには $0.43。

どうやら、週の支払いが $2.10 を下回ったときだけ Charge Sent の額で $2.10 になるよう調整されているみたい。

週 $2.10 が最低支払額だとしたら、日に $0.30 は使わないともったいないなと思ったり。

うちは週 $2.10 を超えるか超えないかなのでそんなに気にならないけれど、日々 $0.05 とかでがんばっているところなんかは、Charge Sent で最後に $1.75 かかったりすると、ちょっと納得がいかないかも。

とか、考えながらごそごそ探してみると、GAE のメニュー「Billing Settings」の「Change Budget」に、普通に書いてあった。Minimum Spend というらしい。
* The Minimum spend subtotal is in support of our new pricing model. The new model requires that you spend at least $2.10/week. This subtotal indicates the value beyond your other spend that we need to add to your contract. To make the transition to the new model smoother we are beginning to account for this minimum when we authorize new budget changes. Please note that you will not be charged for the minimum spend until our new model takes effect.
多分ちゃんと読まずに課金設定したんだろうな…、と反省しきり。

ということで、課金している以上は経費週 $2.10 以下なら、出費を抑えるためのチューニングはそれ以上無理にしなくてもいいってことみたいです。

GAE/J 新 Quota と新料金体系への対策

もういろんなところで書き尽くされていると思うのだけれど、自分の記録用として。

対策結果

最初に、Japanese Bloggers Info の Billing History データ。



インスタンスをたくさん使っているものの、この当時はまだ旧 Quota で CPU Time も 6.50 に収まっていたので、課金はなし。で、最終的にこうなった。



結局課金になってはいるものの、日に 7.00 ドルかかるぞ(Frontend Instance Hour は現在の半額での計算)と宣言されていたのが 0.15 ドルになったのは上出来。


対策内容

<1> Idle Instances と、Pending Latency の設定。GAE の設定ページ「Application Settings」を開いて、スライダーを使って Idle Instances の Max Idle Instances を 1、Min Idle Instances を Auto に。Pending Latency の Min Pending Latency を 15、Max Pending Latency を Auto に設定する。これだけで、Frontend Instance Hour が 35 時間くらいに激減。

<2> TaskQueue の rate(実行頻度)を緩和。5/s だったものを 2/s に。Japanese Bloggers Info は、ブログチェックのため、5 分ごとに cron で 300 個以上の TaskQueue を呼ぶ仕組みになっているので、これだけのことでも意外と効果あり。Frontend Instance Hour も大体 30 時間以内に。

<3> Backends を利用。上記の TaskQueue が一日中動いているので、日に 6 時間は Backends で TaskQueue を動かすことに。どうしても 2 インスタンス動くところがあるので、6 時間は必ず超えるけれど、9 時間までは Backends の無料枠があるので、問題なし。というか、この残りの 3 時間で、2 インスタンス動いてしまう時間を吸収してもらう感じ。で、Frontend Instance Hour もほとんど 28 時間に収まるようになったというわけ。

<4> Memcache の積極利用。全ブログのリストや、各ブログのデータなど、5 分ごとで変化しない内容については、Memcache を利用。大幅なデータ取得の仕組みは改修せず、Entity にアクセスする前に Memcache を確認するようにしただけ。Memcache になければ、通常通り Entity からデータを得て、Memcache に入れておく。それだけで、Datastore Reads は 0.27 ドルから 0.08 ドルへ、約 3 分の 1 に。

で、現在に至る、と。どれもちょっとしたことなので、誰にでもできるはず。よかったら参考にしてください。

2011-12-03

Eclipse で GAE/J の SDK を 1.6.0 に更新したらエラーが

Eclipse の Google App Engine SDK をアップデートして、「ウィンドウ > 設定」から SDK を 1.6 に変更してみたところ、問題ビューに

The App Engine SDK JAR appengine-api-1.0-sdk-1.6.0.jar is missing in the WEB-INF/lib directory
The App Engine SDK JAR appengine-api-labs-1.6.0.jar is missing in the WEB-INF/lib directory
The App Engine SDK JAR appengine-jsr107cache-1.6.0.jar is missing in the WEB-INF/lib directory
The App Engine SDK JAR datanucleus-appengine-1.0.10.final.jar is missing in the WEB-INF/lib directory

というエラー(Google App Engine Problem)が。

プロジェクトのプロパティーから「Java のビルド・パス」を選択「順序およびエクスポート」タブの「App Engine SDK [App Engine (*) - 1.6.0]」をチェックして、「OK」押したら治りました。

…けど、更新のたびに、こんな設定していたっけ?

関連:

2011-06-09

GAE/J で XPath を使って XML 解析

今まで使ったことがなかったのだけれど、XML を解析するのに XPath がめちゃくちゃ便利。
ブログ ID +ポスト ID で投稿のタイトルや URL を取得しようと思っていたのが、ちょっぴり挫折。URLFetch して、XML のパーサを使うか、正規表現でゴリゴリするか、迷い中…。
って前回悩んでいたのだけれど、その URLFetch で得た Blogger の個別投稿フィードを XPath で解析するコードが、ほらこの通り。
/* フィードを取得して InputStream に */
String feed = getSiteText("http://www.blogger.com/feeds/" + blogId + "/posts/summary/" + postId);
InputStream is = new ByteArrayInputStream(feed.getBytes("UTF-8"));

/* Evaluator を生成し、タイトル、サマリー、URL、公開日時を取得 */
XPathEvaluator evaluator = new XPathEvaluator(is);
String title = evaluator.getString("//title");
String summary = evaluator.getString("//summary");
String url = evaluator.getString("//link[@rel='alternate']/@href");
String published = evaluator.getString("//published");
って、非常に簡単に XML のデータを取得できる。ちなみに、getSiteText(String) は、以前に書いたこれ。

public static String getSiteText(String url, String charset) throws IOException {
    return new String(URLFetchServiceFactory.getURLFetchService().fetch(new URL(url)).getContent(), charset);
}
public static String getSiteText(String url) throws IOException {
    return getSiteText(url, "UTF-8");
}
で、一番大事な XPathEvaluator は、こちらで公開されている XPath のラッパークラス。 便利な時代になったものです。ありがたやー。

2011-05-29

GAE/J で com.google.gdata.util.ParseException: Invalid root element とか

Blogger には、個別投稿フィードなるものがあるので、
GoogleService gs = new GoogleService("blogger", "kuribo-japanesebloggers-1");
URL feedUrl = new URL("http://www.blogger.com/feeds/6813881014503035656/posts/summary/552385261504346376");
Feed feed = gs.getFeed(feedUrl, Feed.class);
Entry entry = feed.getEntries().get(0);
GAE/J で GoogleService を使って Blogger の個別投稿フィードを読み込ませようとしたら、

com.google.gdata.util.ParseException: Invalid root element, expected (namespace uri:local name) of (http://www.w3.org/2005/Atom:feed), found (http://www.w3.org/2005/Atom:entry

とかって言われてへこむ。なんだなんだと調べてみると、Blogger の個別投稿フィードは、通常の投稿フィード
<feed>
  <id/>
  <updated/>
  <title/>
  <subtitle/>
  <link/>
  <author/>
  <generator/>
  <entry/>
</feed>
のような構成ではなく、上記 entry 部からいきなり始まる形で、その投稿以外のブログ情報が全く掲載されないようになっているみたい。

ブログ ID +ポスト ID で投稿のタイトルや URL を取得しようと思っていたのが、ちょっぴり挫折。URLFetch して、XML のパーサを使うか、正規表現でゴリゴリするか、迷い中…。

2011-02-20

GAE/J、PreparedQuery#countEntities() ではまっていた件

GAE/J で運営しているサイト Japanese Bloggers Info で、ようやく登録ブログ件数が 1000 件になった。

Japanese Bloggers Info、登録ブログが 1000 件になった。やった! http://japanese-bloggers.appspot.com/Tue Feb 15 18:28:37 via web


サイトトップの
このサイトでは、日本語の Blogger ブログの更新情報を紹介しています。現在、1000 個のブログが登録されています。
という表記でブログ件数を見ていたのだけれど、ここ数日「1000 個」から増えないので、不思議に思ってコードを見てみると、
PreparedQuery pq = ds.prepare(query);
pq.countEntities();
みたいになっていました。そういえば、PreparedQuery#countEntities() は 1000 超えの数を返さない、と噂になっていましたね…。こちらを参考にしつつ、
PreparedQuery pq = ds.prepare(query);
pq.countEntities(FetchOptions.Builder.withOffset(0).limit(Integer.MAX_VALUE));
と変えておきました。すると、すぐさま 1035 件に。いったい、どれだけ放置していたんだろうか…(汗)。

2011-02-06

Japanese Bloggers Info にトラックバック送信機能をつけた。

表題のとおり。Japanese Bloggers Info にトラックバック送信機能を設置。送信先は問わないけれど、送信元は Japanese Bloggers Info に登録しているブログのみ。登録されている最新投稿の内容を、トラックバック Ping で送れるようにしたもの。
今回作った JQuery を使ったページは、Japanese Bloggers Info の、登録 Blogger ブログの投稿データからトラックバックを送るページに使用。今途中のトラックバックを受け取る方の仕組みを完成させたら、公開する予定。
と以前書いたのだけれど、受信機能のほうはいつ完成するのかわからないので、送信機能のみ beta 版として、とにかく公開。 参考にした、トラックバックの仕様はこちら。 変なところがあったら、指摘してもらえると助かります。

2010-12-05

Google App Engine 1.4.0 へのアップデートではまる…。

というわけで、1.4.0 SDK へ更新しようとした矢先、eclipse のコンソールにこんなメッセージが…。
インストールする項目の収集中にエラーが発生しました
  No repository found containing: org.eclipse.jst.server.core/osgi.bundle/1.2.0.v20090421
  No repository found containing: org.eclipse.wst.server.core/osgi.bundle/1.1.102.v20090825
大分アップデートしていなかったから、なんかほかのを先にしなくちゃいけないのかな、と色々更新してからもう一度チャレンジしてみたら…、
インストールする項目の収集中にエラーが発生しました
  No repository found containing: com.google.appengine.eclipse.core/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gdt.eclipse.core/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gdt.eclipse.platform/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gdt.eclipse.platform.e34/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gdt.eclipse.platform.shared/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gdt.eclipse.suite/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gwt.eclipse.core/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gwt.eclipse.oophm/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.appengine.eclipse.sdkbundle.1.4.0/osgi.bundle/1.4.0.v201012021501
  No repository found containing: com.google.gdt.eclipse.maven/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gwt.eclipse.sdkbundle.2.1.0/osgi.bundle/2.1.0.v201010280047
ふ、増えてるし…。

ということで、結局 Google 先生のお世話になることにして、こんなページを発見。 書かれているのとは少し違う方法だけれど、eclipse で「ヘルプ > ソフトウェア更新 > サイトの管理」とすすみ、「エクスポート」で bookmarks.xml を保存。

全部のサイトを選択後「除去」して、さっきのファイルを「インポート」…、っていう風にするとできましたとさ。めでたし、めでたし。

2010-10-15

TaskQueue が Lab 卒業へ

TaskQueueが11月にLabを卒業するとな。 http://ow.ly/2Th9p と同時にクオータ制限が掛かるそうだ。 #appengineless than a minute ago via HootSuite


まだ Lab から出てなかったんだという驚きもありつつ、「Task Queue API Calls」に「Task Queue Stored Task Count」、「Task Queue Stored Task Bytes」の 2 つの Quota が加わるというのは、やはり痛手に感じたり。 今ですらこんなになっているときがあるというのに…、大丈夫なんだろうか…。

Tasks Strage Quota

2010-08-15

GAE/J、AuthSub で AuthenticationException …

アクセス解析にて「Japanese Bloggers Info 登録できない」という検索フレーズを発見、「えっ」と を確認してみると、確かにアプリケーションにブログを登録できず…。

問題は Blogger に所有ブログを問い合わせる際の AuthSub 認証で
com.google.gdata.util.AuthenticationException
が出ていたこと。

Google に登録している証明書の期限が切れたのかな? SSL は?とチェックしてみるも問題なし。うんうんうなること 10 数分…。最終的に、Google 先生に教えてもらいました。
String token = AuthSubUtil.getTokenFromReply(req.getQueryString());
token = URLDecoder.decode(token, "UTF-8");
URLデコードしてやれば良いようです。
これまでちゃんと動いていたんだけど、いつからデコードが要るようになったんだろう(汗)?アクセス解析をつけていて良かったとひしひしと感じた今日この頃。

検索してくれた人、ありがとう。プラス、この間ブログを登録できなかった人、すみません。

2010-06-13

GAE/J の AuthSub で This website has not registered with Google to establish a secure connection for authorization requests. の警告をなくす方法

GAE アプリから、 AuthSub 認証を使って Blogger などのデータにアクセスする際に出る警告がこちら。
This website has not registered with Google to establish a secure connection for authorization requests. We recommend that you continue the process only if you trust the following destination:
(このウェブサイトは認証リクエストに対して保護された接続を確立するよう Google に登録されていません。次のサイトが信頼できる場合のみ処理を続行してください:)
これを出なくさせたいなと思って調べてみたら、こちらがヒット。 そのままズバリなので、これ以上書く必要もないのだけれど…まあ一応。

ウェブサイトの登録

まず にアクセスして、「Add a New Domain」で自分の GAE アプリのドメインを入力する。「Manage registration」に「Manage YOURAPP.appspot.com」のように、入力したドメインが表示されるので、それをクリック。

Google Accounts Authentication API - Terms and Conditions を確認して、「I agree to the Terms of Service」ボタンを押下。ウェブサイトの管理ページが開くので、「Target URL path prefix」に認証後のトークン送信先 URL を入力し「Save」を押すと、Google へのウェブサイト登録が完了。

(場合によっては、ウェブサイトの「所有権の確認」が要る場合も。メタタグをサイトに追加するか、指示されたファイルをアップロードするかして、ウェブサイトの所有権を確認。)

再び AuthSub 認証を使ってみると警告文が変わっているはず。
This website is registered with Google to make authorization requests, but has not been configured to send requests securely. We recommend that you continue the process only if you trust the following destination:
X.509 自己署名証明書

「Google に登録されてはいるけれど、セキュアじゃないよ。」に警告が変わったので、次はセキュアトークンを送ってもらうための準備。 を参考に、X.509 の Self-signing Certificate を取得。Java の keytool が便利だった。
# Generate the RSA keys and certificate
keytool -genkey -v -alias Example -keystore example
  -keyalg RSA -sigalg SHA1withRSA
  -dname "CN=www.example.com, OU=Engineering, O=My_Company, L=Mountain  View, ST=CA, C=US"
  -storepass changeme -keypass changeme
keytool -export -rfc -keystore example -storepass changeme -alias Example -file mycert.pem
作成された PEM ファイルを、前節の Google アカウント情報「Upload new X.509 cert:」にアップロード。keystore は GAE の war に入れて、アップロード。しなきゃ認証時に java.io.FileNotFoundException が出ちゃう。

GAE 側の設定

Google Account Authentication API からセキュアトークンを受け取ったり、実際にデータをやりとりしたりする GAE のページを SSL に対応させる。

appengine-web.xml の appengine-web-app 要素内に
<ssl-enabled>true</ssl-enabled>
を追加。

お好みで web.xml に
<security-constraint>
 <web-resource-collection>
  <url-pattern>/authsub</url-pattern>
 </web-resource-collection>
 <user-data-constraint>
  <transport-guarantee>CONFIDENTIAL</transport-guarantee>
 </user-data-constraint>
</security-constraint>
をつけ加え(/authsub アクセス時は常に https になる)。

プログラム

Google Accounts Authentication API へのアクセスを要求する場合、
String next = "https://YOURAPP.appspot.com/authsub";
String scope = "http://www.blogger.com/feeds/"; //blogger にアクセスする場合
boolean secure = true;
boolean session = true;
String authSubLogin = AuthSubUtil.getRequestUrl(next, scope, secure, session);
とすると、authSubLogin にログイン用の URL が入るので、それでページにリンクを作成してアクセスしてもらう。

セキュアトークンを受け取って、実際にデータにアクセスするには、
String query = req.getQueryString();
String token = AuthSubUtil.getTokenFromReply(query);
PrivateKey privateKey = AuthSubUtil.getPrivateKeyFromKeystore("example", "changeme", "Example", "changeme");
String sessionToken = AuthSubUtil.exchangeForSessionToken(token, privateKey);

GoogleService myService = new GoogleService("blogger", "APPLICATIONNAME");//blogger にアクセスする場合
myService.setAuthSubToken(sessionToken, privateKey);
という風にするといいみたい(例外処理は省略)。後はもう好きなように
URL feedUrl = new URL("http://www.blogger.com/feeds/default/blogs");
Feed resultFeed = myService.getFeed(feedUrl, Feed.class);
とかすると、ユーザーが管理している Blogger ブログ一覧のフィードが取得できたりするわけ。

あ、セキュアトークン受け取りの URL を https に変えると、Google アカウント情報に指定する「Target URL path prefix」も方も https に変えとかないとね。

これで警告がすべてなくなるはずなんだけれど…、少し前にやったのを思い出しながら書いたので、思い違いや書き忘れがあるかも。後はより詳しい人のツッコミに期待。

2010-06-05

GAE でログのダウンロード

メールで流れてきた を読んで、ログがダウンロードできると今さらながらに知った次第。 にちゃんと書いてあるし…。が読んで見ても読解力のなさか、やはりうまくいかなかったので、分かりにくかった点を補足。

以下、Windows での話。

まず、appcfg.cmd はどこ???という状態。うちの場合ここにあった。
C:\eclipse\plugins\com.google.appengine.eclipse.sdkbundle.VERSION\appengine-java-sdk-VERSION\bin\appcfg.cmd

続いて
./appengine-java-sdk/bin/appcfg.sh request_logs myapp/war mylogs.txt
という風に使う…とのことだけれど、「myapp/war」って何を指定するの???と軽くパニック。結局何のことはない、ローカルの GAE プロジェクトの war フォルダへのパスを入れればよかったみたい。

appcfg.cmd のディレクトリを path に追加して、
appcfg request_logs C:\eclipse\workspace\MYPROJECT\war mylogs.txt
みたくしたらカレントディレクトリにちゃんと mylogs.txt が。よかったよかった。

あとは好みで --severity=0 とか、--append とかのオプションをつければいいわけね。フムフム。

2010-05-22

DeadlineExceededException と HardDeadlineExceededError

サーブレットの処理が 30 秒で終わらないときに出る
com.google.apphosting.api.DeadlineExceededException
というのは知ってたんだけれど、最近ログに現れた
com.google.apphosting.runtime.HardDeadlineExceededError
というのは一体何?…と思い調べてみた。

すると ajnk1 で話を聞いた bufferings さんのページを発見。 ほほう、30 秒ルールにひっかかって発生した DeadlineExceededException も、catch でつかまえたり、finally でロールバックしたりできるんだ。その処理に 300~400ms 近くかかってしまうと、次は HardDeadlineExceededError が出て有無を言わさず強制終了になってしまう…という理解でいいのかな。

300ms でできる処理って…。まあ何に使うのかわからないけど、一応覚えとこっと。 には、TaskQueue で呼ばれた Task で HardDeadlineExceededError が出た場合には、リトライがされないというような記述も。

としたら、DeadlineExceededException を catch とか、finally でロールバックとか、下手にしない方がいいのかも。

2010-05-09

GAE/J で Amazon Product Advertising API

今日は Google App Engine for Java で Amazon Product Advertising API を使うためのヒントみたいなもの。「Amazon Product Advertising API」は、2009 年 5 月まで「Amazon アソシエイト Web サービス」と呼ばれていたので、そちらの名前で覚えている人も多いかと。このサービスを使うとアプリケーションから簡単に Amazon の商品を検索することが可能に。この API 詳細は で理解してもらうとして、問題はその Signature ―― サービスの名称変更とともに API のリクエスト URL につけることが義務づけられた電子署名がちょっと大変かも。 リクエスト自体は、これまでのものにタイムスタンプを追加して、パラメータの値を RFC3986 のパーセントエンコード(URL エンコードとわずかに違う)、バイト順にパラメータを並べ替え。そのリクエスト全体の文字列をユーザーの秘密キーで HMAC(SHA256 アルゴリズム)を作成し、BASE64 に変換、パーセントエンコード後 Signature パラメータとしてリクエスト末尾につければ、ようやく API へのリクエストの URL の完成という流れ。ああ、ややこしい。

以前からこのサービスを使って、クリボウの Blogger 入門サイドバーとかで実際にキーワードを元に広告を表示しているのだけれど、これは何年か前に Perl で書いたのを を参考に、2009 年に書き直したもの。じゃあ GAE/J では、認証をどうするんだろう?と思ったので調べてみた。前置きが長かったけれど、結論は簡単。Amazon が公開している Java のサンプルコードを利用するだけ。 …だけ、とか書きながらこのままだと動かないのでちょっと修正。


サンプルコードの修正

import org.apache.commons.codec.binary.Base64;
まず、この BASE64 変換を行う org.apache.commons.codec.binary.Base64 のライブラリがないと思うので、以下からゲット。 Binaries 形式のものをダウンロード、解凍してできた commons-codec-*.*.jar をプロジェクトの WEB-INF/lib に入れてビルドパスに加える。
private String endpoint = "ecs.amazonaws.com"; // must be lowercase
private String awsAccessKeyId = "YOUR AWS ACCESS KEY";
private String awsSecretKey = "YOUR AWS SECRET KEY";
次に 33、34、35 の各行。34、35 行は、自身の Amazon Web Service アクセスキーと、 Amazon Web Service 秘密キーを入力。当たり前か。33 行は、そのままだとアメリカの amazon.com の結果を表示してしまうので、
private String endpoint = "ecs.amazonaws.jp"; // must be lowercase
と書き換えておく。

3つ目は、40 行からのコンストラクタで、UnsupportedEncodingException、NoSuchAlgorithmException、InvalidKeyException が出るぞと警告されてしまうので、throws するなり catch するなり。

最後は、なんでかわからないんだけれど、Signature に無駄な %0D%0A という文字列(パーセントエンコード前は「CRLF」改行)が入ってしまうのを阻止する。いや原因がわからず、阻止の仕方もわからないので、
signature = new String(encoder.encode(rawHmac));
らへんのコードを
signature = (new String(encoder.encode(rawHmac))).replaceFirst("[\\r\\n]*$", "");
と、パーセントエンコード寸前に最後の改行文字を削除させる。なんて対症療法!と思うけれど、まあこれで動くので。だれか詳しい人は教えて下さい。


使い方

HashMap<String, String> map = new HashMap<String, String>();
map.put("Service", "AWSECommerceService");
map.put("Version", "2009-01-06");
map.put("Operation", "ItemSearch");
map.put("SearchIndex", "Books");
map.put("ResponseGroup", "Small,Images");
map.put("Keywords", "Google App Engine");
map.put("AssociateTag", "kuribosblogge-22");
map.put("ItemPage", "1");

SignedRequestsHelper srh = new SignedRequestsHelper();
String url = srh.sign(map);
とかいう風に使うと、url にちゃんと AWSAccessKeyId や Timestamp が補われ、パーセントエンコード、ソートもされて、Signature の付加された URL が入るはず。やってみると…。
http://ecs.amazonaws.jp/onca/xml?AWSAccessKeyId=0E87C7QWYAM53DHJ2682&AssociateTag=kuribosblogge-22&ItemPage=1&Keywords=Google%20App%20Engine&Operation=ItemSearch&ResponseGroup=Small%2CImages&SearchIndex=Books&Service=AWSECommerceService&Timestamp=2010-05-09T08%3A30%3A56Z&Version=2009-01-06&Signature=VHsoRNX4slmxgG%2BS7xLKqcSf3Ogf9r7Qa1I8TILySMk%3D
ほらほら。これに URLFetch でアクセスすると、「Google App Engine」にヒットする書籍のデータ(画像も)が XML 形式で返ってくるので、解析してゴニョゴニョと上手く表示してやれば OK。ちなみに Signature の計算が間違っていると、
<?xml version="1.0"?>
<ItemSearchErrorResponse xmlns="http://ecs.amazonaws.com/doc/2009-01-06/"><Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message></Error><RequestID>17711f2a-00c6-415f-9995-6f3f96d0ae57</RequestID></ItemSearchErrorResponse>
みたいに返って来て、データは全く取れないので、チェックしてみるといいと思う。

This request caused a new process to be started for your application.

This request caused a new process to be started for your application, and thus caused your application code to be loaded for the first time. This request may thus take longer and use more CPU than a typical request for your application.
と Logs に出てくるのが気になっていたんだけれど…。

#appengine でスピンアップのリクエストの時ってWarningのログを出してくれている? "This request caused a new process to be started for your application"って出てるless than a minute ago via sobees


スピンアップした(アプリケーションが起動した)のがわかるようになってたのか。こりゃ便利。CPU 時間がいつもよりかかったリクエストの理由を知ったり、スピンアップ、スピンダウンの間隔を知ったりするのにいいね。

2010-05-01

Google App Engine Datastore Performance Test

Google App Engine Datastore Performance Test なるアプリを発見。

Google App Engine Datastore Performance Test

Python、Java の JDO、Java の Low-level API と、アクセスの手段別のデータストア処理時間が計測可能。

エンティティの追加に関しては Java の Low-level API を使った処理の方が Python 版より速いようだが、更新となると Python 版の方に軍配が上がるみたい。

…というか、JDO が遅すぎだということを再確認。

GAE に新しい Quota が加わるそうな

Tasks Strage Quota

Google App Engine のダッシュボードから Task Queues をクリックすると、見慣れない Quota 表示が。

Tasks Daily Quota として Task Queue API Calls があるのは前からのことなので気にならないのだけど、Tasks Storage Quota として、Task Queue Stored Task Count と Task Queue Stored Task Bytes という項目が新たに加わった様子。

画像は 4 月 29 日のもの。まだ適用されない Quota だとはいえ、171 % とか、1014 % とかいう数字は、刺激が強い…。同日行われた GAE のメンテナンスのせい…だと信じたい。

Zenback - Everyone's Related Posts