appengine ja night #5に いってきました

Kayの人

@tmatsuo

正規化しすぎるとだめ

class Tag(db.Model)
  name = db.StringProperty()
  count = db.IntegerProperty()

class Post(db.Model)
  tags = db.ListProperty(db.Key)
  body = db.TextProperty()

JOINが使えないので検索はこんな感じ...

t =Tag.get_by_key_name(tag_input)
if t is None:
  raise Http404
posts = Post.all().filter("tags = ",t.key()).fetch(200)

上記の場合はPostモデルにStringListを持った方が、無難

  • 質問
    • Q:タグ名を変更する場合は全Postをなめて変更するのか?、
    • A:TaskQueueかRemoteAPIで全部を変更するか,Modelにバージョンを持つ
      • 最近1.3.1でクエリが早くなった->複数クエリを発行することに躊躇する必要はなくなった?
      • GETとQueryは変わらない
      • KeyでGETは18msec程度
      • KeyでQueryは23msec程度(ヒットが1つの場合)
      • 場合場合によってモデル設計を考える

不要なIndexを付けないように

db.StringPropertyはデフォルトでIndexが付いてしまう

class Foo(db.Model)
  prop1 = db.StringProperty()
  prop2 = db.StringProperty()
  prop3 = db.StringProperty()
  prop4 = db.StringProperty()

インデックスを付けない

class Foo(db.Model)
  prop1 = db.StringProperty(indexed=false)
  prop2 = db.StringProperty(indexed=false)
  prop3 = db.StringProperty(indexed=false)
  prop4 = db.StringProperty(indexed=false)

インデックス名を短くする

class Foo(db.Model)
  prop1 = db.StringProperty(name="p1")
  prop2 = db.StringProperty(name="p2")
  prop3 = db.StringProperty(name="p3")
  prop4 = db.StringProperty(name="p4")

GQLを書いた場合kindが必要だが
kind名を短くする

@classmethod
def kind(cls)
  return "z"

参考
http://code.google.com/intl/ja/appengine/docs/python/datastore/gqlreference.html

  • 無料で保存できるデータ容量は限られているのでこれらをできるだけ短い名前にする
    • app engineの管理ツールでDBのうちどれくらいがメタデータかを見ることができる

ログイン - Google アカウント

  • モデルにIndexが何個あるかによってもパフォーマンスが変わる 1個Indexが追加される毎に16msec(cpu time)
  • 検索に使わないのであればIndexを無駄に張らない

db.StringProperty(indexed=false)
から
db.StringProperty(indexed=true)
に変更したばあいfalse時代のものは検索に反映されない!!
http://code.google.com/intl/ja/appengine/docs/python/datastore/queriesandindexes.html

Exploding Index

q = MyModel.all().filter("contents ="."word1").¥
                  filter("contents ="."word2").¥
                  filter("contents ="."word3").¥
                  order("-created")
entries = q.fetch(10)

このクエリにたいしてcontents,contents,contents,createdというインデックスを作ろうとする
content数xcontent数xcontent数xcreated数 の膨大なIndexを作ってしまう(Max 5,000件)

q = MyModel.all().filter("contents ="."word1").¥
                  filter("contents ="."word2").¥
                  filter("contents ="."word3").¥
                  finter("created_month=".this_month)

entries = q.fetch(1000)
entries.sort(cmp=lmbda x,y: cmp(x.created,y.created)

on memoryでsortするように心がける
Composite Indexを作ると危険!!

  • イデア
    • Keyに時刻を埋め込むとorderが不要になる
      • marge joinの仕様でsortされることがおそらく保証されている
  • 質問
    • Q.1000件を越えるInmemory sortは?
    • A.ヒープ領域(100M程度)が許す限りin memoryで可能

Greedy module loading

  • 一部でしか使用しないモジュールは遅延ロードするべき
    • スピンアップはなるべく短く
# -*- coding: utf-8 -*-

from kay.utils import render_to_response

import hoge_utils
import fuga_utils
import moge_utils

def index(request)
  return render_to_response('myapp/index.html',{'message':'hello'})

def hoge(request)
  hoge_utils.get_entries()
  return render_to_response('myapp/index.html',{'message':'hello'})

def fuga(request)
  fuga_utils.get_entries()
  return render_to_response('myapp/index.html',{'message':'hello'})

def moge(request)
  moge_utils.get_entries()
  return render_to_response('myapp/index.html',{'message':'hello'})

先頭でimportしている
apache+mod_pythonの場合はこの方が効率が良い
GAEでは以下の用に 遅延ロードしたほうがよい
許されるスピンアップは10秒程度

# -*- coding: utf-8 -*-

from kay.utils import render_to_response


def index(request)
  return render_to_response('myapp/index.html',{'message':'hello'})

def hoge(request)
  import hoge_utils
  hoge_utils.get_entries()
  return render_to_response('myapp/index.html',{'message':'hello'})

def fuga(request)
  import fuga_utils
  fuga_utils.get_entries()
  return render_to_response('myapp/index.html',{'message':'hello'})

def moge(request)
  import moge_utils
  moge_utils.get_entries()
  return render_to_response('myapp/index.html',{'message':'hello'})

参考

1.3.1の新機能紹介

Transactional Task Queue

http://pixiv.cc/mix3/archives/51343747.html
DBのTransacion内にTaskQueueを積む動作を入れておく
commitが成功した場合にのみ積まれる
不整合が起きにくい

  • 質問
    • Q.TaskQueueは一時間後とか指定できる
    • A.できる etaかcount downで指定可能 正確とは限らないけど。
    • Q.TaskQueueのQueueのサイズに上限がある?
    • A.なし、ただしスピードは同時に秒間20タスクが上限
Cursor

Pagingに使えそう
任意ページに飛んだり、戻るにはもう一捻り

Appstats(Python版のみ)

http://blog.shehas.net/2009/12/11/Kay-appstats

  • 以下のYSlow的なものが見える
    • RPCの統計
    • URLアクセス統計
    • Requestの履歴

app.yaml

  • url: /sats_*

ひがやすをさん

複数のエンティティグループに跨ったTransaction(グローバルトランザクション)

エンティティグループはできるだけ小さく作る

2phase commit

複数のDBに跨るようなTransactionのプロトコル
分散トランザクションに挑戦しよう!

  1. ジャーナルに書きだし
  2. 内部コミット
  3. ジャーナルの適応

appengine ja night #4 Transaction Puzzlers