Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Resolving Tx Conflicts in CockroachDB

Resolving Tx Conflicts in CockroachDB

kota2and3kan

April 15, 2021
Tweet

More Decks by kota2and3kan

Other Decks in Technology

Transcript

  1. • Name: Takanori Yokoyama ◦ @kota2and3kan (Twitter, GitHub) • Job:

    Technical Support • Like: DB ◦ PostgreSQL, CockroachDB • Dislike: Real Cockroach @kota2and3kan こたつ&&みかん Who am I.
  2. 目次 • Node 間の時刻差 (clock offset) について • Timestamp (HLC)

    を使った Tx の動作 • Read Refresh • Timestamp Cache • Tx Conflicts 時の動作まとめ • No Stale Read • 話せなかったこと • まとめ
  3. Max clock offset enforcement \ t1 / • CockroachDB では

    MVCC や Tx の Isolation のために Timestamp (Hybrid Logical Clocks) を使っている。 • そのため、”適度なレベル (moderate levels)” での時刻同期が必要である。 ◦ Spanner の TrueTime API ようなレベルでの時刻同 期は不要。 • 公式では NTP 等を使って各 Node の時刻を 同期させることが推奨されている。 ◦ https://www.cockroachlabs.com/docs/stable/frequently-asked-questions. html#since-cockroachdb-is-inspired-by-spanner-does-it-require-atomic-c locks-to-synchronize-time \ t1 / \ t1 / \ t1 / \ t1 /
  4. Max clock offset enforcement • クラスタ内の各 Node では、常に (定期的に) “他の

    Node との時刻差 (clock offset)” を確認 している。 • クラスタ内で許容される clock offset の最大値 (max offset) は、Node 起動時に指定するオプ ション “--max-offset” で指定できる (デフォルト 500ms)。 • 実際に許容される clock offset は、max offset の 80% (デフォルト 400ms) まで。 \ t1 + 100ms / \ t1 - 50ms / \ t1 + 150ms / \ t1 - 50ms / \ t1 + 50ms /
  5. Max clock offset enforcement • ある Node の時刻が、クラスタ内の過半数の Node と比較して

    “max offset の 80% (デフォ ルト 400ms) 以上” ずれていることを...... \ t1 - 400ms / \ t1 / \ t1 - 200ms / \ t1 / \ t1 /
  6. Max clock offset enforcement \ ズレテル...? / \ t1 /

    \ t1 - 200ms / \ t1 / \ t1 / • 検知すると......
  7. Max clock offset enforcement \ t1 / \ t1 -

    200ms / \ t1 / \ t1 / • 自ら命を断つ。 • クラスタ内の過半数の Node と比較し、max offset * 0.8 以上の時刻のずれを検知した Node は PANIC する。 ◦ https://github.com/cockroachdb/cockroach/blob/v21.1.0-beta.2/pkg/rpc/c lock_offset.go#L242 • このようにして、クラスタ内で発生する clock offset が一定の範囲に留まるようにしている。 ~
  8. Max clock offset enforcement \ !? / \ !? /

    \ !? / \ t1 + 2000ms ! / • ただし、何らかの理由で Node の時刻が突然 大きくずれてしまうと、依存関係のある Tx 間で single-key linearizability が保証できなくなる可 能性があるので注意が必要。 • max offset の範囲内では single-key linearizability が保証される、らしい。 • 時刻が突然大きくずれる一例として、VM が他 の物理 Node 上に移動した場合等が考えられ るらしい。 ◦ https://www.cockroachlabs.com/docs/v20.2/operational-faqs#what-happ ens-when-node-clocks-are-not-properly-synchronized \ !? /
  9. k1 ~ k10 Tx と KV の Timestamp • CockroachDB

    はいわゆるシェアー ドナッシング方式 (ただし、各データ の Replica は分散して保持) なの で、各 Node が担当するデータ (key) の範囲が決まっている。 • データの塊 (512MB で分割) を Range と呼び、3つの Replica が存 在する。 • 3つの Replica の内、Read / Write を処理する 1つの Range を Leaseholder と呼ぶ。 k31 ~ k40 k41 ~ k50 k11 ~ k20 k21 ~ k30 ※Replica は図から省略※ ※lease のみ図中に記載※
  10. k1 ~ k10 • クエリを受け取った Gateway Node (Coordinator) は、自分が担当する 範囲の

    key (自分自信が Leaseholder) であれば自分で処理 する。 k31 ~ k40 k41 ~ k50 k11 ~ k20 k21 ~ k30 Client Tx1 : Read (k31), Write (k33, v3) k31=v1, Write OK Tx と KV の Timestamp ※Replica は図から省略※ ※lease のみ図中に記載※
  11. k1 ~ k10 • この時、Tx1 の Timestamp は Gateway Node

    の Timestamp であ る t4 になる。 • また、書き込まれた KV データ (k33=v3) に割り当てられる Timestamp は、Tx1 の Timestamp である t4 になる。 k31 ~ k40 k41 ~ k50 k11 ~ k20 k21 ~ k30 Client Tx と KV の Timestamp \ t4 / \ t5 / \ t3 / \ t1 / \ t2 / Tx1 : Read (k31), Write (k33, v3) k31=v1, Write OK ※Replica は図から省略※ ※lease のみ図中に記載※
  12. k1 ~ k10 • つまり、他の Node から見ると “k33 の最新の値 v3

    は時刻 t4 で書き込 まれた” というふうに見える。 k31 ~ k40 k41 ~ k50 k11 ~ k20 k21 ~ k30 Client Tx と KV の Timestamp \ t4 / \ k33=v3 at t4 / \ k33=v3 at t4 / \ k33=v3 at t4 / \ k33=v3 at t4 / Tx1 : Read (k31), Write (k33, v3) k31=v1, Write OK ※Replica は図から省略※ ※lease のみ図中に記載※
  13. Tx2 : Write (k45, v7) Write OK k15=v5, Write OK

    Tx2 : Read (k15), Write (k45, v7) k1 ~ k10 • クエリを受け取った Gateway Node (Coordinator) が対象の key の担当 ではない場合、当該 key を担当する Node (Leaseholder) にクエリをルー ティングする。 k31 ~ k40 k41 ~ k50 k11 ~ k20 k21 ~ k30 Client Tx と KV の Timestamp Tx2 : Read (k15) k15=v5 ※Replica は図から省略※ ※lease のみ図中に記載※
  14. Tx2 : Write (k45, v7) Write OK k15=v5, Write OK

    Tx2 : Read (k15), Write (k45, v7) k1 ~ k10 k31 ~ k40 k41 ~ k50 k11 ~ k20 k21 ~ k30 Client Tx と KV の Timestamp Tx2 : Read (k15) k15=v5 • この時、Tx2 の Timestamp は Gateway Node の Timestamp であ る t4 になる。 \ t4 / \ t5 / \ t3 / \ t1 / \ t2 / ※Replica は図から省略※ ※lease のみ図中に記載※
  15. Tx2 : Write (k45, v7) Write OK k15=v5, Write OK

    Tx2 : Read (k15), Write (k45, v7) k1 ~ k10 k31 ~ k40 k41 ~ k50 k11 ~ k20 k21 ~ k30 Client Tx と KV の Timestamp Tx2 : Read (k15) at t4 k15=v5 • Read (k15) は t4 の時点での k15 の最新の値を Read する。 • Write された KV (k45=v7) の Timestamp は、Tx2 の Timestamp である t4 になる。 \ t4 / \ t5 / \ t3 / \ t1 / \ t2 / ※Replica は図から省略※ ※lease のみ図中に記載※
  16. Tx2 : Write (k45, v7) Write OK k15=v5, Write OK

    Tx2 : Read (k15), Write (k45, v7) k1 ~ k10 k31 ~ k40 k41 ~ k50 k11 ~ k20 k21 ~ k30 Client Tx と KV の Timestamp Tx2 : Read (k15) at t4 k15=v5 \ t4 / \ k45=v7 at t4 / \ k45=v7 at t4 / \ k45=v7 at t4 / \ k45=v7 at t4 / • つまり、他の Node から見ると “k45 の最新の値 v7 は時刻 t4 で書き込 まれた” というふうに見える。 ※Replica は図から省略※ ※lease のみ図中に記載※
  17. \ Read (k1) at t4 / \ Write (k1) at

    t5 / \ Read (k1) at t3 / \ オチツケ / \ Write (k1) at t2 / • CockroachDB では全ての Node が Gateway Node (Coordinator) にな れる。 • そのため、各 Node の Timestamp を基にした複数の Tx が同時に実行 される。 • この時、Node 間に clock offset が あると、Timestamp を基にした Tx や KV の 順序が、実際の (Real Time の) Tx の実行順番と異なって しまう可能性がある。 k1 ~ k10 k31 ~ k40 k41 ~ k50 k11 ~ k20 k21 ~ k30 Tx と KV の Timestamp ※Replica は図から省略※ ※lease のみ図中に記載※
  18. Timestamp (HLC) を使った Read (Clock Offset 無し) Tx1 (key1 への

    Write) と Tx2 (key1 を Read) の 2つが、Tx1 -> Tx2 の順番で実行され る。Tx1 / Tx2 で Write / Read する key1 の Leaseholder は Node3。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) < ライト t0 t Tx1’s Gateway Node (Node1) < リード t0 t Tx2’s Gateway Node (Node2) < リースホルダー t0 t key1’s Leaseholder (Node3) x0
  19. Tx1 を受け取った Node1 (Tx1 の Gateway Node) は、ローカルの Timestamp を基

    に、Tx1 に t1 を設定する。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) < Tx1.ts = t1 t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t1 Timestamp (HLC) を使った Read (Clock Offset 無し)
  20. t1 Node1 は key1 の Leaseholder である Node3 に Write

    を送る。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) < ヨロシク t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) < OK t0 t key1’s Leaseholder (Node3) x0 Timestamp (HLC) を使った Read (Clock Offset 無し)
  21. t1 Node3 (Leaseholder) は x1 を t1 で Write し、Node1

    (Gateway Node) にレスポンスを 返す。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) < サンクス t0 t Tx1’s Gateway Node (Node1) < ヒマダナ... t0 t Tx2’s Gateway Node (Node2) < カイタヨ t0 t key1’s Leaseholder (Node3) x0 x1 t1 Timestamp (HLC) を使った Read (Clock Offset 無し)
  22. t1 Node1 (Gateway Node) は、Client に “Write OK” を返す。 Tx1:

    Write (key1, x1) Tx’s Real Time Order (Clients) < Write OK t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) x0 x1 t1 t0 t key1’s Leaseholder (Node3) Timestamp (HLC) を使った Read (Clock Offset 無し)
  23. t1 Tx1 完了後に、Tx2 (Read) が実行される。 Tx1: Write (key1, x1) Tx2:

    Read (key1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) < デバンカナ? t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 x1 t1 Timestamp (HLC) を使った Read (Clock Offset 無し)
  24. t1 Tx2 を受け取った Node2 (Tx2 の Gateway Node) は、ローカルの Timestamp

    を基 に、Tx2 に t2 を設定する。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) < Tx2.ts = t2 t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 x1 t1 t2 Timestamp (HLC) を使った Read (Clock Offset 無し)
  25. t1 Node2 は key1 の Leaseholder である Node3 に Read

    を送る。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) < ヨロシク t0 t Tx2’s Gateway Node (Node2) < OK t0 t key1’s Leaseholder (Node3) x0 x1 t1 t2 Timestamp (HLC) を使った Read (Clock Offset 無し)
  26. t1 Node3 (Leaseholder) は、t2 の時点での key1 の最新の値である x1 を Node2

    (Gateway Node) に返す。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) < ヒマデスワ... t0 t Tx1’s Gateway Node (Node1) < サンクス t0 t Tx2’s Gateway Node (Node2) < x1 ダヨ t0 t key1’s Leaseholder (Node3) x0 x1 t1 t2 t2 x1 Timestamp (HLC) を使った Read (Clock Offset 無し)
  27. t1 Node2 (Gateway Node) は、Client に “key1 = x1” を返す。

    Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) < key1 = x1 t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 x1 t1 t2 t2 t0 t Tx1’s Gateway Node (Node1) x1 Timestamp (HLC) を使った Read (Clock Offset 無し)
  28. t1 Tx1 -> Tx2 の順番で Tx が実行された時、Node 間に Clock Offset

    がなければ、Tx2 は正しく Tx1 で Write された値 (最新の値) を Read できる。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) < ライトシタヨ t0 t Tx1’s Gateway Node (Node1) < リードシタヨ t0 t Tx2’s Gateway Node (Node2) < イロイロシタヨ t0 t key1’s Leaseholder (Node3) x0 x1 t1 t2 t2 x1 Timestamp (HLC) を使った Read (Clock Offset 無し)
  29. Clock Offset に起因して発生する問題 先程と同じように Tx1 と Tx2 が、Tx1 -> Tx2

    の順番で実行される。Tx1 / Tx2 で Write / Read する key1 の Leaseholder は Node3。また、Node1 の時刻がずれており、 Cluster 内に Clock Offset がある。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) < ズレテル t0 t Tx1’s Gateway Node (Node1) < リード t0 t Tx2’s Gateway Node (Node2) < リースホルダー t0 t key1’s Leaseholder (Node3) x0 Clock Offset
  30. Tx1 を受け取った Node1 (Tx1 の Gateway Node) は、ローカルの Timestamp を基に

    (自分自信のずれた時刻を基に)、Tx1 に t3 (t2 < t3) を設定する。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) < Tx1.ts = t3 t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Clock Offset に起因して発生する問題
  31. Node1 は key1 の Leaseholder である Node3 に Write を送る。この時、Tx1

    の Timestamp t3 を基に Node3 で key1 の Write が実行される。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) < ヨロシク t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) < OK t0 t key1’s Leaseholder (Node3) x0 t3 t3 Clock Offset Clock Offset に起因して発生する問題
  32. x1 t3 Node3 (Leaseholder) は x1 を t3 で Write

    し、Node1 (Gateway Node) にレスポンスを 返す。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) < サンクス t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) < カイタヨ t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Clock Offset に起因して発生する問題
  33. x1 t3 Node1 (Gateway Node) は、Client に “Write OK” を返す。

    Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) < Write OK t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Clock Offset に起因して発生する問題
  34. x1 t3 Tx1 完了後に、Tx2 (Read) が実行される。この時、Tx2 には “Tx1 が Write

    した値 (key1 = x1) を Read すること” (一貫性があること) が期待されるが... Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Clock Offset に起因して発生する問題
  35. x1 t3 Tx2 を受け取った Node2 (Tx2 の Gateway Node) は、ローカルの

    Timestamp を基 に、Tx2 に t2 (t2 < t3) を設定する。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 t2 < Tx2.ts = t2 Clock Offset Clock Offset に起因して発生する問題
  36. x1 t3 Node2 は key1 の Leaseholder である Node3 に

    Read を送る。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 t2 < ヨロシク t2 < OK Clock Offset Clock Offset に起因して発生する問題
  37. x1 t3 しかし、Node3 (Leaseholder) には t3 (t2 < t3) で

    Write された x1 と、t0 (t0 < t2) で Write された x0 がある。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 t2 t2 < チョットマテ... Clock Offset Clock Offset に起因して発生する問題
  38. x1 t3 Tx1 -> Tx2 の順番で実行されているため、Tx1 で Write された key1

    = x1 を返すことが 期待されるが、単純に Timestamp の値を比較すると t0 < t2 < t3 であるため、t0 で Write された x0 (古い値) を返すことになる。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 t2 t2 < ドッチダ...? Clock Offset Clock Offset に起因して発生する問題
  39. x1 t3 Node3 (Leaseholder) には、Tx2 の Timestamp (t2) の情報と key1

    = x1 (t3) の情報し か見えず、Tx の実行順序の情報 (Tx1 -> Tx2) を知り得ることはできないので、x0 (t0) と x1 (t3) のどちらの値を返すべきなのか判断できない。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 t2 t2 < ナンモワカラン... < マダー? Clock Offset Clock Offset に起因して発生する問題
  40. x1 t3 この時、x0 (t0) の値を返してしまうと、先に実行された Tx1 で Write された最新の値 x1

    ではなく、古い値を Read してしまうことになり、Tx 間での一貫性がなくなってしまう。 Tx1: Write (key1, x1) Tx2: Read (key1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 t2 t2 < タブン x0… < ホントニ? Clock Offset Clock Offset に起因して発生する問題
  41. 用語概要 (Uncertainty Intervals) • Tx には Gateway Node の Timestamp

    が設定される。 • この時、Tx には 2つの Timestamp が設定される。 ◦ commit_ts ▪ Gateway Node の Local HLC を基にした仮の Timestamp。 ◦ commit_ts + max_offset ▪ commit_ts に Cluster 内の Clock Offset を加算した Timestamp。 • つまり、Tx は [commit_ts, commit_ts + max_offset] という “時刻の幅” の 情報を持っている。 • この “時刻の幅” を “Uncertainty Interval” という。
  42. x1 t3 Timestamp を使った Read (Uncertainty Interval) 先程と同じように、時刻がずれた Node1 が

    Tx1 の Write (x1 を Write) を t3 で実行し、 Tx1 完了後に Tx2 (Read) を実行する。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) < ライトシタヨ
  43. x1 t3 Timestamp を使った Read (Uncertainty Interval) Tx2 を受け取った Node2

    (Tx2 の Gateway Node) は、ローカルの Timestamp を基 に、Tx2 に t2 (t2 < t3) と “t2 + max_offset” を設定する。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 t2 + max_offset < Tx2.ts = [t2, t2 + max_offset]
  44. x1 t3 Timestamp を使った Read (Uncertainty Interval) Node2 は key1

    の Leaseholder である Node3 に Read を送る。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 t2 + max_offset t2 < ヨロシク < OK
  45. x1 t3 Timestamp を使った Read (Uncertainty Interval) この時 t2 +

    max_offset の Timestamp を基にした Uncertainty Interval が設定される。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 t2 + max_offset t2 Uncertainty Interval
  46. x1 t3 Timestamp を使った Read (Uncertainty Interval) Node3 (Leaseholder) での処理で、Uncertainty

    Interval の範囲に収まる Timestamp t3 (t2 < t3 < t2 + max_offset) で Write された値 x1 が存在することを検知する。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 t2 + max_offset t2 < オヤ? Uncertainty Interval
  47. x1 t3 Timestamp を使った Read (Uncertainty Interval) この Uncertainty Interval

    の範囲に収まる Timestamp t3 で Write された値 x1 は、 Cluster 内の Clock Offset に起因して順序 (時刻) が逆転している可能性が有る “不確 実な値” であると判断される。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 t2 + max_offset t2 < ズレテルカモ Uncertainty Interval
  48. x1 t3 Timestamp を使った Read (Uncertainty Interval) この “不確実な値” を検知した

    Node3 (Leaseholder) は、(t2 に対して確実に過去である t0 の値 x0 ではなく) その “不確実な値” である t3 の値 x1 を Node2 (Gateway Node) に返す。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 t2 + max_offset t2 < タブン x1 (t3) < ナルホド?
  49. x1 t3 Timestamp を使った Read (Uncertainty Interval) Node2 (Gateway Node)

    は、Node3 (Leaseholder) から受け取ったレスポンス (x1) の Timestamp が “Tx2 の Timestamp より大きい (Tx2.ts < x1.ts) か否か” を確認する。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 t2 + max_offset t2 < “Tx2.ts < x1.ts“ or Not? t3
  50. t3 x1 t3 Timestamp を使った Read (Uncertainty Interval) Node2 (Gateway

    Node) は、Node3 (Leaseholder) から受け取ったレスポンス (x1) の Timestamp を確認し、”Tx2.ts = t2” < “x1.ts = t3” だった場合、Tx2 の Timestamp を t2 から t3 に変更 (push) する。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 + max_offset t2 < Tx2.ts = t3
  51. x1 t3 Timestamp を使った Read (Uncertainty Interval) Node2 (Gateway Node)

    は、Tx2 (Read) の結果として key1 = x1 を Client に返す。 Tx2 は “t3 で実行された” という扱いになる。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 + max_offset t2 < key1 = x1 t3
  52. x1 t3 Timestamp を使った Read (Uncertainty Interval) 結果として、Tx1 -> Tx2

    の順番で Tx が実行された時に、Node 間で Clock Offset が あった場合でも、Tx2 は Tx1 で Write された値 (最新の値) を Read できる (一貫性のあ る Read が実行される)。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 + max_offset t2 < ライトシタヨ < リードシタヨ < イロイロシタヨ t3
  53. t3 x1 t3 Timestamp を使った Read (Uncertainty Interval) 言い換えると、Uncertainty Interval

    の上限である t2 + max_offset 以下の Timestamp T (T < t2 + max_offset) で Write された値は、Tx2 から見て “全て過去の値である” もの として扱われる。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 + max_offset t2 < ライトシタヨ < リードシタヨ < イロイロシタヨ
  54. t3 x1 t3 Timestamp を使った Read (Uncertainty Interval) ただし、この処理 (単純な

    Timestamp の変更) のみではカバーしきれない問題が有るの で、実際には後述する Read Refresh の処理も併せて実行される。 Tx1: Write (key1, x1) Tx’s Real Time Order (Clients) t0 t Tx1’s Gateway Node (Node1) t0 t Tx2’s Gateway Node (Node2) t0 t key1’s Leaseholder (Node3) x0 t3 Clock Offset Tx2: Read (key1) t2 + max_offset t2 < ホカニモ < ショリガ < アルヨ
  55. 用語概要 (Read Refresh) • Tx の Timestamp t1 が未来の Timestamp

    t2 に変更 (push) された場 合、その Tx で Read 済みの値が (t1, t2] の範囲で更新されていないかを 確認する必要が有る。 • Tx で Read された key の情報がメモリ上に保持されており、その情報を 基に Read 済みの key が (t1, t2] の範囲で更新されているか否かをチェッ クする。 • Read 済みの key が (t1, t2] の範囲で更新されていない場合、 Timestamp を未来の値 t2 に変更 (push) し、Tx の処理を継続できる。 • Read 済みの key が (t1, t2] の範囲で更新されている場合、 Timestamp を未来の値 t2 に変更 (push) した上で、Tx を再実行する必要が有る。
  56. y2 t1 t1 Client1 が Tx3 (key1 と key2 を

    Read) を実行する。また、Client1 とは異なる複数の Client (Tx) にて、key1 = x1 (t1), key2 = y1 (t1), key2 = y2 (t4) の 3つの値が Write さ れている。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) < リード t0 t Read Tx’s Gateway Node (Node1) < key1 ガアルヨ t0 t key1’s Leaseholder (Node2) < key2 ガアルヨ t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 Read Refresh ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。
  57. y2 t1 t1 Tx3 を受け取った Node1 (Tx3 の Gateway Node)

    は、ローカルの Timestamp を基 に、Tx3 に t2 (t1 < t2 < t4) と “t2 + max_offset” を設定する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) < Tx3.ts = [t2, t2 + max_offset] t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh
  58. y2 t1 t1 Node1 (Tx3 の Gateway Node) は key1

    の Leaseholder である Node2 と、key2 の Leaseholder である Node3 に Read を送る (Pipelining によって並列で実行される)。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) < ヨロシク * 2 t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 t2 t2 + max_offset < OK < OK Read Refresh
  59. Uncertainty Interval y2 t1 t1 この時 t2 + max_offset の

    Timestamp を基にした Uncertainty Interval が設定される。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh
  60. Uncertainty Interval y2 t1 t1 Node2 にある key1 の最新の値 x1

    は t1 で Write されており、t1 < t2 であるため、 Node2 (key1 の Leaseholder) は key1 = x1 を Node1 (Tx3 の Gateway Node) に返 す。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset < x1 (t1) ダヨ < サンクス t1 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh
  61. Uncertainty Interval y2 t1 t1 Node3 (Leaseholder) での処理で、Uncertainty Interval の範囲に収まる

    t4 (t2 < t4 < t2 + max_offset) で Write された値 y2 が存在することを検知する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset t1 < オヤ? ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh
  62. Uncertainty Interval y2 t1 t1 Uncertainty Interval の範囲に収まる t4 で

    Write された値 y2 は、Cluster 内の Clock Offset に起因して順序 (時刻) が逆転している可能性が有る “不確実な値” であると判断 される。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset t1 < ズレテルカモ ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh
  63. t4 y2 t1 t1 “不確実な値” を検知した Node3 (key2 の Leaseholder)

    は、”不確実な値” である t4 で Write された値 y2 を、Node1 (Tx3 の Gateway Node) に返す。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < タブン y2 (t4) < ナルホド? t4 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh
  64. t4 y2 t1 t1 Node1 (Gateway Node) は、Node3 (Leaseholder) から受け取ったレスポンス

    (y2) の Timestamp が “Tx3 の Timestamp より大きい (Tx3.ts < y2.ts) か否か” を確認する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < “Tx3.ts < y2.ts“ or Not? t4 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh
  65. t4 y2 t1 t1 “Tx3.ts < y2.ts” が真である場合は、既に Read している他の値

    (key1) が (Tx3.ts, y2.ts] の範囲 (i.e. (t2, t4] の範囲) で更新されているか否かを確認する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < コウシンサレテル? t4 Check ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh
  66. t4 t4 y2 t1 t1 key1 の値が (Tx3.ts, y2.ts] (i.e.

    (t2, t4]) の範囲で更新されていない場合、Node1 (Gateway Node) は Tx3 の Timestamp を t2 から t4 (y2.ts) に変更 (push) する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < カワッテナイ、Tx3.ts = t4 ダナ No Update ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh
  67. t4 t4 y2 t1 t1 Node1 (Gateway Node) は、Tx3 (Read)

    の結果として key1 = x1, key2 = y2 を Client に返す。Tx3 は “t4 で実行された” という扱いになる。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < key1 = x1, key2 = y2 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh
  68. y2 t1 t1 Client1 が Tx3 (key1 と key2 を

    Read) を実行する。また、Client1 とは異なる複数の Client (Tx) にて、key1 = x1 (t1), key2 = y1 (t1), key2 = y2 (t4) の 3つの値が Write さ れている。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) < リード t0 t Read Tx’s Gateway Node (Node1) < key1 ガアルヨ t0 t key1’s Leaseholder (Node2) < key2 ガアルヨ t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx の再実行)
  69. y2 t1 t1 Tx3 を受け取った Node2 (Tx3 の Gateway Node)

    は、ローカルの Timestamp を基 に、Tx3 に t2 (t1 < t2 < t4) と “t2 + max_offset” を設定する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) < Tx3.ts = [t2, t2 + max_offset] t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx の再実行)
  70. y2 t1 t1 Node1 (Tx3 の Gateway Node) は key1

    の Leaseholder である Node2 と、key2 の Leaseholder である Node3 に Read を送る (Pipelining によって並列で実行される)。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) < ヨロシク * 2 t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 t2 t2 + max_offset < OK < OK ※ここは前述の処理と同じ※ Read Refresh (Tx の再実行)
  71. Uncertainty Interval y2 t1 t1 この時 t2 + max_offset の

    Timestamp を基にした Uncertainty Interval が設定される。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx の再実行)
  72. Uncertainty Interval y2 t1 t1 Node2 にある key1 の最新の値 x1

    は t1 で Write されており、t1 < t2 であるため、 Node2 (key1 の Leaseholder) は key1 = x1 を Node1 (Tx3 の Gateway Node) に返 す。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset < x1 (t1) ダヨ < サンクス t1 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx の再実行)
  73. Uncertainty Interval y2 t1 t1 Node3 (Leaseholder) での処理で、Uncertainty Interval の範囲に収まる

    t4 (t2 < t4 < t2 + max_offset) で Write された値 y2 が存在することを検知する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset t1 < オヤ? ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx の再実行)
  74. Uncertainty Interval y2 t1 t1 Uncertainty Interval の範囲に収まる t4 で

    Write された値 y2 は、Cluster 内の Clock Offset に起因して順序 (時刻) が逆転している可能性が有る “不確実な値” であると判断 される。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset t1 < ズレテルカモ ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx の再実行)
  75. t4 y2 t1 t1 “不確実な値” を検知した Node3 は、”不確実な値” である t4

    で Write された値 y2 を Node1 に返す。また、key1 = x1 を t2 で Read した後に、t3 のタイミングで key1 の値 が他の Client (Tx) によって key1 = x2 に更新された。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < タブン y2 (t4) < ナルホド? t4 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここから処理が違う※ t3 x2 < key1 = x2 at t3 ! Read Refresh (Tx の再実行)
  76. t4 y2 t1 t1 Node1 (Gateway Node) は、Node3 (Leaseholder) から受け取ったレスポンス

    (y2) の Timestamp が “Tx の Timestamp より大きい (Tx3.ts < y2.ts)” か否かを確認する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < “Tx3.ts < y2.ts” or Not? t4 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 t3 x2 Read Refresh (Tx の再実行)
  77. t4 y2 t1 t1 “Tx3.ts < y2.ts” が真である場合は、既に Read している他の値

    (key1) が (Tx3.ts, y2.ts] の範囲 (i.e. (t2, t4] の範囲) で更新されているか否かを確認する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < コウシンサレテル? t4 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 t3 x2 Check Read Refresh (Tx の再実行)
  78. x2 t3 t4 y2 t1 t1 Read Refresh 処理で key1

    の値が (t2, t4] の範囲内である t3 で更新されていることを 検知する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < カワッテルワ t4 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Updated Read Refresh (Tx の再実行)
  79. t4 x2 t3 t4 y2 t1 t1 一貫性を保つために、t4 で実行される Tx

    は key1 = x1 (t1) ではなく、key1 = x2 (t3) の 値を読む必要がある。そのため、単純に “Tx3 の Timestamp を t4 に変更” して Client に結果 (key1 = x1, key2 = y2) を返すことはできない。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < ヘンコウデキナイ ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh (Tx の再実行)
  80. < リトライ! (Tx3.ts = t4) t4 x2 t3 t4 y2

    t1 t1 そのため、(Client にまだ何も返していない場合は) 内部で自動的に Tx を再実行する。 この再実行される Tx の Timestamp は t4 になる。また、この時 Uncertainty Interval の 上限である t2 + max_offset の位置は変わらない。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh (Tx の再実行)
  81. < アラタメテヨロシク * 2 t4 x2 t3 t4 y2 t1

    t1 Node1 (Gateway Node) は、新しい Timestamp t4 を設定した Tx3 を再実行する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 < OK < OK Read Refresh (Tx の再実行)
  82. t4 x2 t3 t4 y2 t1 t1 この時 t2 +

    max_offset の Timestamp を基にした Uncertainty Interval [t4, t2 + max_offset] が設定される。Uncertainty Interval の上限である t2 + max_offset の位置は変わらない。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Read Refresh (Tx の再実行)
  83. t2 t3 t4 x2 t3 t4 y2 t1 t1 Node2,

    Node3 からそれぞれ key1, key2 の値が返ってくる。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 < x2 (t3) ダヨ < y2 (t4) ダヨ < サンクス * 2 Read Refresh (Tx の再実行)
  84. t3 t2 t4 x2 t3 t4 y2 t1 t1 各レスポンスの

    Timestamp を確認し、x2.ts =< Tx3.ts 及び y2.ts =< Tx3.ts だった場 合、Uncertain Value (不確実な値) は存在していないので、Tx3 の Timestamp の変更 (2回目) は不要であり、Read Refresh は実行されない。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 < モンダイナシ! Read Refresh (Tx の再実行)
  85. t4 x2 t3 t4 y2 t1 t1 Node1 (Gateway Node)

    は、Tx3 (Read) の結果として key1 = x2, key2 = y2 を Client に返す。Tx3 は “t4 で実行された” という扱いになる。また、内部で実行されているため、 Tx の再実行は Client には見えない (Client にエラーは返らない)。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 < key1 = x2, key2 = y2 t3 Read Refresh (Tx の再実行)
  86. y2 t1 t1 Client1 が Tx3 (key1 と key2 を

    Read) を実行する。また、Client1 とは異なる複数の Client (Tx) にて、key1 = x1 (t1), key2 = y1 (t1), key2 = y2 (t4) の 3つの値が Write さ れている。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) < リード t0 t Read Tx’s Gateway Node (Node1) < key1 ガアルヨ t0 t key1’s Leaseholder (Node2) < key2 ガアルヨ t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  87. y2 t1 t1 Tx3 を受け取った Node2 (Tx3 の Gateway Node)

    は、ローカルの Timestamp を基 に、Tx3 に t2 (t1 < t2 < t4) と “t2 + max_offset” を設定する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) < Tx3.ts = [t2, t2 + max_offset] t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  88. y2 t1 t1 Node1 (Tx3 の Gateway Node) は key1

    の Leaseholder である Node2 と、key2 の Leaseholder である Node3 に Read を送る (Pipelining によって並列で実行される)。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) < ヨロシク * 2 t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 t2 t2 + max_offset < OK < OK ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  89. Uncertainty Interval y2 t1 t1 この時 t2 + max_offset の

    Timestamp を基にした Uncertainty Interval が設定される。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  90. Uncertainty Interval y2 t1 t1 Node2 にある key1 の最新の値 x1

    は t1 で Write されており、t1 < t2 であるため、 Node2 (key1 の Leaseholder) は key1 = x1 を Node1 (Tx3 の Gateway Node) に返 す。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset < x1 (t1) ダヨ < サンクス t1 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  91. Uncertainty Interval y2 t1 t1 Node3 (Leaseholder) での処理で、Uncertainty Interval の範囲に収まる

    t4 (t2 < t4 < t2 + max_offset) で Write された値 y2 が存在することを検知する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset t1 < オヤ? ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  92. Uncertainty Interval y2 t1 t1 Uncertainty Interval の範囲に収まる t4 で

    Write された値 y2 は、Cluster 内の Clock Offset に起因して順序 (時刻) が逆転している可能性が有る “不確実な値” であると判断 される。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 t4 x0 t2 t2 + max_offset t1 < ズレテルカモ ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  93. t4 y2 t1 t1 “不確実な値” を検知した Node3 は、”不確実な値” である t4

    で Write された値 y2 を Node1 に返す。また、key1 = x1 を t2 で Read した後に、t3 のタイミングで key1 の値 が他の Client (Tx) によって key1 = x2 に更新された。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < タブン y2 (t4) < ナルホド? t4 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 t3 x2 < key1 = x2 at t3 ! ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  94. t4 y2 t1 t1 さらに、今回は Read した key1 の値 x1

    を既に Client に返している。インタラクティブな SQL クライアント等を利用し、Tx 内で複数の Statement を実行してる場合、先に実行し た Statement の結果が既に Client に返されている場合がある。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < key1 = x1 t4 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 t3 x2 ※ここの処理が違う※ Read Refresh (Tx Retry Error)
  95. t4 y2 t1 t1 Node1 (Gateway Node) は、Node3 (Leaseholder) から受け取ったレスポンス

    (y2) の Timestamp が “Tx の Timestamp より大きい (Tx3.ts < y2.ts)” か否かを確認する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < “Tx3.ts < y2.ts” or Not? t4 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 t3 x2 ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  96. t4 y2 t1 t1 “Tx3.ts < y2.ts” が真である場合は、既に Read している他の値

    (key1) が (Tx3.ts, y2.ts] の範囲 (i.e. (t2, t4] の範囲) で更新されているか否かを確認する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < コウシンサレテル? t4 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 t3 x2 Check ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  97. x2 t3 t4 y2 t1 t1 Read Refresh 処理で key1

    の値が (t2, t4] の範囲内である t3 で更新されていることを 検知する。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < カワッテルワ t4 ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 Updated ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  98. t4 x2 t3 t4 y2 t1 t1 一貫性を保つために、t4 で実行される Tx

    は key1 = x1 (t1) ではなく、key1 = x2 (t3) の 値を読む必要がある。そのため、単純に “Tx3 の Timestamp を t4 に変更” して Client に結果 (key1 = x1, key2 = y2) を返すことはできない。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset t1 < ヘンコウデキナイ ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ※ここは前述の処理と同じ※ Read Refresh (Tx Retry Error)
  99. < リトライ... デキナイ...... t4 x2 t3 t4 y2 t1 t1

    さらに、今回は既に Client に一部の結果 (t2 時点での値として key1 = x1) を返してし まっているので、内部で自動的に Tx を再実行 (新しい Timestamp t4 で Tx を再実行) することもできない。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 < ドンマイ... < ドンマイ... ※ここから処理が違う※ Read Refresh (Tx Retry Error)
  100. < ゴメン... リトライシテ...... t4 x2 t3 t4 y2 t1 t1

    そのため、Node1 (Gateway Node) は Client に “Transaction Retry Error” (ReadWithinUncertaintyIntervalError)” を返す。Tx は ABORT しているので、Client 側 で再実行等のエラーハンドリングが必要になる。 Tx3: Read (key1), Read (key2) Tx3’s Client (Client1) t0 t Read Tx’s Gateway Node (Node1) t0 t key1’s Leaseholder (Node2) t0 t key2’s Leaseholder (Node3) y1 y0 x1 x0 t2 t2 + max_offset ※Write を実行した他の  Client (Tx) と Gateway  Node は図から省略。 ReadWithinUncertaintyIntervalError Read Refresh (Tx Retry Error)
  101. 用語概要 (Timestamp Cache) • Node 毎に Timestamp Cache (メモリ上の構造体) を持っている。

    • Timestamp Cache には “key を Read した時刻” の情報が保存される。 • 各 Read 操作が実行されるたびに、当該 Read 操作の Timestamp と Read 対象の key の情報が Node Local (Leaseholder?) の Timestamp Cache に格納される。 • この Timestamp Cache を利用して、既に未来の Timestamp で Read さ れた値を過去の Timestamp で上書きする、という Conflict を防ぐことがで きる。 • LRU (Least Recently Used) であり、サイズに上限がある。 • サイズ上限に達すると、古いものから削除される。
  102. • key1 は複数回 Read されており、 key1 が Read された最も新しい時 刻は

    t6。 • 古いものから削除されるので、key の情報が無い場合も有る。 • Timestamp Cache 内に存在しない key は、少なくとも下限である t1 以 降は Read されていない (t1 < T に なる時刻 T で Write 可能) と判断で きる。 Timestamp Cache のイメージ key5 ~ key7 t8 key2 t7 key1 t6 key3 t5 key1 ~ key3 t4 key5 t3 key2 t2 key1 t1 key1’s High Water Mark All key’s Low Water Mark
  103. t1 Tx4 (key1 への Write) と Tx5 (key1 を Read)

    の 2つが並列で実行される。また、Tx4 の完了前に (Tx4 を待たずに) Tx5 が実行される。Tx4 / Tx5 で Write / Read する key1 の Leaseholder は Node3。 < ライト t0 t Tx4’s Gateway Node (Node1) < リード t0 t Tx5’s Gateway Node (Node2) < リースホルダー t0 t key1’s Leaseholder (Node3) x1 Timestamp Cache Two Tx run in Parallel (Clients) Tx4: Write (key1, x2) Tx5: Read (key1)
  104. t1 Tx4 を受け取った Node1 (Tx4 の Gateway Node) は、ローカルの Timestamp

    を基 に、Tx4 に t2 を設定する。 < Tx4.ts = t2 t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t2 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  105. t2 t1 Node 間の Network Latency, Node 自体の性能問題, Tx4 内の

    Statement の都合 (BEGIN してから Write を実行するまでの間に時間がある) 等に起因して、Tx4 の Write が Node3 で実行される前に Tx5 が実行される。 < ショリチュウ... t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  106. t2 t1 Tx5 を受け取った Node2 (Tx5 の Gateway Node) は、ローカルの

    Timestamp を基 に、Tx5 に t3 を設定する。 < ショリチュウ... t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 < Tx5.ts = t3 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  107. t2 t1 Node2 は key1 の Leaseholder である Node3 に

    Read を送る。この時、Node1 での Tx4 (Write) はまだ Leaseholder に送られていない。 < ショリチュウ... t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 < ヨロシク < OK Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  108. t3 x1 t2 t1 Node3 (Leaseholder) は、t3 の時点での key1 の最新の値である

    x1 を Node2 (Tx5 の Gateway Node) に返す。また、key1 が Read された時刻 t3 を Timestamp Cache に保存する。 < ショリチュウ... t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 < サンクス < x1 ダヨ (Read at t3) Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  109. t3 x1 t2 t1 Node2 (Tx5 の Gateway Node) は、Client

    に “key1 = x1” を返す。 < ショリチュウ... t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 < key1 = x1 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  110. t3 x1 t2 t1 Tx5 で (t3 で) key1 の値

    x1 が Read された後に、Tx4 の Write が Node3 (Leaseholder) に届く。 < ヨロシク t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 < OK Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  111. t3 x1 t2 t1 しかし、このタイミングで届いた Write (Tx4) の Timestamp は

    t2 (t2 < t3) になってい る。 t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 < t2…? t2 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  112. t3 x1 t2 t1 Tx5 が既に t3 時点での値として “key1 =

    x1” を Read しているため、後から Tx4 が t2 で key1 の値を x2 (t2) に書き換えてしまうと、不整合が発生する (Tx5 は t3 時点の最 新の値として x2 を Read していないとおかしいことになる)。 t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 < チョットマテ... t2 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  113. t3 x1 t2 t1 そのため、Node3 (Leaseholder) は Tx4 の Write

    を実行しない。Timestamp Cache に “key1 を Read した最も新しい Timestamp は t3” であることが記録されているため、こ の Conflict を検知することができる。 < (´・ω・`) t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 < ヤッパダメ t2 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  114. t2 t3 x1 t2 t1 Node3 (Leaseholder) は Timestamp Cache

    の情報を基に、key1 が Read された最新 の時刻 t3 を Node1 (Tx4 の Gateway Node) に返す。 < ナルホド t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 < key1 was read at t3 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  115. t2 t3 x1 t2 t1 Node1 (Tx4 の Gateway Node)

    は、Node3 (Leaseholder) から得られた時刻 t3 を基 に、Tx4 の Timestamp を t4 (t3 < t4) に変更し、Tx4 を再実行する。 < リトライ! (Tx4.ts = t4) t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) t4 Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  116. t4 t2 t3 x1 t2 t1 Node1 (Tx4 の Gateway

    Node) は再実行した Tx4 (t4) にて Node3 (Leaseholder) に Write を送る。 < アラタメテヨロシク t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) t4 < OK Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  117. x2 t4 x1 t2 t3 t2 t1 Node 3 (Leaseholder)

    は x2 を t4 で Write し、Node1 (Tx4 の Gateway Node) にレス ポンスを返す。 < サンクス t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) t4 < カイタヨ Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  118. t2 t3 t2 t1 Tx4 の Timestamp が変更されているので、Tx4 の COMMIT

    のタイミングで Read Refresh (Tx4 で既に Read されている他の key のチェック処理) が実行される。 ※Read Refresh の処理は前述の通りなので割愛※ < リフレッシュ t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) t4 x2 t4 x1 if (Read Refresh OK) then COMMIT else ABORT Check Update by Read Refresh Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  119. t2 t3 t2 t1 なお、Uncertainty Interval による Timestamp の変更時は即時 Read

    Refresh が実行 されてから後続の処理が実行されるが、Timestamp Cache による Timestamp の変更 時は COMMIT のタイミングで Read Refresh が実行される。 t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) t4 x2 t4 x1 if (Read Refresh OK) then COMMIT else ABORT < ショリノ < チガウヨ < タイミングガ Check Update by Read Refresh Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  120. t2 t3 t2 t1 Read Refresh で問題が無ければ、Tx4 は COMMIT できる。

    < モンダイナイナ t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) t4 Read Refresh OK x2 t4 x1 Read Refresh OK Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  121. x1 x2 t4 t2 t3 t2 t1 Node1 (Tx4 の

    Gateway Node) は、Client に “Write OK” を返す。 < Write OK t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) t4 Read Refresh OK Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  122. t2 t3 t2 t1 Read Refresh の結果が NG だった場合 (Tx4

    を t4 で再実行すると一貫性が保てない 場合)、Tx4 は ABORT される。 t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) t4 x2 t4 x1 < リトライ... デキナイ...... < ドンマイ... < ドンマイ... Read Refresh NG Read Refresh NG Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  123. x1 x2 t4 t2 t3 t2 t1 そのため、Node1 (Gateway Node)

    は Client に “Transaction Retry Error” (RETRY_SERIALIZABLE) を返す。Tx は ABORT しているので、Client 側で再実行等 のエラーハンドリングが必要になる。 t0 t t0 t t0 t key1’s Leaseholder (Node3) x1 t3 Timestamp Cache Tx4: Write (key1, x2) Tx5: Read (key1) Two Tx run in Parallel (Clients) t4 < ゴメン... リトライシテ... Read Refresh NG RETRY_SERIALIZABLE Tx4’s Gateway Node (Node1) Tx5’s Gateway Node (Node2)
  124. t t t0 1. Read Tx (Tx.ts = t) より過去の

    Timestamp t0 (t0 < t) で Write された COMMIT 済みの値が有る場合、その値を Read する。 < key = x1 Read Tx’s Gateway Node (Node1) key’s Leaseholder (Node2) x1 Write - Read Conflicts Read Tx Read Tx (Client)
  125. t t t0 2. Read Tx (Tx.ts = t) より過去の

    Timestamp t0 (t0 < t) で Write された Write Intent (Tx Status = PENDING or STAGING) が有る場合、Write Tx が完了するのを待 つ。 < マツワ Read Tx’s Gateway Node (Node1) key’s Leaseholder (Node2) write intent Write - Read Conflicts Read Tx Read Tx (Client) < インテント
  126. t1 t t 3. Read Tx (Tx.ts = t) に対して

    Uncertainty Interval [ t , t + max_offset] の範囲に COMMIT 済みの値 x2 (t1) があった場合、Read Tx の Timestamp が t1 に変更さ れ、Read Refresh で問題が無ければ、その値を読み込む。 < key = x2 Read Tx’s Gateway Node (Node1) key’s Leaseholder (Node2) Write - Read Conflicts Read Tx Read Tx (Client) Uncertainty Interval x2
  127. t t 4. Read Tx (Tx.ts = t) に対して Uncertainty

    Interval [ t , t + max_offset] の範囲に Write Intent (Tx Status = PENDING or STAGING) がある場合、Write Tx が完了 するのを待つ。 Read Tx’s Gateway Node (Node1) key’s Leaseholder (Node2) Write - Read Conflicts Read Tx Read Tx (Client) Uncertainty Interval write intent < マツワ < インテント
  128. t t 5. Read Tx (Tx.ts = t) に対して Uncertainty

    Interval [ t , t + max_offset] より未来の Timestamp t2 (t < t2) で Write された COMMIT 済みの値があっても、その値は無 視して、t 時点での最新の値を Read する。 Read Tx’s Gateway Node (Node1) key’s Leaseholder (Node2) Write - Read Conflicts Read Tx Read Tx (Client) Uncertainty Interval t2 x2 t0 x1 < key = x1 < ムシ
  129. t t 6. Read Tx (Tx.ts = t) に対して Uncertainty

    Interval [ t , t + max_offset] より未来の Timestamp t2 (t < t2) で Write された Write Intent があっても、その Intent は無視 して、t 時点での最新の値を Read する。 Read Tx’s Gateway Node (Node1) key’s Leaseholder (Node2) Write - Read Conflicts Read Tx Read Tx (Client) Uncertainty Interval t2 t0 x1 < key = x1 write intent < ムシ
  130. t1 t0 Write Tx (Tx.ts = t) より未来の Timestamp t1

    (t < t1) で key が Read されている場 合、その key に対して Write できない。 x1 t1 < key was read at t1 Two Tx run in Parallel (Clients) Read - Write Conflicts Read Tx Write Tx’s Gateway Node (Node1) Read Tx’s Gateway Node (Node2) key’s Leaseholder (Node3) < key = x1 t t < オソカッタ Write Tx (key, x2)
  131. t1 t0 Write Tx (Tx.ts = t) より未来の Timestamp t1

    (t < t1) で key が Read されている場 合、Write Tx を新しい Timestamp t2 (t < t1 < t2) で再実行し、Read Refresh で問題が 無ければ、その値を COMMIT する。 x1 t1 < key was read at t1 Two Tx run in Parallel (Clients) Read - Write Conflicts Write Tx (key, x2) Read Tx Write Tx’s Gateway Node (Node1) Read Tx’s Gateway Node (Node2) key’s Leaseholder (Node3) < key = x1 t t < リトライ! (Write Tx.ts = t2) x2 t2 t2
  132. t t t0 1. Write Tx (Tx.ts = t) より過去の

    Timestamp t0 (t0 < t) で Write された COMMIT 済みの値が有る場合、そのまま新しい値 x2 を t で Write する。 < New key -> x2 Write Tx’s Gateway Node (Node1) key’s Leaseholder (Node2) x1 Write - Write Conflicts Write Tx (key, x2) Write Tx (Client) x2
  133. t t t0 2. Write Tx (Tx.ts = t) より過去の

    Timestamp t0 (t0 < t) で Write された Write Intent (Tx Status = PENDING or STAGING) が有る場合、過去の (t0 の) Write Tx が完 了するのを待つ。 < マツワ key’s Leaseholder (Node2) write intent < インテント Write Tx’s Gateway Node (Node1) Write - Write Conflicts Write Tx (key, x2) Write Tx (Client)
  134. t t 3. Write Tx (Tx.ts = t) に対して t

    より未来の Timestamp t1 (t < t1) で Write された COMMIT 済みの値がある場合、Write Tx を新しい Timestamp t2 (t < t1 < t2) で 再実行し、Read Refresh で問題が無ければ、その値を COMMIT する。 key’s Leaseholder (Node2) t1 < New key -> x1 at t2 Write Tx’s Gateway Node (Node1) Write - Write Conflicts Write Tx (key, x2) Write Tx (Client) t2 x1 x2 t2
  135. t t 4. Write Tx (Tx.ts = t) に対して t

    より未来の Timestamp t1 (t < t1) で Write された Write Intent (Tx Status = PENDING or STAGING) がある場合、未来の (t1 の) Write Tx が完了するのを待つ。 key’s Leaseholder (Node2) t1 < マツワ write intent < インテント Write Tx’s Gateway Node (Node1) Write - Write Conflicts Write Tx (key, x2) Write Tx (Client)
  136. No Stale Read • 公式 (Cockroach Labs) 曰く、CockroachDB の Consistency

    Model (Consistency Level) は “No Stale Read” である。 • これは “Stale Read Anomaly” が発生しないことを意味している。 • “Stale Read Anomaly” は、ざっくり言うと “古い値を Read” してしまう Anomaly。 • CockroachDB では、前述した Uncertainty Interval 等の仕組みを利用す ることで、Cluster 内の Clock Offset が許容される Max Offset の範囲に 収まっていれば、“時刻 t で実行された Tx” は “常に時刻 t における最新の 値を Read” できる。 • そのため、「”Stale Read Anomaly” は発生しない」=「CockroachDB の Consistency Model は “No Stale Read”」と言われている。
  137. No Stale Read (参考情報) • Stale Read Anomaly や後述する Causal

    Reverse Anomaly について は、以下のブログがわかりやすいです。 ◦ https://fauna.com/blog/demystifying-database-systems-correctness-anomalies-und er-serializable-isolation • また、CockroachDB の “No Stale Read” については、以下のブログに記 載されています。 ◦ https://www.cockroachlabs.com/blog/consistency-model/ • この辺をいろいろまとめる時間がなかったので、ブログを参照していただけ ると...
  138. Causal Reverse Anomaly • CockroachDB では “Stale Read Anomaly” は発生しないが、”Causal

    Reverse Anomaly” は発生する。 • “Causal Reverse Anomaly” は、ざっくり言うと “直接因果関係の無い 2つ の Tx の順序が逆転” してしまう Anomaly (いろいろまとめる時間がなかっ たので、詳細は前述のブログを参照していただけると...)。 • 前述のブログ曰く、CockroachDB での一例として、 AS OF SYSTEM TIME 句 (過去の特定の時刻 t で Read する機能) や、内部的に同じよう な機能を使っている (特定の時刻で Read する) BACKUP DATABASE 等 で発生する可能性が有るらしい。
  139. y0 t1 Tx1 (key1 への Write) と Tx2 (key2 への

    Write) が Tx1 -> Tx2 の順番で実行される。 Tx1 と Tx2 はそれぞれ関連しない (Conflict しない) 別々の key である key1 と key2 に それぞれ Write を行う。 < ライト t0 t Tx1’s Gateway Node (Node1) < ライト t0 t Tx2’s Gateway Node (Node2) t key1 and key2’s Leaseholder (Node3) x0 Causal Reverse Anomaly の例 Tx’s Real Time Order (Clients) Tx1: Write (key1, x1) Tx2: Write (key2, y1) t0 < リースホルダー
  140. y0 t1 また、Tx1 と Tx2 が実行される付近で Tx3 (key1 と key2

    を両方 Read) が実行される。 < ライト t0 t < ライト t0 t < リースホルダー t key1 and key2’s Leaseholder (Node3) x0 Causal Reverse Anomaly の例 Tx’s Real Time Order (Clients) Tx1: Write (key1, x1) t0 Tx3: Read (key1, key2) Tx1’s Gateway Node (Node1) Tx2’s Gateway Node (Node2) Tx2: Write (key2, y1)
  141. y0 t1 Tx1 -> Tx2 の順番で実行されるので、通常 Tx3 の結果として期待される値は「A : key1

    = x0, key2 = y0」「B : key1 = x1, key2 = y0」「C : key1 = x1, key2 = y1」のいずれかに なる。 < Tx1.ts = t2 t0 t < Tx2.ts = t3 t0 t t key1 and key2’s Leaseholder (Node3) x0 Causal Reverse Anomaly の例 Tx’s Real Time Order (Clients) Tx1: Write (key1, x1) t0 Tx3: Read (key1, key2) Tx1’s Gateway Node (Node1) y1 x1 A t2 t3 B C Tx2’s Gateway Node (Node2) Tx2: Write (key2, y1)
  142. y0 t1 しかし、Node1 の時刻がずれている場合、key1 と key2 の Timestamp が逆転してしま う可能性が有る。

    < Tx1.ts = t4 t0 t < Tx2.ts = t3 t0 t t key1 and key2’s Leaseholder (Node3) x0 Causal Reverse Anomaly の例 Tx’s Real Time Order (Clients) Tx1: Write (key1, x1) t0 Tx3: Read (key1, key2) Tx1’s Gateway Node (Node1) y1 x1 t4 t3 t4 Tx2’s Gateway Node (Node2) Tx2: Write (key2, y1)
  143. y0 t1 その場合、Tx3 の結果が「A : key1 = x0, key2 =

    y0」「B : key1 = x0, key2 = y1」「C : key1 = x1, key2 = y1」のいずれかになる。B の結果は Tx1 -> Tx2 という Client 側での 実行順序と相反する結果 (Tx2 の Write だけ見える) になっている。 < Tx1.ts = t4 t0 t < Tx2.ts = t3 t0 t t key1 and key2’s Leaseholder (Node3) x0 Causal Reverse Anomaly の例 Tx’s Real Time Order (Clients) Tx1: Write (key1, x1) t0 Tx3: Read (key1, key2) Tx1’s Gateway Node (Node1) y1 x1 A t4 t3 B C t4 Tx2’s Gateway Node (Node2) Tx2: Write (key2, y1)
  144. y0 t1 CockroachDB は “関連する (Conflict する)” Tx に対しては実行順序 (Timestamp)

    を調 整する。例えば、同じ key1 に対して Tx1 -> Tx2 が実行されていれば、Write - Write Conflict になるので、Tx2 は t5 (t4 < t5) で再実行される。 < Tx1.ts = t4 t0 t < Tx2.ts = t3 t0 t t key1 and key2’s Leaseholder (Node3) x0 Causal Reverse Anomaly の例 Tx’s Real Time Order (Clients) Tx1: Write (key1, x1) t0 Tx3: Read (key1, key2) Tx1’s Gateway Node (Node1) y1 x1 A t4 t3 B C t4 Tx2’s Gateway Node (Node2) Tx2: Write (key2, y1)
  145. y0 t1 しかし、”関連しない (Conflict しない)” 2つの key に対する Timestamp の調整は実行さ

    れないので、図のように Real Time の実行順序に相反する順番 (Timestmap) で 2つの key が Write され、Causal Reverse Anomaly が発生する。 < Tx1.ts = t4 t0 t < Tx2.ts = t3 t0 t t key1 and key2’s Leaseholder (Node3) x0 Causal Reverse Anomaly の例 Tx’s Real Time Order (Clients) Tx1: Write (key1, x1) t0 Tx3: Read (key1, key2) Tx1’s Gateway Node (Node1) y1 x1 A t4 t3 B C t4 Tx2’s Gateway Node (Node2) Tx2: Write (key2, y1)
  146. 話せなかったこと • Tx の競合が多いワークロードの場合、Uncertainty Interval や Timestamp Cache による Tx

    の再実行が多く発生し、性能が悪化 する可能性が有るため、最近のバージョンでは悲観的同時実行制 御 (Lock) の仕組みも実装されはじめています。 ◦ Concurrency manager ◦ Lock table • 興味がある人はドキュメントを是非読んでみてください。 ◦ https://www.cockroachlabs.com/docs/stable/architecture/transaction-layer.html
  147. • Uncertainty Interval, Read Refresh, Timestamp Cache 等 の仕組みを利用して、Timestamp を使った

    Tx 処理を実現し ている。 • 状況に応じて、内部で (Client には見えずに) Tx が再実行さ れる場合がある。 • 内部的な再実行で一貫性を保つことができない場合は、 Client に Retry Error が返ってくる (Client 側でエラーハンドリ ングが必要)。 まとめ
  148. 参考情報 • https://www.cockroachlabs.com/docs/stable/architecture/transaction-layer.html • https://www.cockroachlabs.com/blog/living-without-atomic-clocks/ • https://www.cockroachlabs.com/blog/serializable-lockless-distributed-isolation-cockroac hdb/ • https://www.cockroachlabs.com/blog/consistency-model/

    • https://resources.cockroachlabs.com/guides/cockroachdb-the-resilient-geo-distributed-s ql-database-sigmod-2020 • https://fauna.com/blog/demystifying-database-systems-correctness-anomalies-under-se rializable-isolation