SlideShare a Scribd company logo
1 of 54
Download to read offline
Transaction Puzzlers

appengine ja night #4
あらかわ (@ashigeru)
今日の内容
  トランザクション処理の考え方
  トランザクション処理のパターン




2010/01/22   appengine ja night #4 - @ashigeru   4
トランザクション処理の考え方
  リソースを一時的に独占できる技術
     同時に変更して不整合が起こる、などを回避


  今回は悲観的/楽観的をあまり気にしない
     App Engineは楽観的並⾏性制御
     いずれも一時的にリソースを独占できる
     設計/実装時には考慮する必要がある



2010/01/22   appengine ja night #4 - @ashigeru   5
App Engineのトランザクション
  トランザクションはEntity Group (EG)単位
     同一EG内のエンティティに対する操作はACID
     複数EGにまたがる操作は対応していない




2010/01/22   appengine ja night #4 - @ashigeru   6
Entity Groupの構成
  同じルートキーを持つエンティティ群
     データストア上で近くに配置される
  例
     Foo(A)                                EG: Foo(A)
     Foo(A)/Hoge(B)
     Foo(B)                                EG: Foo(B)
     Bar(A)/Foo(A)
                                           EG: Bar(A)
     Bar(A)/Foo(B)/Hoge(D)

2010/01/22      appengine ja night #4 - @ashigeru       7
Entity Groupの特徴
  ポイント
     トランザクションの範囲はエンティティ作成
     時に決まり、変更できない
     EGを⼤きくするとトランザクションで独占す
     るエンティティが多くなる
  EGの設計が非常に重要に
     間違えると並列性が極端に低下する
     うまくやればスケールアウトする


2010/01/22   appengine ja night #4 - @ashigeru   8
ここまでのまとめ (1)
  App EngineのトランザクションはEG単位
     EG内ではACIDトランザクション
     EGをまたぐトランザクションは未サポート
  EGの設計によっては並列性が落ちる
     EGを⼤きくすると独占範囲が広がる
     EGを分断すると整合性を保つのが困難




2010/01/22   appengine ja night #4 - @ashigeru   9
トランザクション処理のパターン
  App Engineのトランザクションはやや特殊
     パターンで対応したほうがよさそう
  本日紹介するもの
     Read-modify-write
     トランザクションの合成
     ユニーク制約
     冪(べき)等な処理
     Exactly Once
     BASE Transaction


2010/01/22    appengine ja night #4 - @ashigeru   10
注意点
  プログラムの説明に擬似コードを多⽤
     言語はJavascriptライク
     APIはJavaのLow-Level APIライク
  ⾒慣れない言語要素
     キーリテラル – KEY:…
        KEY:Foo(A), KEY:Foo(A)/Bar(B), など
     データストア
        get(tx, key), put(tx, entity), beginTransaction()
     タスクキュー
        enqueue([tx,] statement)


2010/01/22           appengine ja night #4 - @ashigeru      11
パターン: read-modify-write
  エンティティのプロパティを変更する
  例:
     カウンタの増加
     ショッピングカートに商品を追加


  現在の値をもとに次の値が決まる
     読む、変更、書き戻す、の3ステップが必要
     途中で割り込まれると不整合が起こる

2010/01/22   appengine ja night #4 - @ashigeru   12
read-modify-write (1)
  考え方
     読んでから書き戻すまでエンティティを独占




                        100 + 1

             100                            101



2010/01/22         appengine ja night #4 - @ashigeru   13
read-modify-write (2)

var tx = beginTransaction()
try {
  var counter = get(tx, KEY:Counter(C))
  counter.value++
  put(tx, counter)
  tx.commit()
}
finally {
  if (tx.isActive()) tx.rollback()
}
2010/01/22   appengine ja night #4 - @ashigeru   14
read-modify-write (3)

var tx = beginTransaction()
try {
  var counter = get(tx, KEY:Counter(C))
  counter.value++
  put(tx, counter)
  tx.commit()       読んでから書き戻す
}                   までをACIDに⾏う
finally {
  if (tx.isActive()) tx.rollback()
}
2010/01/22   appengine ja night #4 - @ashigeru   15
DSL: atomic (tx) { … }
   以後は下記のように省略
      トランザクションの開始と終了を簡略化

atomic(tx) {
  var counter = get(tx, KEY:Counter(C))
  counter.value++
  put(tx, counter)
}



 2010/01/22   appengine ja night #4 - @ashigeru   16
パターン: トランザクションの合成
  同じEGに対する複数のトランザクション
  処理を合成
  例:
     2つのカウンタを同時に変更 (恣意的)
     非正規化した2つの情報を同時に更新
  注意点
     分断したトランザクションでは、途中で失敗
     した際に修復が⼤変

2010/01/22   appengine ja night #4 - @ashigeru   17
トランザクションの合成 (1)
  考え方
     同じEGのトランザクションが2つあったら、
     一度に処理してしまう



                              15                  16

             30         31


2010/01/22        appengine ja night #4 - @ashigeru    18
トランザクションの合成 (2)

atomic(tx) {
  var a = get(tx, KEY:Eg(C)/Counter(A))
  a.value++
  put(tx, a)
  var b = get(tx, KEY:Eg(C)/Counter(B))
  b.value++
  put(tx, b)
}


2010/01/22   appengine ja night #4 - @ashigeru   19
トランザクションの合成 (3)

atomic(tx) {
  var a = get(tx, KEY:Eg(C)/Counter(A))
  a.value++
  put(tx, a)
  var b = get(tx, KEY:Eg(C)/Counter(B))
  b.value++
  put(tx, b)
}                  同じEGのエンティティ
                   に対する操作

2010/01/22   appengine ja night #4 - @ashigeru   20
トランザクションの合成 (4)

atomic(tx) {
  var a = get(tx, KEY:Eg(C)/Counter(A))
  a.value++
  put(tx, a)
  var b = get(tx, KEY:Eg(C)/Counter(B))
  b.value++
  put(tx, b)
}                 複数のトランザクション
                  を合成, 全体がACIDに

2010/01/22   appengine ja night #4 - @ashigeru   21
パターン: ユニーク制約
  重複するエンティティの登録を防止する
  例:
     同じIDを持つユーザの登録を防ぐ
     ダブルブッキングを防ぐ
  注意点
     データストアは制約機能を組み込んでいない
     クエリはトランザクションに参加できない



2010/01/22   appengine ja night #4 - @ashigeru   22
ユニーク制約 (1)
  考え方
     エンティティの入れ物ごと独占
     入れ物が空なら追加するエンティティは一意




         @hoge   @hoge              @hoge



2010/01/22       appengine ja night #4 - @ashigeru   23
ユニーク制約 (2)

var key = KEY:User(hoge@example.com)
atomic(tx) {
  var user = get(tx, key)
  if (user != null) {
    throw new NotUniqueException()
  }
  user = new User(key, ...)
  put(tx, user)
}

2010/01/22   appengine ja night #4 - @ashigeru   24
ユニーク制約 (3)

var key = KEY:User(hoge@example.com)
atomic(tx) {
  var user = get(tx, key)
                 ユニーク制約をキーで
  if (user != null) {
                 表す(メールアドレス)
    throw new NotUniqueException()
  }
  user = new User(key, ...)
  put(tx, user)
}

2010/01/22   appengine ja night #4 - @ashigeru   25
ユニーク制約 (4)

var key = KEY:User(hoge@example.com)
atomic(tx) {
  var user = get(tx, key)
  if (user != null) {
    throw new NotUniqueException()
  }
  user = new User(key, ...)
  put(tx, user)    そのエンティティが
}                  すでにあれば制約違反

2010/01/22   appengine ja night #4 - @ashigeru   26
ユニーク制約 (5)

var key = KEY:User(hoge@example.com)
atomic(tx) {
  var user = get(tx, key)
  if (user != null) { 存在しなければ
                      ユニークなので追加
    throw new NotUniqueException()
  }
  user = new User(key, ...)
  put(tx, user)
}

2010/01/22   appengine ja night #4 - @ashigeru   27
ユニーク制約 (6)

var key = KEY:User(hoge@example.com)
atomic(tx) {
  var user = get(tx, key)
  if (user != null) {
    throw new NotUniqueException()
  }
  user = new User(key, ...)
  put(tx, user)
}                     getからputまでを独占


2010/01/22   appengine ja night #4 - @ashigeru   28
ここまでのまとめ (2)
  read-modify-write
     最初に読んでから書き戻すまで独占
  トランザクションの合成
     同一EGに対する操作をまとめる
  ユニーク制約
     入れ物を独占してからエンティティを作成
     すでにあったらユニークじゃないので失敗



2010/01/22   appengine ja night #4 - @ashigeru   29
パターン: 冪(べき)等な処理
  1回分しか効果を出さない処理
     2回以上成功しても、1回分しか反映しない
  例:
     フォームの多重送信を防止
     お一人様一点限り。
  注意点
     英語でidempotentだけど覚えにくい



2010/01/22   appengine ja night #4 - @ashigeru   30
冪等な処理 (1)
  考え方
     「処理がユニークに成功する」ということ
     まだ成功していなかったら成功させる
     一度成功していたら何もしない


     成功      成功                           成功
                     結果


2010/01/22    appengine ja night #4 - @ashigeru   31
冪等な処理 (2)
var key = KEY:Counter(C)/Flag(unique)
atomic(tx) {
  var flag = get(tx, key)
  if (flag != null) {
    return
  }
  put(tx, new Flag(key))
  var counter = get(tx, KEY:Counter(C))
  counter.value++
  put(tx, counter)
}
2010/01/22   appengine ja night #4 - @ashigeru   32
冪等な処理 (3)
var key = KEY:Counter(C)/Flag(unique)
atomic(tx) {
  var flag = get(tx, key)
      「ユニークなキー」を表す
  if (flag != null) {
      → db.allocate_ids()
    return
  }   → DatastoreService.allocateIds()
  put(tx, new Flag(key))
  var counter = get(tx, KEY:Counter(C))
  counter.value++
  put(tx, counter)
}
2010/01/22   appengine ja night #4 - @ashigeru   33
冪等な処理 (4)
var key = KEY:Counter(C)/Flag(unique)
atomic(tx) {
  var flag = get(tx, key)
  if (flag != null) {
    return
  }
  put(tx, new Flag(key))
  var counter = get(tx, KEY:Counter(C))
             ユニーク制約をユニークなキーで。
  counter.value++
  put(tx, counter)
             1回目は確実に成功、
}            キーを使いまわせば2回目は失敗
2010/01/22   appengine ja night #4 - @ashigeru   34
冪等な処理 (5)
var key = KEY:Counter(C)/Flag(unique)
atomic(tx) {
  var flag = get(tx, key)
  if (flag != null) {
                   それ以降の処理は
    return
                   一度だけしか⾏われない
  }
  put(tx, new Flag(key))
  var counter = get(tx, KEY:Counter(C))
  counter.value++
  put(tx, counter)
}
2010/01/22   appengine ja night #4 - @ashigeru   35
冪等な処理 (6)
var key = KEY:Counter(C)/Flag(unique)
atomic(tx) {
  var flag = get(tx, key)
  if (flag != null) {
    return
  }
  put(tx, new Flag(key))
  var counter = get(tx, KEY:Counter(C))
  counter.value++
  put(tx, counter)    全体を合成してACIDに
}
2010/01/22   appengine ja night #4 - @ashigeru   36
冪等な処理 (まとめ)
  冪等な処理
     「1回分しか効果を出さない」パターン
  やりかた
     「成功」済みかどうかについてユニーク制約
     トランザクションを合成
        そのキーでユニーク制約を確認
        OKなら続きの処理を⾏う
  注意点
     ごみ(Flag)が残るが、これを消すのは一⼿間

2010/01/22   appengine ja night #4 - @ashigeru   37
パターン: Exactly Once
  確実にぴったり1回成功する処理
     冪等な処理では0回の場合もある (最⼤1回)
  例:
     カウンタの値を正確に更新する(恣意的)
  注意点
     「確実に失敗する」処理には適⽤できない




2010/01/22   appengine ja night #4 - @ashigeru   38
Exactly Once (1)
  考え方
     1度しか反映されない操作を執拗に繰り返す
     いつかは成功するはず
     間違えて2回以上成功しても効果は1回分




2010/01/22   appengine ja night #4 - @ashigeru   39
Exactly Once (2)
var key = KEY:Counter(C)/Flag(unique)
while (true) {
  try {
    atomic(tx) {
      var flag = get(tx, key)
      if (flag != null) {
        return
      }
      put(tx, new Flag(key))
      var counter = get(tx, KEY:Counter(C))
      counter.value++
      put(tx, counter)
    }
  } catch (ignore) {}
}

2010/01/22        appengine ja night #4 - @ashigeru   40
Exactly Once (3)
var key = KEY:Counter(C)/Flag(unique)
while (true) {
  try {
    atomic(tx) {
      var flag = get(tx, key)       冪等な処理の
      if (flag != null) {
        return
                                    パターン
      }
      put(tx, new Flag(key))
      var counter = get(tx, KEY:Counter(C))
      counter.value++
      put(tx, counter)
    }
  } catch (ignore) {}
}

2010/01/22      appengine ja night #4 - @ashigeru   41
Exactly Once (4)
var key = KEY:Counter(C)/Flag(unique)
while (true) {
  try {
    atomic(tx) {
                                    冪等な処理を
      var flag = get(tx, key)       無限に繰り返す
      if (flag != null) {
        return
      }
      put(tx, new Flag(key))
      var counter = get(tx, KEY:Counter(C))
      counter.value++
      put(tx, counter)
    }                         30秒ルールがあるので
  } catch (ignore) {}           確実とはいえない
}

2010/01/22     appengine ja night #4 - @ashigeru   42
Exactly Once (5)
var key = KEY:Counter(C)/Flag(unique)
enqueue(atomic(tx) {
   var flag = get(tx, key)
   if (flag != null) {
     return
   }
   put(tx, new Flag(key))
   var counter = get(tx, KEY:Counter(C))
   counter.value++
   put(tx, counter)
})
                         代わりにTask Queueで
                                 成功するまで繰り返し

2010/01/22    appengine ja night #4 - @ashigeru   43
Exactly Once (まとめ)
  Exactly Once
     「確実にぴったり1回成功する」パターン
     ただし、いつ成功するかは不明
  やりかた
     冪等な処理を無限に繰り返す
     一度成功したらあとは無駄なので打ち切る
  App EngineのTask Queueを使える
     成功するまで無限に繰り返す、という性質
     30秒ルールがあるからwhile(true)は無理


2010/01/22       appengine ja night #4 - @ashigeru   44
パターン: BASE Transaction
  複数のEGにまたがるゆるいトランザク
  ション
     ACIDほど強い制約がない
  例:
     口座間の送⾦処理
  注意点
     途中の状態が外側に⾒える
     ACIDよりアプリケーションが複雑

2010/01/22   appengine ja night #4 - @ashigeru   45
BASE Transaction (1)
  送⾦処理で本当にやりたいことは2つ
     Aの口座からX円引く
     Bの口座にX円足す
  「トランザクションの合成」は困難
     Aの口座とBの口座を同じEGに配置?
     Aから送⾦されうるすべての口座を同じEGに?
  トランザクションを分断すると危険
     失敗例:「Aの口座からX円引いたけどBに届かない」
     補償トランザクションすら失敗する可能性


2010/01/22   appengine ja night #4 - @ashigeru   46
BASE Transaction (2)
   単純に考えてみる
     まずAの口座から5000円引く
     そのあと「一度だけ」Bの口座に5000円足す
atomic (tx1) {
  var a = get(tx1, KEY:Account(A))       Exactly Once
  a.amount -= 5000
  put(tx1, a)
}                  atomic (tx2) {
                     var b = get(tx2, KEY:Account(B))
                     b.amount += 5000
                     put(tx2, b)
                   }
2010/01/22        appengine ja night #4 - @ashigeru   47
BASE Transaction (3)
var key = KEY:Account(B)/Flag(unique)
atomic (tx1) {
  var a = get(tx1, KEY:Account(A))
  a.amount -= 5000
  put(tx1, a)
  enqueue(tx1, atomic(tx2) {
    var flag = get(tx2, key)
    if (flag != null) {
      return
    }
    put(tx2, new Flag(key))
    var b = get(tx2, KEY:Account(B))
    b.amount += 5000
    put(tx2, b)
  })
}
2010/01/22        appengine ja night #4 - @ashigeru   48
BASE Transaction (4)
var key = KEY:Account(B)/Flag(unique)
atomic (tx1) {
  var a = get(tx1, KEY:Account(A))
  a.amount -= 5000
  put(tx1, a)
  enqueue(tx1, atomic(tx2) {
    var flag = get(tx2, key)      Read-modify-write
    if (flag != null) {               (A -= 5000)
      return
    }
    put(tx2, new Flag(key))
    var b = get(tx2, KEY:Account(B))
    b.amount += 5000
    put(tx2, b)
  })
}
2010/01/22        appengine ja night #4 - @ashigeru   49
BASE Transaction (5)
var key = KEY:Account(B)/Flag(unique)
atomic (tx1) {
  var a = get(tx1, KEY:Account(A))
  a.amount -= 5000
  put(tx1, a)
  enqueue(tx1, atomic(tx2) {
    var flag = get(tx2, key)      Read-modify-write
    if (flag != null) {               (B += 5000)
      return
    }
    put(tx2, new Flag(key))
    var b = get(tx2, KEY:Account(B))
    b.amount += 5000
    put(tx2, b)
  })
}
2010/01/22        appengine ja night #4 - @ashigeru   50
BASE Transaction (6)
var key = KEY:Account(B)/Flag(unique)
atomic (tx1) {
  var a = get(tx1, KEY:Account(A))
  a.amount -= 5000
  put(tx1, a)
  enqueue(tx1, atomic(tx2) {
    var flag = get(tx2, key)                          Exactly Once
    if (flag != null) {                                (B += 5000)
      return
    }
    put(tx2, new Flag(key))
    var b = get(tx2, KEY:Account(B))
    b.amount += 5000
    put(tx2, b)
  })
}
2010/01/22        appengine ja night #4 - @ashigeru                  51
BASE Transaction (7)
var key = KEY:Account(B)/Flag(unique)
atomic (tx1) {
  var a = get(tx1, KEY:Account(A))
  a.amount -= 5000
  put(tx1, a)
  enqueue(tx1, atomic(tx2) {
    var flag = get(tx2, key)          全体を合成
    if (flag != null) {        (A -= 5000, B += 5000)
      return
    }
    put(tx2, new Flag(key))
    var b = get(tx2, KEY:Account(B))
    b.amount += 5000
    put(tx2, b)
  })
}
2010/01/22        appengine ja night #4 - @ashigeru   52
BASE Transaction (まとめ)
  BASE Transaction
     EGをまたいだゆるいトランザクション
     いつか確実に完了する、という性質
  やりかた
     トランザクションを合成
        一つ目のEGに対して操作を⾏う
        Exactly Onceで二つ目のEGに対して操作を⾏う
  注意点
     操作が⾏われるまでタイムラグがある
        Eventual Consistency: いずれ整合性が取れる
     二つ目のEGに対する操作は制約をかけられない
        送⾦先に受け取り拒否されるとすごく困る


2010/01/22       appengine ja night #4 - @ashigeru   53
ここまでのまとめ (3)
  パターン: 冪等な処理
     操作自体を最⼤一回だけ(ユニーク)にする
     = ユニーク制約 + トランザクションの合成
  パターン: Exactly Once
     最⼤一回だけ成功する処理を無限に繰り返す
     = 冪等な処理 + Task Queue
  パターン: BASE Transaction
     自分を変更後、相⼿の変更を確実に一度だけ適⽤
     = read-modify-write + 合成 + Exactly Once


2010/01/22      appengine ja night #4 - @ashigeru   54
おわりに
  App Engineのトランザクションは「パズ
  ル」になりがち
     複雑な制約を考慮しつつ、時間内に解く
     ルールも定⽯もあるので、積み重ねが⼤切
  「仮説→検証」のサイクルが必要な段階
     みんなで情報共有
     パターンやアンチパターンを持ち寄ろう



2010/01/22   appengine ja night #4 - @ashigeru   55
参考文献
  Programming Google App Engine
     Oreilly & Associates Inc, 2009/11
  リレーショナルデータベース入門
     サイエンス社, 2003/03
  トランザクション処理
     日経BP社, 2001/10
  BASE: An Acid Alternative
     http://queue.acm.org/detail.cfm?id=1394128



2010/01/22         appengine ja night #4 - @ashigeru   56

More Related Content

What's hot

新しい並列for構文のご提案
新しい並列for構文のご提案新しい並列for構文のご提案
新しい並列for構文のご提案yohhoy
 
GPGPU deいろんな問題解いてみた
GPGPU deいろんな問題解いてみたGPGPU deいろんな問題解いてみた
GPGPU deいろんな問題解いてみたRyo Sakamoto
 
Popcntによるハミング距離計算
Popcntによるハミング距離計算Popcntによるハミング距離計算
Popcntによるハミング距離計算Norishige Fukushima
 
x86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNTx86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNTtakesako
 
ConcurrentHashMap Code Reading
ConcurrentHashMap Code ReadingConcurrentHashMap Code Reading
ConcurrentHashMap Code ReadingNaoyuki Kakuda
 
科学技術計算関連Pythonパッケージの概要
科学技術計算関連Pythonパッケージの概要科学技術計算関連Pythonパッケージの概要
科学技術計算関連Pythonパッケージの概要Toshihiro Kamishima
 
Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35Keisuke Fukuda
 
メタプログラミング C#
メタプログラミング C#メタプログラミング C#
メタプログラミング C#Fujio Kojima
 
Boost.Coroutine
Boost.CoroutineBoost.Coroutine
Boost.Coroutinemelpon
 
Cloud TPU Driver API ソースコード解析
Cloud TPU Driver API ソースコード解析Cloud TPU Driver API ソースコード解析
Cloud TPU Driver API ソースコード解析Mr. Vengineer
 
Async design with Unity3D
Async design with Unity3DAsync design with Unity3D
Async design with Unity3DKouji Hosoda
 
Metaprogramming in JuliaLang
Metaprogramming in JuliaLangMetaprogramming in JuliaLang
Metaprogramming in JuliaLangYuichi Motoyama
 

What's hot (20)

新しい並列for構文のご提案
新しい並列for構文のご提案新しい並列for構文のご提案
新しい並列for構文のご提案
 
機械学習
機械学習機械学習
機械学習
 
GPGPU deいろんな問題解いてみた
GPGPU deいろんな問題解いてみたGPGPU deいろんな問題解いてみた
GPGPU deいろんな問題解いてみた
 
プログラムを高速化する話
プログラムを高速化する話プログラムを高速化する話
プログラムを高速化する話
 
実用Brainf*ckプログラミング
実用Brainf*ckプログラミング実用Brainf*ckプログラミング
実用Brainf*ckプログラミング
 
R-hpc-1 TokyoR#11
R-hpc-1 TokyoR#11R-hpc-1 TokyoR#11
R-hpc-1 TokyoR#11
 
Popcntによるハミング距離計算
Popcntによるハミング距離計算Popcntによるハミング距離計算
Popcntによるハミング距離計算
 
x86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNTx86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNT
 
ConcurrentHashMap Code Reading
ConcurrentHashMap Code ReadingConcurrentHashMap Code Reading
ConcurrentHashMap Code Reading
 
科学技術計算関連Pythonパッケージの概要
科学技術計算関連Pythonパッケージの概要科学技術計算関連Pythonパッケージの概要
科学技術計算関連Pythonパッケージの概要
 
Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35
 
boost tour 1.48.0 all
boost tour 1.48.0 allboost tour 1.48.0 all
boost tour 1.48.0 all
 
メタプログラミング C#
メタプログラミング C#メタプログラミング C#
メタプログラミング C#
 
Boost.Coroutine
Boost.CoroutineBoost.Coroutine
Boost.Coroutine
 
コルーチンの使い方
コルーチンの使い方コルーチンの使い方
コルーチンの使い方
 
Cloud TPU Driver API ソースコード解析
Cloud TPU Driver API ソースコード解析Cloud TPU Driver API ソースコード解析
Cloud TPU Driver API ソースコード解析
 
llvm入門
llvm入門llvm入門
llvm入門
 
Async design with Unity3D
Async design with Unity3DAsync design with Unity3D
Async design with Unity3D
 
Metaprogramming in JuliaLang
Metaprogramming in JuliaLangMetaprogramming in JuliaLang
Metaprogramming in JuliaLang
 
Boost Tour 1.50.0 All
Boost Tour 1.50.0 AllBoost Tour 1.50.0 All
Boost Tour 1.50.0 All
 

Viewers also liked

appengine ja night #6 図解Global Transaction
appengine ja night #6 図解Global Transactionappengine ja night #6 図解Global Transaction
appengine ja night #6 図解Global TransactionSuguru ARAKAWA
 
appengine ja night BT 近くを探す?
appengine ja night BT 近くを探す?appengine ja night BT 近くを探す?
appengine ja night BT 近くを探す?Suguru ARAKAWA
 
appengine ja night #4 Transaction Puzzlers Advanced Topics
appengine ja night #4 Transaction Puzzlers Advanced Topicsappengine ja night #4 Transaction Puzzlers Advanced Topics
appengine ja night #4 Transaction Puzzlers Advanced TopicsSuguru ARAKAWA
 
Hbase勉強会(第一回)メモ
Hbase勉強会(第一回)メモHbase勉強会(第一回)メモ
Hbase勉強会(第一回)メモTakashi Kambayashi
 
クラスローダーについて
クラスローダーについてクラスローダーについて
クラスローダーについてSuguru ARAKAWA
 
業務系SEの今後について
業務系SEの今後について業務系SEの今後について
業務系SEの今後についてTakashi Kambayashi
 
A critique of ansi sql isolation levels 解説公開用
A critique of ansi sql isolation levels 解説公開用A critique of ansi sql isolation levels 解説公開用
A critique of ansi sql isolation levels 解説公開用Takashi Kambayashi
 

Viewers also liked (10)

appengine ja night #6 図解Global Transaction
appengine ja night #6 図解Global Transactionappengine ja night #6 図解Global Transaction
appengine ja night #6 図解Global Transaction
 
Inside of Asakusa DSL
Inside of Asakusa DSLInside of Asakusa DSL
Inside of Asakusa DSL
 
appengine ja night BT 近くを探す?
appengine ja night BT 近くを探す?appengine ja night BT 近くを探す?
appengine ja night BT 近くを探す?
 
appengine ja night #4 Transaction Puzzlers Advanced Topics
appengine ja night #4 Transaction Puzzlers Advanced Topicsappengine ja night #4 Transaction Puzzlers Advanced Topics
appengine ja night #4 Transaction Puzzlers Advanced Topics
 
Hbase勉強会(第一回)メモ
Hbase勉強会(第一回)メモHbase勉強会(第一回)メモ
Hbase勉強会(第一回)メモ
 
クラスローダーについて
クラスローダーについてクラスローダーについて
クラスローダーについて
 
Asakusaの今後の方向性
Asakusaの今後の方向性Asakusaの今後の方向性
Asakusaの今後の方向性
 
業務系SEの今後について
業務系SEの今後について業務系SEの今後について
業務系SEの今後について
 
A critique of ansi sql isolation levels 解説公開用
A critique of ansi sql isolation levels 解説公開用A critique of ansi sql isolation levels 解説公開用
A critique of ansi sql isolation levels 解説公開用
 
20161125 Asakusa Framework Day オラクル講演資料
20161125 Asakusa Framework Day オラクル講演資料20161125 Asakusa Framework Day オラクル講演資料
20161125 Asakusa Framework Day オラクル講演資料
 

Similar to appengine ja night #4 Transaction Puzzlers

JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_cccJEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_cccYujiSoftware
 
知って得するC#
知って得するC#知って得するC#
知って得するC#Shota Baba
 
20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swift20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swiftnecocen
 
Replace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JPReplace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JPAkira Takahashi
 
[Basic 3] 計算量 / 配列, 連結リスト / ハッシュ テーブル / スタック, キュー
[Basic 3] 計算量 / 配列, 連結リスト / ハッシュ テーブル / スタック, キュー[Basic 3] 計算量 / 配列, 連結リスト / ハッシュ テーブル / スタック, キュー
[Basic 3] 計算量 / 配列, 連結リスト / ハッシュ テーブル / スタック, キューYuto Takei
 
Boost9 session
Boost9 sessionBoost9 session
Boost9 sessionfreedom404
 
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8y_taka_23
 
Gura プログラミング言語の紹介
Gura プログラミング言語の紹介Gura プログラミング言語の紹介
Gura プログラミング言語の紹介Yutaka Saito
 
2018/06/23 Sony"s deep learning software and the latest information
2018/06/23 Sony"s deep learning software and the latest information2018/06/23 Sony"s deep learning software and the latest information
2018/06/23 Sony"s deep learning software and the latest informationSony Network Communications Inc.
 
第四回 JavaScriptから始めるプログラミング2016
第四回 JavaScriptから始めるプログラミング2016第四回 JavaScriptから始めるプログラミング2016
第四回 JavaScriptから始めるプログラミング2016kyoto university
 
Brief introduction of Boost.ICL
Brief introduction of Boost.ICLBrief introduction of Boost.ICL
Brief introduction of Boost.ICLyak1ex
 
動的計画法の並列化
動的計画法の並列化動的計画法の並列化
動的計画法の並列化Proktmr
 
Continuation with Boost.Context
Continuation with Boost.ContextContinuation with Boost.Context
Continuation with Boost.ContextAkira Takahashi
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competitionyak1ex
 
わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61TATSUYA HAYAMIZU
 
10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!bitter_fox
 

Similar to appengine ja night #4 Transaction Puzzlers (20)

JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_cccJEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
 
知って得するC#
知って得するC#知って得するC#
知って得するC#
 
20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swift20141128 iOSチーム勉強会 My Sweet Swift
20141128 iOSチーム勉強会 My Sweet Swift
 
Replace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JPReplace Output Iterator and Extend Range JP
Replace Output Iterator and Extend Range JP
 
[Basic 3] 計算量 / 配列, 連結リスト / ハッシュ テーブル / スタック, キュー
[Basic 3] 計算量 / 配列, 連結リスト / ハッシュ テーブル / スタック, キュー[Basic 3] 計算量 / 配列, 連結リスト / ハッシュ テーブル / スタック, キュー
[Basic 3] 計算量 / 配列, 連結リスト / ハッシュ テーブル / スタック, キュー
 
Boost9 session
Boost9 sessionBoost9 session
Boost9 session
 
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
 
Gura プログラミング言語の紹介
Gura プログラミング言語の紹介Gura プログラミング言語の紹介
Gura プログラミング言語の紹介
 
2018/06/23 Sony"s deep learning software and the latest information
2018/06/23 Sony"s deep learning software and the latest information2018/06/23 Sony"s deep learning software and the latest information
2018/06/23 Sony"s deep learning software and the latest information
 
Prosym2012
Prosym2012Prosym2012
Prosym2012
 
第四回 JavaScriptから始めるプログラミング2016
第四回 JavaScriptから始めるプログラミング2016第四回 JavaScriptから始めるプログラミング2016
第四回 JavaScriptから始めるプログラミング2016
 
Brief introduction of Boost.ICL
Brief introduction of Boost.ICLBrief introduction of Boost.ICL
Brief introduction of Boost.ICL
 
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
 
動的計画法の並列化
動的計画法の並列化動的計画法の並列化
動的計画法の並列化
 
Continuation with Boost.Context
Continuation with Boost.ContextContinuation with Boost.Context
Continuation with Boost.Context
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competition
 
Coqチュートリアル
CoqチュートリアルCoqチュートリアル
Coqチュートリアル
 
わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61
 
Objc lambda
Objc lambdaObjc lambda
Objc lambda
 
10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!
 

Recently uploaded

[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略Ryo Sasaki
 
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)Hiroki Ichikura
 
SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものですiPride Co., Ltd.
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNetToru Tamaki
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Danieldanielhu54
 
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Yuma Ohgami
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムsugiuralab
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...Toru Tamaki
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A surveyToru Tamaki
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdftaisei2219
 

Recently uploaded (10)

[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
 
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
 
SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものです
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Daniel
 
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システム
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdf
 

appengine ja night #4 Transaction Puzzlers

  • 1. Transaction Puzzlers appengine ja night #4 あらかわ (@ashigeru)
  • 2. 今日の内容 トランザクション処理の考え方 トランザクション処理のパターン 2010/01/22 appengine ja night #4 - @ashigeru 4
  • 3. トランザクション処理の考え方 リソースを一時的に独占できる技術 同時に変更して不整合が起こる、などを回避 今回は悲観的/楽観的をあまり気にしない App Engineは楽観的並⾏性制御 いずれも一時的にリソースを独占できる 設計/実装時には考慮する必要がある 2010/01/22 appengine ja night #4 - @ashigeru 5
  • 4. App Engineのトランザクション トランザクションはEntity Group (EG)単位 同一EG内のエンティティに対する操作はACID 複数EGにまたがる操作は対応していない 2010/01/22 appengine ja night #4 - @ashigeru 6
  • 5. Entity Groupの構成 同じルートキーを持つエンティティ群 データストア上で近くに配置される 例 Foo(A) EG: Foo(A) Foo(A)/Hoge(B) Foo(B) EG: Foo(B) Bar(A)/Foo(A) EG: Bar(A) Bar(A)/Foo(B)/Hoge(D) 2010/01/22 appengine ja night #4 - @ashigeru 7
  • 6. Entity Groupの特徴 ポイント トランザクションの範囲はエンティティ作成 時に決まり、変更できない EGを⼤きくするとトランザクションで独占す るエンティティが多くなる EGの設計が非常に重要に 間違えると並列性が極端に低下する うまくやればスケールアウトする 2010/01/22 appengine ja night #4 - @ashigeru 8
  • 7. ここまでのまとめ (1) App EngineのトランザクションはEG単位 EG内ではACIDトランザクション EGをまたぐトランザクションは未サポート EGの設計によっては並列性が落ちる EGを⼤きくすると独占範囲が広がる EGを分断すると整合性を保つのが困難 2010/01/22 appengine ja night #4 - @ashigeru 9
  • 8. トランザクション処理のパターン App Engineのトランザクションはやや特殊 パターンで対応したほうがよさそう 本日紹介するもの Read-modify-write トランザクションの合成 ユニーク制約 冪(べき)等な処理 Exactly Once BASE Transaction 2010/01/22 appengine ja night #4 - @ashigeru 10
  • 9. 注意点 プログラムの説明に擬似コードを多⽤ 言語はJavascriptライク APIはJavaのLow-Level APIライク ⾒慣れない言語要素 キーリテラル – KEY:… KEY:Foo(A), KEY:Foo(A)/Bar(B), など データストア get(tx, key), put(tx, entity), beginTransaction() タスクキュー enqueue([tx,] statement) 2010/01/22 appengine ja night #4 - @ashigeru 11
  • 10. パターン: read-modify-write エンティティのプロパティを変更する 例: カウンタの増加 ショッピングカートに商品を追加 現在の値をもとに次の値が決まる 読む、変更、書き戻す、の3ステップが必要 途中で割り込まれると不整合が起こる 2010/01/22 appengine ja night #4 - @ashigeru 12
  • 11. read-modify-write (1) 考え方 読んでから書き戻すまでエンティティを独占 100 + 1 100 101 2010/01/22 appengine ja night #4 - @ashigeru 13
  • 12. read-modify-write (2) var tx = beginTransaction() try { var counter = get(tx, KEY:Counter(C)) counter.value++ put(tx, counter) tx.commit() } finally { if (tx.isActive()) tx.rollback() } 2010/01/22 appengine ja night #4 - @ashigeru 14
  • 13. read-modify-write (3) var tx = beginTransaction() try { var counter = get(tx, KEY:Counter(C)) counter.value++ put(tx, counter) tx.commit() 読んでから書き戻す } までをACIDに⾏う finally { if (tx.isActive()) tx.rollback() } 2010/01/22 appengine ja night #4 - @ashigeru 15
  • 14. DSL: atomic (tx) { … } 以後は下記のように省略 トランザクションの開始と終了を簡略化 atomic(tx) { var counter = get(tx, KEY:Counter(C)) counter.value++ put(tx, counter) } 2010/01/22 appengine ja night #4 - @ashigeru 16
  • 15. パターン: トランザクションの合成 同じEGに対する複数のトランザクション 処理を合成 例: 2つのカウンタを同時に変更 (恣意的) 非正規化した2つの情報を同時に更新 注意点 分断したトランザクションでは、途中で失敗 した際に修復が⼤変 2010/01/22 appengine ja night #4 - @ashigeru 17
  • 16. トランザクションの合成 (1) 考え方 同じEGのトランザクションが2つあったら、 一度に処理してしまう 15 16 30 31 2010/01/22 appengine ja night #4 - @ashigeru 18
  • 17. トランザクションの合成 (2) atomic(tx) { var a = get(tx, KEY:Eg(C)/Counter(A)) a.value++ put(tx, a) var b = get(tx, KEY:Eg(C)/Counter(B)) b.value++ put(tx, b) } 2010/01/22 appengine ja night #4 - @ashigeru 19
  • 18. トランザクションの合成 (3) atomic(tx) { var a = get(tx, KEY:Eg(C)/Counter(A)) a.value++ put(tx, a) var b = get(tx, KEY:Eg(C)/Counter(B)) b.value++ put(tx, b) } 同じEGのエンティティ に対する操作 2010/01/22 appengine ja night #4 - @ashigeru 20
  • 19. トランザクションの合成 (4) atomic(tx) { var a = get(tx, KEY:Eg(C)/Counter(A)) a.value++ put(tx, a) var b = get(tx, KEY:Eg(C)/Counter(B)) b.value++ put(tx, b) } 複数のトランザクション を合成, 全体がACIDに 2010/01/22 appengine ja night #4 - @ashigeru 21
  • 20. パターン: ユニーク制約 重複するエンティティの登録を防止する 例: 同じIDを持つユーザの登録を防ぐ ダブルブッキングを防ぐ 注意点 データストアは制約機能を組み込んでいない クエリはトランザクションに参加できない 2010/01/22 appengine ja night #4 - @ashigeru 22
  • 21. ユニーク制約 (1) 考え方 エンティティの入れ物ごと独占 入れ物が空なら追加するエンティティは一意 @hoge @hoge @hoge 2010/01/22 appengine ja night #4 - @ashigeru 23
  • 22. ユニーク制約 (2) var key = KEY:User(hoge@example.com) atomic(tx) { var user = get(tx, key) if (user != null) { throw new NotUniqueException() } user = new User(key, ...) put(tx, user) } 2010/01/22 appengine ja night #4 - @ashigeru 24
  • 23. ユニーク制約 (3) var key = KEY:User(hoge@example.com) atomic(tx) { var user = get(tx, key) ユニーク制約をキーで if (user != null) { 表す(メールアドレス) throw new NotUniqueException() } user = new User(key, ...) put(tx, user) } 2010/01/22 appengine ja night #4 - @ashigeru 25
  • 24. ユニーク制約 (4) var key = KEY:User(hoge@example.com) atomic(tx) { var user = get(tx, key) if (user != null) { throw new NotUniqueException() } user = new User(key, ...) put(tx, user) そのエンティティが } すでにあれば制約違反 2010/01/22 appengine ja night #4 - @ashigeru 26
  • 25. ユニーク制約 (5) var key = KEY:User(hoge@example.com) atomic(tx) { var user = get(tx, key) if (user != null) { 存在しなければ ユニークなので追加 throw new NotUniqueException() } user = new User(key, ...) put(tx, user) } 2010/01/22 appengine ja night #4 - @ashigeru 27
  • 26. ユニーク制約 (6) var key = KEY:User(hoge@example.com) atomic(tx) { var user = get(tx, key) if (user != null) { throw new NotUniqueException() } user = new User(key, ...) put(tx, user) } getからputまでを独占 2010/01/22 appengine ja night #4 - @ashigeru 28
  • 27. ここまでのまとめ (2) read-modify-write 最初に読んでから書き戻すまで独占 トランザクションの合成 同一EGに対する操作をまとめる ユニーク制約 入れ物を独占してからエンティティを作成 すでにあったらユニークじゃないので失敗 2010/01/22 appengine ja night #4 - @ashigeru 29
  • 28. パターン: 冪(べき)等な処理 1回分しか効果を出さない処理 2回以上成功しても、1回分しか反映しない 例: フォームの多重送信を防止 お一人様一点限り。 注意点 英語でidempotentだけど覚えにくい 2010/01/22 appengine ja night #4 - @ashigeru 30
  • 29. 冪等な処理 (1) 考え方 「処理がユニークに成功する」ということ まだ成功していなかったら成功させる 一度成功していたら何もしない 成功 成功 成功 結果 2010/01/22 appengine ja night #4 - @ashigeru 31
  • 30. 冪等な処理 (2) var key = KEY:Counter(C)/Flag(unique) atomic(tx) { var flag = get(tx, key) if (flag != null) { return } put(tx, new Flag(key)) var counter = get(tx, KEY:Counter(C)) counter.value++ put(tx, counter) } 2010/01/22 appengine ja night #4 - @ashigeru 32
  • 31. 冪等な処理 (3) var key = KEY:Counter(C)/Flag(unique) atomic(tx) { var flag = get(tx, key) 「ユニークなキー」を表す if (flag != null) { → db.allocate_ids() return } → DatastoreService.allocateIds() put(tx, new Flag(key)) var counter = get(tx, KEY:Counter(C)) counter.value++ put(tx, counter) } 2010/01/22 appengine ja night #4 - @ashigeru 33
  • 32. 冪等な処理 (4) var key = KEY:Counter(C)/Flag(unique) atomic(tx) { var flag = get(tx, key) if (flag != null) { return } put(tx, new Flag(key)) var counter = get(tx, KEY:Counter(C)) ユニーク制約をユニークなキーで。 counter.value++ put(tx, counter) 1回目は確実に成功、 } キーを使いまわせば2回目は失敗 2010/01/22 appengine ja night #4 - @ashigeru 34
  • 33. 冪等な処理 (5) var key = KEY:Counter(C)/Flag(unique) atomic(tx) { var flag = get(tx, key) if (flag != null) { それ以降の処理は return 一度だけしか⾏われない } put(tx, new Flag(key)) var counter = get(tx, KEY:Counter(C)) counter.value++ put(tx, counter) } 2010/01/22 appengine ja night #4 - @ashigeru 35
  • 34. 冪等な処理 (6) var key = KEY:Counter(C)/Flag(unique) atomic(tx) { var flag = get(tx, key) if (flag != null) { return } put(tx, new Flag(key)) var counter = get(tx, KEY:Counter(C)) counter.value++ put(tx, counter) 全体を合成してACIDに } 2010/01/22 appengine ja night #4 - @ashigeru 36
  • 35. 冪等な処理 (まとめ) 冪等な処理 「1回分しか効果を出さない」パターン やりかた 「成功」済みかどうかについてユニーク制約 トランザクションを合成 そのキーでユニーク制約を確認 OKなら続きの処理を⾏う 注意点 ごみ(Flag)が残るが、これを消すのは一⼿間 2010/01/22 appengine ja night #4 - @ashigeru 37
  • 36. パターン: Exactly Once 確実にぴったり1回成功する処理 冪等な処理では0回の場合もある (最⼤1回) 例: カウンタの値を正確に更新する(恣意的) 注意点 「確実に失敗する」処理には適⽤できない 2010/01/22 appengine ja night #4 - @ashigeru 38
  • 37. Exactly Once (1) 考え方 1度しか反映されない操作を執拗に繰り返す いつかは成功するはず 間違えて2回以上成功しても効果は1回分 2010/01/22 appengine ja night #4 - @ashigeru 39
  • 38. Exactly Once (2) var key = KEY:Counter(C)/Flag(unique) while (true) { try { atomic(tx) { var flag = get(tx, key) if (flag != null) { return } put(tx, new Flag(key)) var counter = get(tx, KEY:Counter(C)) counter.value++ put(tx, counter) } } catch (ignore) {} } 2010/01/22 appengine ja night #4 - @ashigeru 40
  • 39. Exactly Once (3) var key = KEY:Counter(C)/Flag(unique) while (true) { try { atomic(tx) { var flag = get(tx, key) 冪等な処理の if (flag != null) { return パターン } put(tx, new Flag(key)) var counter = get(tx, KEY:Counter(C)) counter.value++ put(tx, counter) } } catch (ignore) {} } 2010/01/22 appengine ja night #4 - @ashigeru 41
  • 40. Exactly Once (4) var key = KEY:Counter(C)/Flag(unique) while (true) { try { atomic(tx) { 冪等な処理を var flag = get(tx, key) 無限に繰り返す if (flag != null) { return } put(tx, new Flag(key)) var counter = get(tx, KEY:Counter(C)) counter.value++ put(tx, counter) } 30秒ルールがあるので } catch (ignore) {} 確実とはいえない } 2010/01/22 appengine ja night #4 - @ashigeru 42
  • 41. Exactly Once (5) var key = KEY:Counter(C)/Flag(unique) enqueue(atomic(tx) { var flag = get(tx, key) if (flag != null) { return } put(tx, new Flag(key)) var counter = get(tx, KEY:Counter(C)) counter.value++ put(tx, counter) }) 代わりにTask Queueで 成功するまで繰り返し 2010/01/22 appengine ja night #4 - @ashigeru 43
  • 42. Exactly Once (まとめ) Exactly Once 「確実にぴったり1回成功する」パターン ただし、いつ成功するかは不明 やりかた 冪等な処理を無限に繰り返す 一度成功したらあとは無駄なので打ち切る App EngineのTask Queueを使える 成功するまで無限に繰り返す、という性質 30秒ルールがあるからwhile(true)は無理 2010/01/22 appengine ja night #4 - @ashigeru 44
  • 43. パターン: BASE Transaction 複数のEGにまたがるゆるいトランザク ション ACIDほど強い制約がない 例: 口座間の送⾦処理 注意点 途中の状態が外側に⾒える ACIDよりアプリケーションが複雑 2010/01/22 appengine ja night #4 - @ashigeru 45
  • 44. BASE Transaction (1) 送⾦処理で本当にやりたいことは2つ Aの口座からX円引く Bの口座にX円足す 「トランザクションの合成」は困難 Aの口座とBの口座を同じEGに配置? Aから送⾦されうるすべての口座を同じEGに? トランザクションを分断すると危険 失敗例:「Aの口座からX円引いたけどBに届かない」 補償トランザクションすら失敗する可能性 2010/01/22 appengine ja night #4 - @ashigeru 46
  • 45. BASE Transaction (2) 単純に考えてみる まずAの口座から5000円引く そのあと「一度だけ」Bの口座に5000円足す atomic (tx1) { var a = get(tx1, KEY:Account(A)) Exactly Once a.amount -= 5000 put(tx1, a) } atomic (tx2) { var b = get(tx2, KEY:Account(B)) b.amount += 5000 put(tx2, b) } 2010/01/22 appengine ja night #4 - @ashigeru 47
  • 46. BASE Transaction (3) var key = KEY:Account(B)/Flag(unique) atomic (tx1) { var a = get(tx1, KEY:Account(A)) a.amount -= 5000 put(tx1, a) enqueue(tx1, atomic(tx2) { var flag = get(tx2, key) if (flag != null) { return } put(tx2, new Flag(key)) var b = get(tx2, KEY:Account(B)) b.amount += 5000 put(tx2, b) }) } 2010/01/22 appengine ja night #4 - @ashigeru 48
  • 47. BASE Transaction (4) var key = KEY:Account(B)/Flag(unique) atomic (tx1) { var a = get(tx1, KEY:Account(A)) a.amount -= 5000 put(tx1, a) enqueue(tx1, atomic(tx2) { var flag = get(tx2, key) Read-modify-write if (flag != null) { (A -= 5000) return } put(tx2, new Flag(key)) var b = get(tx2, KEY:Account(B)) b.amount += 5000 put(tx2, b) }) } 2010/01/22 appengine ja night #4 - @ashigeru 49
  • 48. BASE Transaction (5) var key = KEY:Account(B)/Flag(unique) atomic (tx1) { var a = get(tx1, KEY:Account(A)) a.amount -= 5000 put(tx1, a) enqueue(tx1, atomic(tx2) { var flag = get(tx2, key) Read-modify-write if (flag != null) { (B += 5000) return } put(tx2, new Flag(key)) var b = get(tx2, KEY:Account(B)) b.amount += 5000 put(tx2, b) }) } 2010/01/22 appengine ja night #4 - @ashigeru 50
  • 49. BASE Transaction (6) var key = KEY:Account(B)/Flag(unique) atomic (tx1) { var a = get(tx1, KEY:Account(A)) a.amount -= 5000 put(tx1, a) enqueue(tx1, atomic(tx2) { var flag = get(tx2, key) Exactly Once if (flag != null) { (B += 5000) return } put(tx2, new Flag(key)) var b = get(tx2, KEY:Account(B)) b.amount += 5000 put(tx2, b) }) } 2010/01/22 appengine ja night #4 - @ashigeru 51
  • 50. BASE Transaction (7) var key = KEY:Account(B)/Flag(unique) atomic (tx1) { var a = get(tx1, KEY:Account(A)) a.amount -= 5000 put(tx1, a) enqueue(tx1, atomic(tx2) { var flag = get(tx2, key) 全体を合成 if (flag != null) { (A -= 5000, B += 5000) return } put(tx2, new Flag(key)) var b = get(tx2, KEY:Account(B)) b.amount += 5000 put(tx2, b) }) } 2010/01/22 appengine ja night #4 - @ashigeru 52
  • 51. BASE Transaction (まとめ) BASE Transaction EGをまたいだゆるいトランザクション いつか確実に完了する、という性質 やりかた トランザクションを合成 一つ目のEGに対して操作を⾏う Exactly Onceで二つ目のEGに対して操作を⾏う 注意点 操作が⾏われるまでタイムラグがある Eventual Consistency: いずれ整合性が取れる 二つ目のEGに対する操作は制約をかけられない 送⾦先に受け取り拒否されるとすごく困る 2010/01/22 appengine ja night #4 - @ashigeru 53
  • 52. ここまでのまとめ (3) パターン: 冪等な処理 操作自体を最⼤一回だけ(ユニーク)にする = ユニーク制約 + トランザクションの合成 パターン: Exactly Once 最⼤一回だけ成功する処理を無限に繰り返す = 冪等な処理 + Task Queue パターン: BASE Transaction 自分を変更後、相⼿の変更を確実に一度だけ適⽤ = read-modify-write + 合成 + Exactly Once 2010/01/22 appengine ja night #4 - @ashigeru 54
  • 53. おわりに App Engineのトランザクションは「パズ ル」になりがち 複雑な制約を考慮しつつ、時間内に解く ルールも定⽯もあるので、積み重ねが⼤切 「仮説→検証」のサイクルが必要な段階 みんなで情報共有 パターンやアンチパターンを持ち寄ろう 2010/01/22 appengine ja night #4 - @ashigeru 55
  • 54. 参考文献 Programming Google App Engine Oreilly & Associates Inc, 2009/11 リレーショナルデータベース入門 サイエンス社, 2003/03 トランザクション処理 日経BP社, 2001/10 BASE: An Acid Alternative http://queue.acm.org/detail.cfm?id=1394128 2010/01/22 appengine ja night #4 - @ashigeru 56