========== cockroachの分散トランザクション
- server(server.go) : リクエストを受け取り、rangeから探した処理先に送信する。
- node(node.go) : serverが受け取ったリクエストをraft的に複数ノードにレプリケートする。
- たぶん、server => node という流れで処理されるのだろう。
- server
- client.DB => TxnCoordSender => DistSender
- txnをつくる => txnのタイムスタンプやstateを管理する => rangeから送信先を決定して送信
- node
- clietn.DB => TxnCoordSender => LocalSender
- txnをつくる => txnのタイプスタンプやstateを管理する => ローカルのノードからコマンドのraftログへの書き込みを行う
- なぜどちらもTxnCoordSenderでラップされているのか? => 必要に応じてどちらも分散txnができるようにしているのではないか?
- 呼び出し時にtxnを作るが、保存まではしない。保存はどこかでしているが、どこでやっているかは謎
- txnは呼び出し側のメモリ上のmapに保持され、定期的なheartbeatで何かをしている。何をしている?
- 長い時間更新されてないtxnは削除
- 更新が続いている場合、そのtxnをキーに対応したノードのstorageに書き込む
- その後、heartbeatを受け取る側はそれのtsをみてtimestampをアップデートする
- 結果として更新されたtxnを呼び出し側に返す。abort/commitになればheartbeatは終わり
- rangeにたいする操作を受けた側は、操作を行った側(TxnCoordSender)にtxnを返している
- txnの状態および、エラーをみてtxnをコミットするかどうか決めている
- どうもputごとにraft logに書いて...をやっているわけではないらしい。TxnCoordSenderにコマンドをためてまとめて全部投げている。全transactionを一個のlogにするようなイメージかもしれない。
- よくみたら1つ1つやっていた。
- linearizability / serialized snapshot isolationの違いはタイムスタンプの違いをどのように許容するか、だけらしい。
- 分散transactionで使われるTransaction recordとmvccのトランザクショナルナ更新は別なので注意 transaction recordをcommit => mvccのトランザクショナルナ更新となっている。
- InternalPushTxn => 複数のtxnが競合した時に、可能であれば後のtxnで前のtxnを上書きする。あるいはgc的に途中で止まったtxnを終了させるなど
- EndTransaction => txnの終了
- InternalHeartbeatTxn => coordinatorが自分のtxnの状態でtxnレコードを更新し続ける。必要なのだろうか。特に5秒に一回しか更新しないのであまり効果的な気がしないが
- EndTransaction/InternalPushTxnではレコードがなければ送ってきたtxnでレコードを作っている
- txnを含むExecuteCmdが、対象となるレコードがすでに別のtxnでロックされていて(WriteIntentError)失敗した場合に、maybeResolveWriteIntentError経由で呼ばれている.
- retryは、RunTransaction自体のretryとPushTxnによるretryがある。後者が失敗するとtransaction全体がretryになるイメージ。
- readする場合にリクエスト先がleaderであることを保証しなくてはいけない。これはreplicaのうち、そのノードがプライマリだと思っているノードにリクエストを投げればいいという話ではない(リクエストが届いた瞬間にはすでにリーダーではないかもしれないから)。consistentなリクエストを受け取ったノードは自身がリーダーになる必要がある。これがleader leaseの考え方であり、実装が必要。luactにおいてconsistentをしていしたばあいには、cockroachにおけるCONSENSUS-read consistencyの意味。
========== implementation plan
- rangeにnon-mvccに読み書きを行うsystem rangeを追加(flagで挙動をコントロール).bootstrapで作成してmeta2から検索可能にする
- range_managerにpush_txn/end_txn/hb_txnを追加。それぞれPushTxn/EndTransaction/HeartbeatTxnに相当。それぞれsystem rangeに対して発行される つまり、keyからsystem rangeのキーを作ってfindして、そこにpush/end/hbを発行する
- txncoordで_M.rmを使ってこの辺りのコマンドを送る。
- write commandの内部で読んでいるrange:writeをpcallにしてエラーを受け取る。エラーが起きた時にはpushtxnでリカバリーを試みる。失敗した場合はさらに外(txncoord.run_txn)にエラーをpropagate
- txncoord.run_txnはさらにtxn エラーなども見ながらretryを制御する。以上。