2011-05-11
GAEのDatastoreのエントリ削除していたらOver Quotaになった…
いつの間にかこれのDatastoreのエントリのサイズが380MB程になってquota limitに陥り(それにしても、Datastore Statisticsで見る限り、全てのデータを合わせても400MBしかないのに、なぜquota limitになるのだろう?Free Quotaでも1GBのはず…)、こちらがうまく動かなくなっていたみたいなので、思い切って前者のデータをDatastore上に持つのを止めにして、全部削除しようとしていたところ……
CPU Timeが6.50CPU hoursを使い切ってOver Quotaになってしまいました(哀)。
どうしてこうなった
削除処理としては、
entries=dbClassName.all().fetch(500)
db.delete(entries)
のような処理をtaskqueueで順次呼び出すというシンプルなもの。
ところが、ログで確認する限り、500件削除するだけで、200000cpu_ms=200cpu_sec前後かかっており…ということは、Free Quotaの範囲の1日辺りのCPU Time=6.50 CPU hoursだと、単純計算で、
6.50 CPU hours→23400cpu_sec
500件×23400cpu_sec/200cpu_sec=58500件
のように、6万弱のエントリ削除でOver Quotaに陥ってしまうことになります。
うーん、あと倍近くあるんだけれど…cronで30分間隔くらいで呼び出すようにするか…?
他の方法は?
面倒なのでこれまでちゃんと調べていませんでしたが、remote_apiとかDatastore Adminの機能でもエントリを削除することは出来るみたいですね。
- Accessing the datastore remotely with remote_api - Google App Engine — Google Developers
- Backup/Restore, Copy, and Delete Data - Google App Engine — Google Developers
ただ、どちらもQuotaにカウントされるみたいなので、通常のアプリでやるのとどれくらい違うかというと疑問がありますが…実際のところどんなものなのでしょう?
追記→GAEでremote_apiを使う場合の覚書 - 風柳メモ。ちなみにQuotaの消費はアプリでやるのとほとんど変わらなかった。
Requests to remote_api use quota
Since remote_api operates over HTTP, every datastore call you make incurs quota usage for HTTP requests, bytes in and out, as well as the usual datastore quota you would expect. Bear this in mind if you're using remote_api to do bulk updates.
Accessing the datastore remotely with remote_api
Warning! Deleting entities in bulk happens within your application, and thus counts against your quota.
Deleting Entities in Bulk
DatastoreのQuotaだけ換算されて、CPU Timeを使わないというならうれしいですが、そう甘くは無さそうな気がするしなぁ…。
特に後者の場合、entity kindを指定して一括削除出来るのだけれど、操作中にquota limitになったらどうなるんだろう…?
関連
GAEのmemcacheでlist等を読み書きすると予想外にコストがかかる
Google App Engine/Pythonのmemcacheはtupleやdictやobject、それらのlistなんかも読み書き出来るので手軽なのだが、単純なstringなんかを読み書きする場合と比較すると、思ったよりもコストがかかるようなので、覚書。
テスト
from google.appengine.api import memcache from google.appengine.api import quota from django.utils import simplejson from array import array test_str = 'A'*100000 test_list = array('c',test_str).tolist()
のようにして用意したstringとlistをmemcacheで書き込み/読み出しして、quota.get_request_cpu_usage()で要したCPU時間(megacycles)を比較。
1. string
# 書き込み memcache.set(key='test_str',value=test_str,time=60) # 読み出し test_str = memcache.get(key='test_str')
2. list
# 書き込み memcache.set(key='test_list',value=test_list,time=60) # 読み出し test_list = memcache.get(key='test_list')
3. listを','で結合(string化)/分割(復元)
# 書き込み join_list=','.join(test_list) memcache.set(key='join_list',value=join_list,time=60) # 読み出し join_list = memcache.get(key='join_list') test_list = join_list.split(',')
4. listをsimplejsonでstring化/復元
# 書き込み json_list=simplejson.dumps(test_list,separators=(',',':')) memcache.set(key='json_list',value=json_list,time=60) # 読み出し json_list = memcache.get(key='json_list') test_list = simplejson.loads(json_list)
結果
| 1. string | 2. list | 3. join()/split() | 4. simplejson | |
|---|---|---|---|---|
| 書き込み | 2 | 714 | 7 | 984 |
| 読み出し | 2 | 392 | 12 | 4044 |
| ※書き込み(set)/読み出し(get)の値はmegacycles | ||||
- 文字列(string)の読み書きに比べてlistの読み書きは結構遅くなってしまう。
- 単純な構造であればフォーマットを決めて(上記例では','区切り)一度文字列化(同join())してから書き込み、読み出し後に復元(同split())した方がよい。
- そうかといって、simplejsonを用いて文字列化/復元すると、そっちのオーバヘッドの方が大きくなってしまう。
GAEのログ(Logs)のCPU Time関連の値等の見方
Google App Engineのログ(Logs)画面、特に所要時間関連はいつも適当に流していたので、きちんとした意味を把握しようと Google-App-Engine-Japan | Google グループ で質問してみた。
該当ディスカッション: ログ(Logs)のCPU Time関連の値の見方等について - Google-App-Engine-Japan | Google グループ
ログは例えば
2011-05-07 20:42:53.656 (アクセスPATH) 200 4998ms 37518cpu_ms 28255api_cpu_ms (サイズ) (ユーザエージェント情報),gzip(gfe)
(端末のIPアドレス) - - [07/May/2011:20:42:53 -0700] "GET (アクセスPATH) HTTP/1.1" 200 (サイズ) - "(Referrer)" "(ユーザエージェント情報),gzip(gfe)" "(Host)" ms=4999 cpu_ms=37518 api_cpu_ms=28255 cpm_usd=1.042369
のような表示になるが、各値は次のように読み取る。
*ms、ms=*
当該リクエストに対する所要時間(実時間・単位:ミリ秒)。
*cpu_ms、cpu_ms=*
当該リクエストに費やしたCPU Time(単位:ミリ秒)。
*api_cpu_ms、api_cpu_ms=*
当該リクエストに費やしたCPU Time中、(datastore等の)API処理に費やしたもの(単位:ミリ秒)。
cpm_usd=*
Cost Per Mille, United States Dollarsのことで、1000回辺りのコスト(単位:米ドル)を意味する。
cpm_usdの値/1000が当該リクエスト(1回辺り)の課金額の目安となる。
なお、CPU Timeは(仮に)当該リクエストを1.2 GHz(1200MHz)のIntel x86プロセッサで処理したものとして換算した所要時間(当然、高速なプロセッサを用いたり、複数のプロセッサで並列処理した場合には実際の所要時間はこれよりも短くて済む)。
GAEで使用するDjangoのバージョンを指定しているつもりなのに警告が出る
【追記】
下記件はひとまず解決済→GAEで使用するDjangoのバージョンを指定しているつもりなのに警告が出る(続き) - 風柳メモ。
いつからか、ログ上に
You are using the default Django version (0.96). The default Django version will change in an App Engine release in the near future. Please call use_library() to explicitly select a Django version. For more information see http://code.google.com/appengine/docs/python/tools/libraries.html#Django
という警告が出るようになっていたので、いくつかのアプリで
from google.appengine.dist import use_library use_library('django', '0.96') from google.appengine.ext.webapp import template
と明示してみたのだが、相変らず警告が出てしまう。どうしたらいいんだろう、指定方法がおかしい?
本当は
use_library('django', '1.2')
として1.2を使うべきなんだろうけど、0.96から随分変わっているようなので、どのような問題が出るのか調べるのが面倒。どうしようかなぁ…。
関連
GAEのデプロイ後の反映に時間がかかっている?
の影響なのかどうかわからないのだけれど、2011/05/11にGoogle App Engine/Pythonのアプリをいじっていたところ、デプロイが完了してから、実際に当該バージョンが反映されるまで、時間がかかっている気がする。
- デプロイ完了直後にアクセスすると、古いバージョンの動作のままのことがある。
- 一度新しいバージョンが動作した後、再度アクセスすると、古いバージョンの動作に戻っているケースが有る。
これまでは、デプロイ完了と同時に、新しいバージョンに完全に切り替わっていたような覚えがあるのだが。
反映が遅れるのがデフォルトであれば、実際に完全に反映されるのに充分な待ち時間はどのくらいか、目安を知りたいところ。

