Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
How COMMIT Works in CockroachDB
kota2and3kan
March 09, 2021
Technology
15
1.8k
How COMMIT Works in CockroachDB
kota2and3kan
March 09, 2021
Tweet
Share
More Decks by kota2and3kan
See All by kota2and3kan
分散トランザクション in CockroachDB / Distributed Tx in CockroachDB
kota2and3kan
0
1k
Resolving Tx Conflicts in CockroachDB ~Short Version~
kota2and3kan
0
140
Resolving Tx Conflicts in CockroachDB
kota2and3kan
5
970
How COMMIT Works in CockroachDB ~Short Version~
kota2and3kan
0
48
20190701_What_is_CockroachDB
kota2and3kan
10
1.6k
Other Decks in Technology
See All in Technology
數據的多重宇宙 @ LINE Taiwan
line_developers_tw
PRO
0
1k
LINE WORKS API 2.0について
mmclsntr
0
150
CTOのためのQAのつくりかた #scrumniigata / SigSQA How to create QA for CTOs and VPoEs
caori_t
0
360
220524_開発PM勉強会vol.11_MNTSQ
kkawase
0
120
A1A会社紹介資料-2022-05-20
a1a
2
1.1k
Kubernetesの上に作る、統一されたマイクロサービス運用体験
tkuchiki
1
1.3k
A Conditional Point Diffusion-Refinement Paradigm for 3D Point Cloud Completion
takmin
0
230
Declarative Clients in Spring
olgamaciaszek
0
110
testing journey / テストが嫌いでIT業界を離れるはずだったのに〜テスト嫌いが現場で品質改善を実施するまでの物語〜
aki_moon
1
440
CAMのサービス開発の歴史と共通基盤を使った 開発スタイルへの変遷について
ishikawa_pro
0
110
失敗を経験したあなたへ〜建設的なインシデントの振り返りを行うために実践するべきこと〜
nobuakikikuchi
0
220
How We Foster Reliability in Diversity
nari_ex
PRO
10
3.3k
Featured
See All Featured
Infographics Made Easy
chrislema
233
17k
The Brand Is Dead. Long Live the Brand.
mthomps
45
2.7k
Building an army of robots
kneath
299
40k
Intergalactic Javascript Robots from Outer Space
tanoku
261
25k
Testing 201, or: Great Expectations
jmmastey
21
5.4k
Unsuck your backbone
ammeep
659
55k
Raft: Consensus for Rubyists
vanstee
126
5.4k
Build The Right Thing And Hit Your Dates
maggiecrowley
19
1.2k
JazzCon 2018 Closing Keynote - Leadership for the Reluctant Leader
reverentgeek
172
8.3k
Ruby is Unlike a Banana
tanoku
91
9.2k
Making Projects Easy
brettharned
98
4.3k
How STYLIGHT went responsive
nonsquared
85
3.9k
Transcript
How COMMIT works in CockroachDB \ コンニチワ /
• Name: Takanori Yokoyama ◦ @kota2and3kan (Twitter, GitHub) • Job:
Technical Support • Like: DB ◦ PostgreSQL, CockroachDB • Dislike: Real Cockroach @kota2and3kan こたつ&&みかん Who am I.
※Note※ ちょっと自信が無いとこもあります! 間違ってたらごめんさない!!! \ ユルシテ /
目次 • CockroachDB についてざっくり概要 • COMMIT / ABORT (Write) の動作
• Read の動作 • Parallel Commits • 話せなかったこと • まとめ • おまけ
CockroachDB についてざっくり概要 \ ガイヨウ /
CockroachDB 概要 • 分散 SQL データベース。いわゆる NewSQL。 • Spanner と
F1 にインスパイアされている。 • 公式曰く CAP でいうと CP システム。 ◦ https://www.cockroachlabs.com/docs/stable/frequently-asked-questions.html#how-is-cockroachdb-both-highly -available-and-strongly-consistent ◦ (Linearizability は満たしてないので厳密にいうと “P システム” ?) • コンセンサスアルゴリズムには Raft を使用。 • Storage Engine に独自開発の KVS (Pebble) を使用。 ◦ https://github.com/cockroachdb/pebble ◦ 以前は RocksDB を使っていた。今も後方互換として使える。 • Isolation Level : Serializable • Consistency Level : No Stale Reads ◦ https://www.cockroachlabs.com/blog/consistency-model/
全てのノードで Read / Write のクエリを処理可能 Load Balancer Service (k8s) etc...
Read / Write
全てのノードで Read / Write のクエリを処理可能 Load Balancer Service (k8s) etc...
Read / Write Scale OUT / IN も可能。
CockroachDB についてざっくり概要 格納されるデータ (Key-Value) の構造 \ キーバリュー /
Table と Key-Value id c1 c2 c3 1 foo bar
baz 2 hoge fuga piyo 3 (・ω・´) (´・ω・`) m9(^Д^) key value /Table/54/1/1/0/1615095374.173379261,0 c1: foo, c2: bar, c3: baz /Table/54/1/2/0/1615095392.837630582,0 c1: hoge, c2: fuga, c3:piyo /Table/54/1/3/0/1615095596.484809926,0 c1: (・ω・´), c2: (´・ω・`), c3: m9(^Д^) test ※ id -> Primary Key ・Key-Value 形式に変換。 ・Key で Sort。 ・最近はデフォルトだと 1 record = 1 KV。 ・Key は以下の形式で変換される。 ・Key に Timestamp が含まれている。 /Table/Table ID/Primary Key ID/Value of PK/Column Family/Timestamp https://github.com/cockroachdb/cockroach/blob/master/docs/tech-notes/encoding.md
Table と Key-Value (補足) id c1 c2 c3 1 foo
bar baz 2 hoge fuga piyo 3 (・ω・´) (´・ω・`) m9(^Д^) key value /Table/54/1/1/0/1615095374.173379261,0 c1: foo, c2: bar, c3: baz /Table/54/1/2/0/1615095392.837630582,0 c1: hoge, c2: fuga, c3:piyo /Table/54/1/3/0/1615095596.484809926,0 c1: (・ω・´), c2: (´・ω・`), c3: m9(^Д^) test ※ id -> Primary Key key value 0xBE898988001669F8AF87FB1ABD09 0x4CF924910A2603666F6F1603626172160362617A 0xBE898A88001669F8B3E074FA7609 0x65EEC34A0A2604686F676516046675676116047069796F 0xBE898B88001669F8E34AC654C609 0xC11CE7CF0A260C28E383BBCF89E383BBC2B429160D28C2B4E383BBCF89E3 83BB602916086D39285ED0945E29 ※実際は Byte 形式で格納される。
Keyspace test Monolithic & Sorted by key tbl1 tbl2 key
value /Table/54/1/1/0/1615095374.173379261,0 c1: foo, c2: bar, c3: baz /Table/54/1/2/0/1615095392.837630582,0 c1: hoge, c2: fuga, c3:piyo /Table/54/1/3/0/1615095596.484809926,0 c1: (・ω・´), c2: (´・ω・`), c3: m9(^Д^) Key-Value と Keyspace Key-Value 形式に変換された各 Table は、 Key で Sort された Keyspace (論理的に 1つの大きな塊) に map される。
Keyspace と Range Range1 Range2 Range3 ・ ・ ・ Range
N 512MB 512MB 512MB Keyspace ・いい感じに分割。 ・デフォルト 512MB。
Range と Node Range1 Range2 Range3 ・いい感じに分散。
Range と Node Range2 Range1 Range3 Range1 Range2 Range3
Range と Node Range2 Range1 Range3 Node 障害 = データ消失
Range と Replica Range2 Range1 Range3 • Range を複製 (Replica)。
• デフォルト 3つ。 • Replica をいい感じ分散。 • 各 Replica は Raft を使って複製する。 Range1 Range1 Range2 Range2 Range3 Range3
Range と Replica Range2 Range1 Range3 適当に Read / Write
= データ不整合 Range1 Range1 Range2 Range2 Range3 Range3
Replica と Leaseholder Range1 Range2 Range3 Range1 Range2 Range3 Range1
Range2 Range3 lease lease lease • Replica の 1つが lease を持つ。 • lease を持つ Range をLeaseholder と呼 ぶ。 • Read / Write は全て Leaseholder で処 理。 • 基本 Leaseholder = “Raft Leader” にな る。
5匹の場合 Range1 Range2 Range3 Range1 Range2 Range3 Range1 Range2 Range3
lease lease lease • Node が増える と、いい感じに分 散される。
Range Group Range1 Range2 Range3 Range1 Range2 Range3 Range1 Range2
Range3 lease lease lease Raft Group 1 Raft Group 2 Raft Group 3 • Range 毎に Raft Group を作成。
CockroachDB についてざっくり概要 SQL を誰が処理するか \ クエリ /
Gateway Node (Coordinator) • クエリを受け取った Node は “Gateway Node” と呼ばれる。
◦ いわゆる “コーディネーター” Node。 • Gateway Node が Tx を管理する。 ◦ Tx 内の各 SQL は Gateway Node 経由で各 Node にルーティングされる。 • 各 Node で処理されたデータ (SQL の結果) は、Gateway Node に集約され、Gateway Node から Client に SQL の結果を返す。 SQL
Range1 Range2 Range3 lease • Gateway Node 内に Leaseholder が有る。
◦ 自分で処理。 • Gateway Node 内に Leaseholder が無い。 ◦ Leaseholder を持つ Node にクエリをルーティ ング。 • つまり Tx 内の各 SQL は Raft Leader 経由で Read / Write を実行する。 ◦ Raft Leader を持つ Node に SQL がルーティン グされる。 Gateway Node の動作
Write Query その1 Write lease lease lease • Gateway Node
== Leaseholder
Write Write Query その2 lease lease lease • Gateway Node
!= Leaseholder
• 時間が足りないので割愛。 • もう少し細かい説明は以下参照。 ◦ https://speakerdeck.com/kota2and3kan/20190701-what-is-cockroachdb?slide=48 Gateway Node の動作
COMMIT / ABORT (Write) の動作 \ コミット /
用語概要 • Transaction Record ◦ https://github.com/cockroachdb/cockroach/blob/v20.2.5/pkg/roachpb/data.pb.go#L940 • Write Intent ◦
https://github.com/cockroachdb/cockroach/blob/v20.2.5/pkg/roachpb/data.pb.go#L985 ◦ https://github.com/cockroachdb/cockroach/blob/v20.2.5/pkg/storage/enginepb/mvcc3.pb.go#L 31 • Transaction Liveness Threshold ◦ https://github.com/cockroachdb/cockroach/blob/v20.2.5/pkg/kv/kvserver/txnwait/queue.go#L46
用語概要 (Transaction Record) • Transaction の状態を管理する。 • Tx 内で最初に Write
された key と同じ Range に書き込まれる。 • 以下の State がある。 ◦ PENDING ▪ Tx は処理中。 ◦ COMMITTED ▪ Tx は COMMIT 済み。 ◦ STAGING ▪ Tx は Parallel Commits で処理中。 ◦ ABORTED ▪ Tx は ABORT 済み。 ◦ (MISSING) ▪ Transaction Record そのものが無い。
用語概要 (Write Intent) • Transaction の状態 (COMMIT / ABORT) が確定していないレコード
(KV) に付けられる印。 • 以下のような情報を持っている。 ◦ 関連する (当該レコードを Write した) Tx の ID。 ◦ Write Intent が書き込まれた時間。
用語概要 (Transaction Liveness Threshold) • Transaction の生死 (Tx を管理している Node
の生死) を判断するための 閾値 (死んだと判断するまでの時間)。 ◦ Gateway Node での障害発生有無を確認する場合等に使われる。 ◦ 文字通り、そのまんまの感じのやつ。
COMMIT の仕組み Range : Set of KV date (512BM) Tx1
(timestamp t1) BEGIN; Write (key1, x1); COMMIT; key1 (t0): x0 key2 (t0): y0 key3 (t0): z0
Tx 開始 Range : Set of KV date (512BM) Tx1
(t1) BEGIN; Write (key1, x1); COMMIT; key1 (t0): x0 key2 (t0): y0 key3 (t0): z0
key1 を更新 Range : Set of KV date (512BM) Tx1
(t1) BEGIN; Write (key1, x1); COMMIT; Write (UPDATE) は Inplace Update ではなく、新しい Timestamp で追記する。 この時 COMMIT / ABORT が 確定していない KV には “write intent” という印が付 け られる。 key1 (t0): x0 key2 (t0): y0 key3 (t0): z0 key1 (t1): x1 (intent:Tx1)
Transaction Record (PENDING) を作成 Range : Set of KV date
(512BM) Tx1 (t1) BEGIN; Write (key1, x1); COMMIT; Tx の状態を管理するための ”Transaction Record” が他の KV データ同様に書き込まれる。 各 “write intent” は、対応する Transaction Record を特定する 情報 (Tx の ID) を持っている。 key1 (t0): x0 key2 (t0): y0 key3 (t0): z0 key1 (t1): x1 (intent:Tx1) Tx1’s “Transaction Record” ------------------------------------ State: PENDING
Transaction Record の State を COMMIT に更新 Range : Set
of KV date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); COMMIT; COMMIT を実行すると、 Transaction Record の値が COMMITTED に変更される。 このタイミングで、クライアントに COMMIT 完了を返す。 key1 (t0): x0 key2 (t0): y0 key3 (t0): z0 key1 (t1): x1 (intent:Tx1) Tx1’s “Transaction Record” ------------------------------------ State: COMMITTED
“write intent” を削除 (COMMIT とは非同期で実施) Range : Set of KV
date (512BM) BEGIN; Write (key1, x1); COMMIT; key1 (t0): x0 key2 (t0): y0 key3 (t0): z0 key1 (t1): x1 Tx1’s “Transaction Record” ------------------------------------ State: COMMITTED COMMIT 完了後、非同期で “write intent” が削除され、通常 の KV と同じ状態になる。
Tx Record を削除 (COMMIT とは非同期で実施) Range : Set of KV
date (512BM) BEGIN; Write (key1, x1); COMMIT; key1 (t0): x0 key2 (t0): y0 key3 (t0): z0 key1 (t1): x1 Tx1’s “Transaction Record” ------------------------------------ State: COMMITTED Tx に関連する全ての “write intent” が削除された後、 Transaction Record が非同期 で削除される。
ABORT の仕組み Range : Set of KV date (512BM) Tx1
(timestamp t1) BEGIN; Write (key1, x1); ABORT; key1 (t0): x0 key2 (t0): y0 key3 (t0): z0
Tx 開始 Range : Set of KV date (512BM) Tx1
(t1) BEGIN; Write (key1, x1); ABORT; key1 (t0): x0 key2 (t0): y0 key3 (t0): z0
key1 を更新 Range : Set of KV date (512BM) Tx1
(t1) BEGIN; Write (key1, x1); ABORT; Write (UPDATE) は Inplace Update ではなく、新しい Timestamp で追記する。 この時 COMMIT / ABORT が 確定していない KV には “write intent” という印が付 け られる。 key1 (t0): x0 key2 (t0): y0 key3 (t0): z0 key1 (t1): x1 (intent:Tx1)
Transaction Record (PENDING) を作成 Range : Set of KV date
(512BM) Tx1 (t1) BEGIN; Write (key1, x1); ABORT; Tx の状態を管理するための ”Transaction Record” が他の KV データ同様に書き込まれる。 各 “write intent” は、対応する Transaction Record を特定する 情報 (Tx の ID) を持っている。 key1 (t0): x0 key2 (t0): y0 key3 (t0): z0 key1 (t1): x1 (intent:Tx1) Tx1’s “Transaction Record” ------------------------------------ State: PENDING
Transaction Record の State を ABORT に更新 Range : Set
of KV date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); ABORT; ABORT を実行すると、 Transaction Record の値が ABORTED に変更される。 key1 (t0): x0 key2 (t0): y0 key3 (t0): z0 key1 (t1): x1 (intent:Tx1) Tx1’s “Transaction Record” ------------------------------------ State: ABORTED
“write intent” を削除 (ABORT とは非同期で実施) Range : Set of KV
date (512BM) BEGIN; Write (key1, x1); ABORT; key1 (t0): x0 key2 (t0): y0 key3 (t0): z0 key1 (t1): x1 (intent:Tx1) Tx1’s “Transaction Record” ------------------------------------ State: ABORTED ABORT 後、非同期で ABORT された key が削除される。
Tx Record を削除 (ABORT とは非同期で実施) Range : Set of KV
date (512BM) BEGIN; Write (key1, x1); ABORT; key1 (t0): x0 key2 (t0): y0 key3 (t0): z0 Tx1’s “Transaction Record” ------------------------------------ State: ABORTED Tx に関連する全ての key が削 除された後、Transaction Record が非同期で削除される。
Read の動作 \ リード /
Read Tx はどの値を読み込むのか? Range : Set of KV date (512BM)
Tx1 (timestamp t1) BEGIN; Write (key1, x1); COMMIT / ABORT; Tx2 (timestamp t2) Read (key1); key1 (t0): x0
Read Tx は Timestamp を見て読み込む値を決める Range : Set of KV
date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); Tx1’s “Transaction Record” ------------------------------------ State: PENDING Tx2 (t2) Read (key1); -> x0 t0 < t2 < t1 だった場合は、t0 の値 (x0) を 読む。 key1 (t1): x1 (intent:Tx1) key1 (t0): x0
Read Tx は Timestamp を見て読み込む値を決める Range : Set of KV
date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); Tx1’s “Transaction Record” ------------------------------------ State: PENDING Tx2 (t2) Read (key1); -> x1 (intent:Tx1) t0 < t1 < t2 だった場合は、t1 の値 (x1) を 読む。(x1 は “write intent” なのでこの時 点で COMMIT / ABORT は不明) key1 (t1): x1 (intent:Tx1) key1 (t0): x0
Read した値が “write intent” だった場合 Range : Set of KV
date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); Tx1’s “Transaction Record” ------------------------------------ State: PENDING Read した値が “write intent” だった場合、 Tx Record の値を確認する。 key1 (t1): x1 (intent:Tx1) key1 (t0): x0 Tx2 (t2) Read (key1); -> x1 (intent:Tx1)
Read した値が “write intent” だった場合 (COMMIT) Range : Set of
KV date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); COMMIT; Tx1’s “Transaction Record” ------------------------------------ State: COMMITTED Tx Record の値が COMMITTED だった 場合は COMMIT 済みなので、そのまま x1 を Read する。 key1 (t1): x1 (intent:Tx1) key1 (t0): x0 Tx2 (t2) Read (key1); -> x1
Read した値が “write intent” だった場合 (ABORT) Range : Set of
KV date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); ABORT; Tx1’s “Transaction Record” ------------------------------------ State: ABORTED Tx Record の値が ABORTED だった場合 は Tx1 の更新が無かったことになるので、 x0 を Read する。 key1 (t1): x1 (intent:Tx1) key1 (t0): x0 Tx2 (t2) Read (key1); -> x0
Read した値が “write intent” だった場合 (PENDING) Range : Set of
KV date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); … ... Tx1’s “Transaction Record” ------------------------------------ State: PENDING Tx Record の値が PENDING だった場合 は状態 (COMMIT / ABORT) が確定して いないので、Tx1 の完了を待つ。 key1 (t1): x1 (intent:Tx1) key1 (t0): x0 Tx2 (t2) Read (key1); -> wait...
Read した値が “write intent” だった場合 (MISSING) Range : Set of
KV date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); 特定のタイミング (Tx 処理中) や Gateway Node の障害等に起因して、Tx Record が 存在しない場合もある。 key1 (t1): x1 (intent:Tx1) key1 (t0): x0 Tx2 (t2) Read (key1); -> ??? Tx1’s “Transaction Record” ------------------------------------ MISSING
Read した値が “write intent” だった場合 (MISSING) Range : Set of
KV date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); Tx Record が無い場合は “write intent” の Timestamp が “Transaction Liveness Threshold” の範囲内か否かをチェックす る。 key1 (t1): x1 (intent:Tx1) key1 (t0): x0 Tx2 (t2) Read (key1); -> ??? 無い...? Tx1’s “Transaction Record” ------------------------------------ MISSING
Read の動作 補足 (Transaction Liveness Threshold) \ ダッセンスルヨ /
補足 (Transaction Liveness Threshold) Node 1 Tx1 (t1) BEGIN; Write
(key1, x1); … Range : Set of KV date (512BM) Tx1’s “Transaction Record” ------------------------------------ State: PENDING key1 (t0): x0 key1 (t1): x1 (intent:Tx1) SQL を受け取った Gateway Node (いわゆるコーディネーター) が Tx を管理している。 Manage Create Write \ カンリ /
補足 (Transaction Liveness Threshold) Node 1 Tx1 (t1) BEGIN; Write
(key1, x1); … Range : Set of KV date (512BM) Tx1’s “Transaction Record” ------------------------------------ State: PENDING key1 (t0): x0 key1 (t1): x1 (intent:Tx1) Tx1 の途中で Node 障害が発生すると、その Node が管理していた Tx1 は ABORT することになるが、Range の中に Transaction Record が PENDING のまま残ってしまう。 ~
補足 (Transaction Liveness Threshold) Node 1 Range : Set of
KV date (512BM) Tx1’s “Transaction Record” ------------------------------------ State: PENDING key1 (t0): x0 key1 (t1): x1 (intent:Tx1) Range の中に Transaction Record が PENDING のまま残ってい ると、他の Tx2 が Tx1 の状態を判断できず、いつまでも Tx1 の状 態が確定 (COMMIT or ABORT) するのを待ってしまう。 Tx2 (t2) Read (key1); -> wait... Wait forever... Read \ マダー? / Node 2
補足 (Transaction Liveness Threshold) Node 1 TxnCoordSender Tx1 (t1) BEGIN;
Write (key1, x1); … Range : Set of KV date (512BM) Tx1’s “Transaction Record” ------------------------------------ State: PENDING key1 (t0): x0 key1 (t1): x1 (intent:Tx1) Transaction Record の残存を防ぐため Gateway Node 内の TxnCoordSender が Transaction Record に対して定期的に Heartbeat を実施している。多分デフォルト 1秒間隔。 https://github.com/cockroachdb/cockroach/blob/v20.2.5/pkg/base/constants.go#L27 Heartbeat \ イキテルヨ /
補足 (Transaction Liveness Threshold) Node 1 TxnCoordSender Tx1 (t1) BEGIN;
Write (key1, x1); … Range : Set of KV date (512BM) Tx1’s “Transaction Record” ------------------------------------ Tx1 is expired! Heartbeat が一定時間途絶えた場合、Tx (Node) が死んでいると判 断できる。この Tx の生死を判断する閾値が “Transaction Liveness Threshold”。多分デフォルト 5秒。 https://github.com/cockroachdb/cockroach/blob/v20.2.5/pkg/kv/kvserver/txnwait/queue.go#L46 ~ Heartbeat key1 (t0): x0 key1 (t1): x1 (intent:Tx1)
Node 1 TxnCoordSender Range : Set of KV date (512BM)
Tx1’s “Transaction Record” ------------------------------------ Tx1 is expired! key1 (t0): x0 key1 (t1): x1 (intent:Tx1) Tx2 (t2) Read (key1); -> x1 (intent:Tx1) 2. Check 1. Read \ イッタカ... / Heartbeat 補足 (Transaction Liveness Threshold) Tx1 を管理する Node が死んだ場合でも、他の Tx2 が write intent 読み込み Transaction Record を確認した際に、正しく「死んだ」と判 断できる。 Node 2
Node 1 TxnCoordSender Range : Set of KV date (512BM)
Tx1’s “Transaction Record” ------------------------------------ State: ABORTED key1 (t0): x0 key1 (t1): x1 (intent:Tx1) Tx2 (t2) Read (key1); -> x0 ABORT Read \ サラバダ... / Heartbeat 補足 (Transaction Liveness Threshold) Tx1 が「死んだ」と判断された場合、それを見つけた Tx2 を管理する Node 2 が Tx1 の Transaction Record の値を ABORTED に更新 し、Tx2 は 1つ前の値 x0 を Read する (t0 < t1 < t2 の場合)。 Node 2
Read の動作 補足 (Transaction Record が作成される条件) \ サラニダッセンスルヨ /
補足 (Transaction Record が作成される条件) Node 1 TxnCoordSender Tx1 (t1) BEGIN;
Write (key1, x1); … Range : Set of KV date (512BM) Tx1’s “Transaction Record” ------------------------------------ State: XXXXXX key1 (t0): x0 key1 (t1): x1 (intent:Tx1) 1. COMMIT が実行された時。(※詳細は Parallel Commits で) 2. TxnCoordSender が Heartbeat を実行した時。 3. Tx が強制的に ABORT された時。 Create \ ジョウケン /
補足 (Transaction Record が作成される条件1) Node 1 Tx1 (t1) BEGIN; Write
(key1, x1); COMMIT; Range : Set of KV date (512BM) Tx1’s “Transaction Record” ------------------------------------ State: STAGING 1. COMMIT が実行された時 (STAGING)。 ※何故 COMMIT のタイミングなのかについては後述する Parallel Commits 参照。 When it COMMITs \ コミット / key1 (t0): x0 key1 (t1): x1 (intent:Tx1)
補足 (Transaction Record が作成される条件2) Node 1 TxnCoordSender Tx1 (t1) BEGIN;
Write (key1, x1); … Range : Set of KV date (512BM) Tx1’s “Transaction Record” ------------------------------------ State: PENDING 2. TxnCoordSender が Heartbeat を実行した時。 ※Heartbeat は 1秒間隔なので、Tx 内の最初の Write から 1秒経つと Transaction Record が作成される。 When it Heartbeats \ イキテルヨ / key1 (t0): x0 key1 (t1): x1 (intent:Tx1)
Node 1 Tx1 (t1) BEGIN; Write (key1, x1); … Range
: Set of KV date (512BM) Tx1’s “Transaction Record” ------------------------------------ State: PENDING key1 (t0): x0 key1 (t1): x1 (intent:Tx1) 3. Tx1 が key1 への Write は実行したが Transaction Record は まだ作成していない状態で障害が発生すると、write intent は 存在するが対応する Transaction Record が無い、という状態 になる。 補足 (Transaction Record が作成される条件3) ~ Write Not yet...
Node 1 Range : Set of KV date (512BM) key1
(t1): x1 (intent:Tx1) Node 2 Tx2 (t2) Read (key1); -> x1 (intent:Tx1) 2. Check 1. Read \ ナイ / 3. 他の Tx2 が write intent 読み込んだ際に、対応する Transaction Record が存在しないことを検知する。 ※ただ し、この時点で Node 1 が死んでいるか否かは判断 できない (単純に Node 1 が処理途中の可能性が有る)。 補足 (Transaction Record が作成される条件3) key1 (t0): x0
Node 1 Range : Set of KV date (512BM) Node
2 Check Timestamp \ カクニン / 3. Transaction Record が無い場合は、読み込んだ write intent の Timestamp が Transaction Liveness Threshold 以内か否 かを確認する。 補足 (Transaction Record が作成される条件3) key1 (t1): x1 (intent:Tx1) Tx2 (t2) Read (key1); -> x1 (intent:Tx1) key1 (t0): x0
Node 1 Range : Set of KV date (512BM) Node
2 Treat as PENDING \ マツワ / 3. write intent の Timestamp t1 が Transaction Liveness Threshold 以内の場合 PENDING として扱い、待つ。 多分 (Current time - t1) < 5 の場合。 補足 (Transaction Record が作成される条件3) key1 (t1): x1 (intent:Tx1) Tx2 (t2) Read (key1); -> x1 (intent:Tx1) key1 (t0): x0
Node 1 Range : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: ABORTED key1 (t0): x0 key1 (t1): x1 (intent:Tx1) Node 2 Tx2 (t2) Read (key1); -> x0 ABORT Read \ シンダナ / 補足 (Transaction Record が作成される条件3) 3. write intent の Timestamp t1 が Transaction Liveness Threshold の範囲外 (古い) の場合、Tx1 を ABORT させるた めに Transaction Record を作成する。 多分 (Current time - t1) > 5 の場合。
Read の動作 つづき \ モドッテキタヨ /
Read した値が “write intent” だった場合 (MISSING) Range : Set of
KV date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); Tx1’s “Transaction Record” ------------------------------------ MISSING Tx Record が無い場合は “write intent” の Timestamp が “Transaction Liveness Threshold” の範囲内か否かをチェックす る。 key1 (t0): x0 key1 (t1): x1 (intent:Tx1) Tx2 (t2) Read (key1); -> ??? 無い...? 再掲
Read した値が “write intent” だった場合 (MISSING) Range : Set of
KV date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); “write intent” の Timestamp が “Transaction Liveness Threshold” 以内 の場合、PENDING として扱い、待つ。 key1 (t0): x0 key1 (t1): x1 (intent:Tx1) Tx2 (t2) Read (key1); -> wait... Tx1’s “Transaction Record” ------------------------------------ MISSING
Read した値が “write intent” だった場合 (MISSING) Range : Set of
KV date (512BM) Tx1 (t1) BEGIN; Write (key1, x1); ※Tx1 に「何らかの 問題が発生した」 判 断される。 key1 (t0): x0 key1 (t1): x1 (intent:Tx1) Tx2 (t2) Read (key1); -> x0 “write intent” の Timestamp が “Transaction Liveness Threshold” の範 囲外 (古い) の場合、ABORT として処理し x0 を Read する。 Tx1’s “Transaction Record” ------------------------------------ State: ABORTED
Parallel Commits \ パラレル /
Not Parallel Commits \ ムカシ / 初期の頃 (~ v2.0) の実装
v2.0 まで BEGIN; Write (key1, x1); Write (key2, y1); Write
(key3, z1); COMMIT; Timeline Tx 内の Statement は順番に 実行される。
v2.0 まで BEGIN; Write (key1, x1); Write (key2, y1); Write
(key3, z1); COMMIT; Timeline Write1 実行 Tx Record 作成 (PENDING) 最初の Statement と一緒に Tx Record も作成される。
v2.0 まで BEGIN; Write (key1, x1); Write (key2, y1); Write
(key3, z1); COMMIT; Timeline 各 Statement は並列実行でき ないので、順番に実行される。 Write2 実行
v2.0 まで BEGIN; Write (key1, x1); Write (key2, y1); Write
(key3, z1); COMMIT; Timeline 各 Statement は並列実行でき ないので、順番に実行される。 Write3 実行
v2.0 まで BEGIN; Write (key1, x1); Write (key2, y1); Write
(key3, z1); COMMIT; Timeline 最後に、Tx Record の値を PENDING -> COMMITTED に変える処理が実行される。 COMMIT 実行 Tx Record 更新 (COMMIT)
v2.0 まで BEGIN; Write (key1, x1); Write (key2, y1); Write
(key3, z1); COMMIT; Timeline Tx 内の Statement は順番に 実行されるので、処理時間は O(n) になる。 ※n = Num of Statements COMMIT (Tx Record の更新 処理) も入れると O(n+1) にな る。
Not Parallel Commits \ チョットマエ / ちょっと前 (v2.1 ~ v19.1)
までの実装
BEGIN; Write (key1, x1); Write (key2, y1); Write (key3, z1);
COMMIT; Timeline v2.1 で Pipelining という機能 が実装され、Tx 内の各 Statement は並列で実行され るようになった。 v2.1 ~ v19.1 まで (Pipelining)
v2.1 ~ v19.1 まで (Pipelining) BEGIN; Write (key1, x1); Write
(key2, y1); Write (key3, z1); COMMIT; Timeline Write1 実行 Tx Record 作成 (PENDING) Write2 実行 Write3 実 行 各 Statement を並列で実行。 ただし、全ての Statement の 書き込み完了が返ってくるまで Tx Record の値を COMMIT に更新できない。
v2.1 ~ v19.1 まで (Pipelining) BEGIN; Write (key1, x1); Write
(key2, y1); Write (key3, z1); COMMIT; Timeline 全ての Statement の書き込み 完了が返ってきたら、Tx を COMMIT する (Tx Record の 値を COMMITTED に更新す る)。 COMMIT 実行 Tx Record 更新 (COMMIT)
v2.1 ~ v19.1 まで (Pipelining) BEGIN; Write (key1, x1); Write
(key2, y1); Write (key3, z1); COMMIT; Timeline Pipelining により各 Statement を並列で実行でき るようになったので、理論上の 最短処理時間は O(2) になっ た。 ※この O(2) を O(1) にする のが Parallel Commits。
BEGIN; Write (key1, x1); Read (key1); Write (key3, z1); COMMIT;
Timeline なお、Statement 間に依存関 係が有る場合は、並列には実 行されず、ちゃんと順番に実行 される仕組みになっている。 v2.1 ~ v19.1 まで (Pipelining : Read-Your-Writes)
BEGIN; Write (key1, x1); Read (key1); Write (key3, z1); COMMIT;
Timeline Write1 と Write3 は別々の key に対する Write であり、依 存関係はないので並列実行。 v2.1 ~ v19.1 まで (Pipelining : Read-Your-Writes)
BEGIN; Write (key1, x1); Read (key1); Write (key3, z1); COMMIT;
Timeline Write1 と Write3 は別々の key に対する Write であり、依 存関係はないので並列実行。 Read は Write1 が更新した key1 の値を読む必要が有る (依存関係が有る) ので、 Write1 完了後に実行される。 v2.1 ~ v19.1 まで (Pipelining : Read-Your-Writes)
BEGIN; Write (key1, x1); Read (key1); Write (key3, z1); COMMIT;
Timeline Tx 内の Statement 間に依存 関係が有る場合は、必ずしも O(2) になるわけではない。 ※あくまで理論上の最短が O(2) になる感じ。 この “理論上の最短” を O(2) から O(1) にするのが Parallel Commits。 v2.1 ~ v19.1 まで (Pipelining : Read-Your-Writes)
Parallel Commits \ サイシン / 最新 (v19.2 ~) の実装
Parallel Commits (v19.2 ~) BEGIN; Write (key1, x1); Write (key2,
y1); Write (key3, z1); COMMIT; Timeline Parallel Commits は、 COMMIT の処理 (Tx Record の作成) も他の Statement と 並列で実行することで、処理時 間の理論上の最短を O(1) に するもの。 Tx 内で Write する key が全て 確定している必要があり、 Transaction Record は COMMIT のタイミングで作成 される。
Parallel Commits (v19.2 ~) BEGIN; Write (key1, x1); Write (key2,
y1); Write (key3, z1); COMMIT; Timeline 書き込み完了前 に COMMIT して 大丈夫か...?
Parallel Commits \ kwsk / COMMIT (Write) の動作
Parallel Commits (Write) Node 1 Range1 : Set of KV
date (512BM) key1 (t0): x0 Range2 : Set of KV date (512BM) key2 (t0): y0 \ スタート / Client key1, key2 を更新する。 BEGIN; Write (key1, x1); Write (key2, y2); COMMIT;
Node 1 Range1 : Set of KV date (512BM) Range2
: Set of KV date (512BM) \ リクエスト / Client BEGIN; Write (key1, x1); Write (key2, y2); COMMIT; Write のタイミングではまだ Tx Record は作成しない。 Parallel Commits (Write) key1 (t0): x0 key2 (t0): y0 key1 (t1): x1 (intent:Tx1) key2 (t1): y1 (intent:Tx1) Write Write
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 Range2 : Set of KV date (512BM) key1 (t1): x1 (intent:Tx1) key2 (t1): y1 (intent:Tx1) \ ヘイレツ / Parallel Write Client BEGIN; Write (key1, x1); Write (key2, y2); COMMIT; COMMIT のタイミングで Tx Record を作成する。この時、State には STAGING を設定 し、一緒に「更新対象の key の一覧 (key1, key2)」の情報を Tx Record 内に含める。 Parallel Commits (Write) key1 (t0): x0 key2 (t0): y0
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 Range2 : Set of KV date (512BM) \ カンリョウ / Write OK Client BEGIN; Write (key1, x1); Write (key2, y2); COMMIT; Write OK Write OK 各 Write 及び Tx Record の作成が完了したことを確認する。 Parallel Commits (Write) key1 (t1): x1 (intent:Tx1) key2 (t1): y1 (intent:Tx1) key1 (t0): x0 key2 (t0): y0
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 Range2 : Set of KV date (512BM) \ オワッタヨ / Client BEGIN; Write (key1, x1); Write (key2, y2); COMMIT; COMMIT Complete 各 Write 及び Tx Record の作成が完了したことを確認した時点で、Client に COMMIT 完了を返す。 Parallel Commits (Write) key1 (t1): x1 (intent:Tx1) key2 (t1): y1 (intent:Tx1) key1 (t0): x0 key2 (t0): y0
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: COMMITTED Keys: key1, key2 Range2 : Set of KV date (512BM) \ ヒドウキ / Client Client に COMMIT 完了を返した後に「非同期」かつ並列で Tx Record の更新 (COMMITTED に更新) と write intent の削除を実施する。他の Tx が Read する可能 性が有るため、非同期ではあるが「できるだけ速く」更新する。 State is COMMITTED Resolve intent Resolve intent Parallel Commits (Write) key1 (t1): x1 (intent:Tx1) key2 (t1): y1 (intent:Tx1) key1 (t0): x0 key2 (t0): y0
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: COMMITTED Keys: key1, key2 Range2 : Set of KV date (512BM) \ オワリ / Client write intent の処理完了後に、Tx Record は削除され更新した値の状態が確定 (COMMIT 済みに確定) する。 Parallel Commits (Write) key1 (t1): x1 key2 (t1): y1 key1 (t0): x0 key2 (t0): y0
Parallel Commits \ モット kwsk / Read の動作
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 key1 (t1): x1 (intent:Tx1) Range2 : Set of KV date (512BM) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Node 2 \ リードスルヨ / \ ヘイレツ / Node 1 が Tx1 で key1, key2 を更新し、Node 1 の処理途中 (Tx Record の State が STAGING のタイミング) で Node 2 が Tx2 で key1 を Read する。 Parallel Write Parallel Commits (Read) Tx2 (t2) ※t0 < t1 < t2 Read (key1);
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ リード / \ ショリチュウ... / Node 2 が Tx2 で key1 を Read すると、write intent (COMMIT or ABORT が不定) に なっている。また、Node 1 は処理中であるため Tx1 の Tx Record に定期的に Heartbeat が送信されている。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); Read key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Parallel Commits (Read) Heartbeat Write OK Write OK Write OK
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ カクニン / \ ショリチュウ... / Node 2 (Tx2) が key1 の状態を確認するために Tx Record を参照すると、State が STAGING であることがわかる。また、Node 1 からの Heartbeat も継続しているため Node 1 (Tx1) は生きている (処理中) であることも確認できる。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); Check key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Parallel Commits (Read) Heartbeat Write OK Write OK Write OK
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ マツワ / \ イキテルヨ / Tx1 を管理する Node1 からの Heartbeat が続いているため、Node 1 (Tx1) は生きて いる (処理中) と判断し、Node 2 (Tx2) は Tx1 が完了するまで待つ。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); -> wait... Heartbeat key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t0): y0 Parallel Commits (Read) key2 (t1): y1 (intent:Tx1) Write OK Write OK Write OK
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ タイキ / \ ショリチュウ... / Tx1 は処理中... (Heartbeat は継続) Tx2 は待機... (Tx1 の完了待ち) Tx2 (t2) ※t0 < t1 < t2 Read (key1); -> wait... key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Parallel Commits (Read) Heartbeat Write OK Write OK Write OK
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: COMMITTED Range2 : Set of KV date (512BM) Node 2 \ オワッタッポイ / \ オワッタヨ / Tx1 の処理が完了すると、Tx2 (Node 2) は Tx1 にて更新された値 (x1) を Read する。 後述する Status Recovery Procedure は非常に重い処理であるため、Tx1 を管理する Node 1 が生きている場合は、Tx1 が COMMIT されるまで待つ。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); -> x1 Read State is COMMITTED key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Parallel Commits (Read)
Parallel Commits \ サラニ kwsk / 障害発生時の Read (Status Recovery
Procedure)
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 key1 (t1): x1 (intent:Tx1) Range2 : Set of KV date (512BM) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Node 2 \ リードスルヨ / \ ヘイレツ / Case 1 : Node 1 が Tx1 で key1, key2 を更新し、Node 1 の処理途中 (Tx Record の State が STAGING のタイミング) で Node 2 が Tx2 で key1 を Read する。また、全て の write intent 書き込み完了後に Node 1 で障害が発生する。 Parallel Write Status Recovery Procedure1 Tx2 (t2) ※t0 < t1 < t2 Read (key1);
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 key1 (t1): x1 (intent:Tx1) Range2 : Set of KV date (512BM) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Node 2 \ リードスルヨ / 全ての write intent 書き込み完了後に Node 1 で障害が発生する。また、Node 1 の障 害に起因して Tx1 の Tx Record への Heartbeat が途切れる。 Status Recovery Procedure1 ~ Heartbeat Tx2 (t2) ※t0 < t1 < t2 Read (key1); Write OK Write OK Write OK
Node 1 Range1 : Set of KV date (512BM) Tx1’s
“Transaction Record” ------------------------------------ State: STAGING (expired) Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ リード / Node 2 が Tx2 で key1 を Read すると、write intent (COMMIT or ABORT が不定) に なっている。また、Node 1 に障害が発生し Heartbeat が途切れたため、Tx1 は expired となる。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); Read key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Status Recovery Procedure1 Heartbeat
Range1 : Set of KV date (512BM) Tx1’s “Transaction Record”
------------------------------------ State: STAGING (expired) Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ カクニン / Node 2 (Tx2) が key1 の状態を確認するために Tx Record を参照すると、State が STAGING であることがわかる。また、Node 1 からの Heartbeat が途切れているため Node 1 (Tx1) が死んだと判断する。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); Check (Tx1 is expired) key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Status Recovery Procedure1 Node 1
Range1 : Set of KV date (512BM) Tx1’s “Transaction Record”
------------------------------------ State: STAGING (expired) Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ サラニカクニン / Tx Record に含まれている更新対象の key の情報を基に、Tx1 の更新対象である key2 の write intent が作成されているかを確認する。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); Check key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Status Recovery Procedure1 Node 1
Range1 : Set of KV date (512BM) Tx1’s “Transaction Record”
------------------------------------ State: STAGING (expired) Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ コミットダナ / Tx Record (STAGING) 内に含まれている key の write intent が全て書き込まれている 場合、implicitly committed (暗黙的な COMMIT である) として扱い、Tx2 (Node 2) は write intent の値を Read する。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); -> x1 Read key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Status Recovery Procedure1 Node 1
Range1 : Set of KV date (512BM) Tx1’s “Transaction Record”
------------------------------------ State: COMMITTED Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ コミットダヨ / 他の Tx が Status Recovery Procedure を実行しなくても済むように、Node 2 が Tx1 の Tx Record の State を COMMITTED に更新する。 key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Status Recovery Procedure1 Node 1 State is COMMITTED
Status Recovery Procedure2 Node 1 Range1 : Set of KV
date (512BM) Tx1’s “Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ リードスルヨ / \ ヘイレツ / Case 2 : Node 1 が Tx1 で key1, key2 を更新し、Node 1 の処理途中 (Tx Record の State が STAGING のタイミング) で Node 2 が Tx2 で key1 を Read する。また、key2 の write intent 作成前に Node 1 で障害が発生する。 Parallel Write key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Tx2 (t2) ※t0 < t1 < t2 Read (key1);
Status Recovery Procedure2 Node 1 Range1 : Set of KV
date (512BM) Tx1’s “Transaction Record” ------------------------------------ State: STAGING Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ リードスルヨ / 一部の write intent 書き込み完了前に Node 1 で障害が発生する。また、Node 1 の 障害に起因して Tx1 の Tx Record への Heartbeat が途切れる。 key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Tx2 (t2) ※t0 < t1 < t2 Read (key1); ~ Heartbeat Write OK Write OK
Range1 : Set of KV date (512BM) Tx1’s “Transaction Record”
------------------------------------ State: STAGING (expired) Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ リード / Node 2 が Tx2 で key1 を Read すると、write intent (COMMIT or ABORT が不定) に なっている。また、Node 1 に障害が発生し Heartbeat が途切れたため、Tx1 は expired となる。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); Read key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Status Recovery Procedure2 Node 1 Heartbeat
Range1 : Set of KV date (512BM) Tx1’s “Transaction Record”
------------------------------------ State: STAGING (expired) Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ カクニン / Node 2 (Tx2) が key1 の状態を確認するために Tx Record を参照すると、State が STAGING であることがわかる。また、Node 1 からの Heartbeat が途切れているため Node 1 (Tx1) が死んだと判断する。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); Check (Tx1 is expired) key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t0): y0 Node 1 Status Recovery Procedure2 key2 (t1): y1 (intent:Tx1)
Range1 : Set of KV date (512BM) Tx1’s “Transaction Record”
------------------------------------ State: STAGING (expired) Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ ナイ / Tx Record に含まれている更新対象の key の情報を基に、Tx1 の更新対象である key2 の write intent が作成されているかを確認する。しかし、key2 の write intent は存 在していないことが確認される。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); Check key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t0): y0 Node 1 Status Recovery Procedure2 key2 (t1): y1 (intent:Tx1)
Range1 : Set of KV date (512BM) Tx1’s “Transaction Record”
------------------------------------ State: STAGING (expired) Keys: key1, key2 Range2 : Set of KV date (512BM) Node 2 \ アボートダナ / Tx1 は ABORT したものとして扱い、Tx2 (Node 2) は x0 を Read する。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); -> x0 Read Node 1 key1 (t0): x0 key2 (t0): y0 Status Recovery Procedure2 key1 (t1): x1 (intent:Tx1) key2 (t1): y1 (intent:Tx1)
Range1 : Set of KV date (512BM) Tx1’s “Transaction Record”
------------------------------------ State: ABORTED Range2 : Set of KV date (512BM) Node 2 \ シンダッポイ / 他の Tx が Status Recovery Procedure を実行しなくても済むように、Node 2 が Tx1 の Tx Record の State を ABORTED に更新する。 Tx2 (t2) ※t0 < t1 < t2 Read (key1); Node 1 State is ABORTED key1 (t1): x1 (intent:Tx1) key1 (t0): x0 key2 (t1): y1 (intent:Tx1) key2 (t0): y0 Status Recovery Procedure2
Parallel Commits (v19.2 ~) BEGIN; Write (key1, x1); Write (key2,
y1); Write (key3, z1); COMMIT; Timeline COMMIT 時に作成する Tx Record (STAGING) の中に更 新対象の key の一覧を含める ことで、処理時間を (最短で) O(1) にしつつ、他の Tx の Read も正しく処理できるように している。
Parallel Commits (v19.2 ~) BEGIN; Write (key1, x1); Write (key2,
y1); Write (key3, z1); COMMIT; Update Tx Record & Remove write intent Timeline 非同期 (async) Client に COMMIT 完了を返し た後に、非同期で Tx Record の値を (できるだけ速く) COMMITTED に更新し、write intent を削除する。 Status Recovery Procedure は「重い処理」であり、通常 (Gateway Node が生きてる場 合) は COMMIT 完了を待つ が、障害発生時は他の Node で適切に対処できる。
話せなかったこと \ ホカニモイロイロアルヨ /
話せなかったこと • Tx の競合を解決したり一貫性を確保したりするために、他にもいろ んな機能があります。 ◦ Max clock offset enforcement
◦ Timestamp cache ◦ Concurrency manager ◦ Lock table ◦ TxnWaitQueue ◦ Read refreshing • 興味がある人はドキュメントを是非読んでみてください。 ◦ https://www.cockroachlabs.com/docs/stable/architecture/transaction-layer.html
まとめ \ マトメ /
• Transaction Record を使って分散 Tx を実現している。 • Parallel Commits で性能向上を図っている。
• 今回紹介した機能以外の機能も使って Tx の競合を解決した り一貫性を確保したりしている。 まとめ
参考情報 • https://www.cockroachlabs.com/docs/stable/architecture/transaction-layer.html • https://www.cockroachlabs.com/docs/v20.2/architecture/life-of-a-distributed-transaction. html • https://www.cockroachlabs.com/blog/parallel-commits/ • https://www.cockroachlabs.com/blog/transaction-pipelining/
• https://github.com/cockroachdb/cockroach/blob/master/docs/tech-notes/encoding.md
おまけ \ オマケ /
おまけ (実際の KV データを覗いてみる) root@cockroach-1:26257/defaultdb> select * from test; id
| c1 | c2 | c3 -----+----------+-----------+---------- 1 | foo | bar | baz 2 | hoge | fuga | piyo 3 | (・ω・´) | (´・ω・`) | m9(^Д^) (3 rows) root@cockroach-1:26257/defaultdb> update test set c1 = 'foobar' where id = 1; UPDATE 1 Time: 15ms total (execution 15ms / network 0ms) root@cockroach-1:26257/defaultdb> root@cockroach-1:26257/defaultdb> select * from test; id | c1 | c2 | c3 -----+----------+-----------+---------- 1 | foobar | bar | baz 2 | hoge | fuga | piyo 3 | (・ω・´) | (´・ω・`) | m9(^Д^) (3 rows) Step 1. テスト用の TABLE Step 2. id = 1 の record を更新 (foo -> foobar)
おまけ (実際の KV データを覗いてみる) root@cockroach-1:26257/defaultdb> begin; BEGIN Time: 0ms total
(execution 0ms / network 0ms) root@cockroach-1:26257/defaultdb OPEN> root@cockroach-1:26257/defaultdb OPEN> update test set c1 = 'foobaz' where id = 1; UPDATE 1 Time: 2ms total (execution 2ms / network 0ms) root@cockroach-1:26257/defaultdb OPEN> root@cockroach-1:26257/defaultdb OPEN> select * from test; id | c1 | c2 | c3 -----+----------+-----------+---------- 1 | foobaz | bar | baz 2 | hoge | fuga | piyo 3 | (・ω・´) | (´・ω・`) | m9(^Д^) (3 rows) Time: 1ms total (execution 1ms / network 0ms) root@cockroach-1:26257/defaultdb OPEN> Step 3. Tx 内で id = 1 を更新 (foobar -> foobaz) Tx は COMMIT せず OPEN のまま
おまけ (実際の KV データを覗いてみる) Byte 形式 Transaction Record key: 0x016B12BE8989000174786E2D79AE144DD9264CF2AC519A81DE7CFFBE00
value: 0x12040800100018002000280032480F1CFC56030A350A1079AE144DD9264CF2AC519A81DE7CFFBE1A03BE89892A0C08B0B4B2C7E5 A0FEB416100230E8AA594A0A08819EE0A1EA9FFEB4162A0A08A0AFB9B7BBA7FEB416 Write Intent key: 0xBE89898800 value: 0x0A370A1079AE144DD9264CF2AC519A81DE7CFFBE1A03BE89892A0C08B0B4B2C7E5A0FEB416100230E8AA5938014A0A08819EE0A1 EA9FFEB416120C08B0B4B2C7E5A0FEB41610021800200C2817 A) “Step 3.” の Tx (OPEN 中の Tx) の Transaction Record B) “Step 3.” の Tx (OPEN 中の Tx) 内で UPDATE した id = 1 の record の Write Intent
おまけ (実際の KV データを覗いてみる) Byte 形式 Rows [id = 1
(c1 = foobaz)] key : 0xBE898988001669F90658EC9A30000000020D value : 0x76AE0A090A2606666F6F62617A1603626172160362617A [id = 1 (c1 = foobar)] key : 0xBE898988001669F8F845F4657A09 value : 0xCC6DEB4C0A2606666F6F6261721603626172160362617A [id = 1 (c1 = foo)] key : 0xBE898988001669F8AF87FB1ABD09 value : 0x4CF924910A2603666F6F1603626172160362617A [id = 2] key : 0xBE898A88001669F8B3E074FA7609 value : 0x65EEC34A0A2604686F676516046675676116047069796F [id = 3] key : 0xBE898B88001669F8E34AC654C609 value : 0xC11CE7CF0A260C28E383BBCF89E383BBC2B429160D28C2B4E383BBCF89E383BB602916086D39285ED0945E29 C) “Step 3.” の時点での TABLE ”test” の各 record
おまけ (実際の KV データを覗いてみる) Decode Transaction Record key: /Local/Range/Table/54/1/1/Transaction/"79ae144d-d926-4cf2-ac51-9a81de7cffbe" value:
meta={id=79ae144d key=/Table/54/1/1 pri=0.06815679 epo=0 ts=1615095747.046054448,2 min=1615095713.949552385,0 seq=0} lock=true stat=PENDING rts=0,0 wto=false max=0,0 Write Intent key: /Table/54/1/1/0 value: 1615095747.046054448,2 {Txn:id=79ae144d key=/Table/54/1/1 pri=0.06815679 epo=0 ts=1615095747.046054448,2 min=1615095713.949552385,0 seq=1 Timestamp:1615095747.046054448,2 Deleted:false KeyBytes:12 ValBytes:23 RawBytes:[] IntentHistory:[] MergeTimestamp:<nil>} A) “Step 3.” の Tx (OPEN 中の Tx) の Transaction Record B) “Step 3.” の Tx (OPEN 中の Tx) 内で UPDATE した id = 1 の record の Write Intent
おまけ (実際の KV データを覗いてみる) Decode Rows [id = 1 (c1
= foobaz)] key : /Table/54/1/1/0/1615095747.046054448,2 value : "v\xae\n\t\n&\x06foobaz\x16\x03bar\x16\x03baz" [id = 1 (c1 = foobar)] key : /Table/54/1/1/0/1615095686.598255994,0 value : "\xccm\xebL\n&\x06foobar\x16\x03bar\x16\x03baz" [id = 1 (c1 = foo)] key : /Table/54/1/1/0/1615095374.173379261,0 value : "L\xf9$\x91\n&\x03foo\x16\x03bar\x16\x03baz" [id = 2] key : /Table/54/1/2/0/1615095392.837630582,0 value : "e\xee\xc3J\n&\x04hoge\x16\x04fuga\x16\x04piyo" [id = 3] key : /Table/54/1/3/0/1615095596.484809926,0 value : "\xc1\x1c\xe7\xcf\n&\f( ・ω・´)\x16\r(´・ω・`)\x16\bm9(^Д^)" C) “Step 3.” の時点での TABLE ”test” の各 record
Thank you!