Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add docs/limestone_race.md #44

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions docs/limestone_race.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# limestoneにおけるraceについて

2023-10-23 horikawa

## この文書について
下記を説明する。
* shirakamiがcallすることを想定しているlimestoneのdatastore::switch_epoch()とdata_channel::begin_session()のタイミング制約
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* shirakamiがcallすることを想定しているlimestoneのdatastore::switch_epoch()とdata_channel::begin_session()のタイミング制約
* shirakamiがcallすることを想定しているlimestoneのdatastore::switch_epoch()とlog_channel::begin_session()のタイミング制約

おそらく文中と図3~7の data_channel はすべて log_channel の誤記かと思います。

* durable epochを決める前提となるdata_channel::begin_session()の挙動、特に、begin_session()が属するepochを決める方法

## log書き込みの基本動作
1) あるepochに属するlogのshirakamiによる書き込みは、data_channel::begin_session()で開始し、data_channel::add_entry()によりlogをlimestoneに送り、data_channel::end_session()で終了する。
2) 通常、limestoneはdata_channel::end_session()を契機として、data_channel::add_entry()によって送られたlogを不揮発性記録媒体に書き込む操作を開始する。
3) logが属するepochは、data_channel::begin_session()をcallしたときのlimestone epochとなる。
4) limestone epochは、shirakamiがdatastore::switch_epoch()をcallすることで切り替わる。
5) どのlimestone epochまでdurableになった(logが不揮発記録として書き込まれた)のかは、datastore::last_epoch()で調べることができる。
6) durableとなったlimestone epochは、datastore::add_persistent_callback()でcallbackを登録しておくことで、durable epochがが更新される度にcall backとして通知を受け取ることもできる。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
6) durableとなったlimestone epochは、datastore::add_persistent_callback()でcallbackを登録しておくことで、durable epochがが更新される度にcall backとして通知を受け取ることもできる。
6) durableとなったlimestone epochは、datastore::add_persistent_callback()でcallbackを登録しておくことで、durable epochが更新される度にcall backとして通知を受け取ることもできる。

なお、現在の仕様では、data_channel::begin_session()により開始したlog書き込みが属するlimestone epochを陽に知ることはできない。


## shirakami
### limestoneに対する操作
* shirakamiは、datastore::switch_epoch()により、limestone epochの切り替わりをlimestoneに伝える。
* shirakamiは、data_channel::begin_session()により、現在のlimestone epochに属するlog書き込み開始をlimestoneに通知する。

上記の操作がshirakamiの別スレッドで実行されると、両者の間でlimestone epochに関するraceが発生する可能性がある。つまり、
shirakamiがlimestone epochをn-1からnに切り換えるためにdatastore::switch_epoch()をcallする場合、それがreturnするまでの間はlimestone epochがn-1かnかは不定となるため、
その実行期間(callからreturnまでの間)とdata_channel::begin_session()の実行期間に重なりがあると、data_channel::begin_session()が属するlimestone epochがn-1なのかnになるのかが不定となる。
(図1 case 1~4)。

<p>
<img src="limestone_race/Fig01.png" width="400">

図1 datastore::switch_epoch()とdata_channel::begin_session()の実行期間が重なっている場合
</p>

なお、両者が重なっていない場合(図2)は、data_channel::begin_session()が属するlimestone epochが不確定となることはない。

<p>
<img src="limestone_race/Fig02.png" width="400">

図2 datastore::switch_epoch()とdata_channel::begin_session()の実行期間に重なりがない場合
</p>


## limestone
### durable epochの定義
limestoneが認識するdurable epochは、
1) limestone epochより小さく、かつ、
2) 不揮発性記憶への書き込みが完了していないlogを持っているdata_channelのepoch
よりも小さいepochの最大値である。

### limestoneによるdurable epochの追跡
limestoneがdurable epochを認識できるようにするため、各log_channelは、各々が書き込んでるlogのepochを管理している。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
limestoneがdurable epochを認識できるようにするため、各log_channelは、各々が書き込んでるlogのepochを管理している
limestoneがdurable epochを認識できるようにするため、各log_channelは、各々が書き込んでいるlogのepochを管理している

その基本動作を図3に示す。

<p>
<img src="limestone_race/Fig03.png" width="400">

図3 log_channelによるlogのepoch管理<br>
各log_channelは、begin_session()時にlimestone epochを調べ、そのepochを以降のadd_entry()で送られるlogのepoch(channel's epoch)として記憶しておく。
ここで、channel's epochがUINT_MAXとなっている期間は、log_channel::begin_session()が行われていない(未書き込み尾logは存在しない)ことを示している。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ここで、channel's epochがUINT_MAXとなっている期間は、log_channel::begin_session()が行われていない(未書き込み尾logは存在しない)ことを示している。
ここで、channel's epochがUINT64_MAXとなっている期間は、log_channel::begin_session()が行われていない(未書き込みのlogは存在しない)ことを示している。

UINT_MAX だと unsigned int の MAX となってしまい多くの場合 32bit になってしまうため。(図3~7 も同様)
(epoch の max 的な別名を付けてもいいかとは思いますが、ここでやる話ではなさそう)

なお、channel's epochはlimestone内の他モジュールから読み出せるようになっている。

</p>

### channel's epochに関する制約
limestoneによるdurable epochの認識でraceが発生しないようにするため、各々のlog_channelがlogのepochを確定する操作には、以下の制約を設ける。
* limestoneのモジュールがlimestone epochとしてnを読み込んだ後(datastore::switch_epoch()によりnが設定された後)に全log channelのlog epochを読み出す操作を行った場合、
n-1以下のlogを書き込んでいるlog_channelが存在するのであれば、そのchannel's epochは必ずreadできる。

端的な例を示すと、datastore::switch_epoch()がnをlimestone epochとして書き込んだ後に全log channelのlog epochを読み出すと、
n-1以下のlogを書き込んでいるlog_channelが存在するのであればそのchannel's epochは必ずreadできる(図4の状況)、
逆に、n-1以下のlogを書き込んでいるlog_channelが存在しないことが判明した場合は、
その操作それ以降にepochがn-1以下のlog書き込みを開始(begin_session())するlog_channelは存在しないことが保証されている、ということである。

<p>
<img src="limestone_race/Fig04.png" width="400">

図4 datastore::switch_epoch()とlog_channel::begin_session()の関係<br>
switch_epoch()がlimestone epochをn-1からnに更新した後にlog_channelのchannel's epochをreadした場合、n-1のchannel's epochがreadできる状況。

</p>

但し、naiveに構築された(race対策が施されていない)log_channel::begin_session()では、図5に示すraceが発生し、上記の制約を満たさなくなる。

<p>
<img src="limestone_race/Fig05.png" width="400">

図5 datastore::switch_epoch()とlog_channel::begin_session()間のrace<br>
switch_epoch()がlimestone epochをn-1からnに更新する際、log_channel::begin_session()は更新前のlimestone epochをreadし、switch_epoch()によるchannel's epochのread操作後にchannel's epochをn-1に設定すると、channel's epochに関する制約を満たさなくなる。

</p>

このため、log_channel::begin_session()では、図6に示すように、channel's epochへの書き込みを行った後、再度limestone epochを読み込み、それがchannel's epochに書き込んだepochと同じかどうかを確認する。
両者が一致している場合は、channel's epochに関する制約が満たされるので、begin_session()の操作を完了させる一方、
両者が異なっている場合は、channel's epochへの書き込み操作を再実行し、channel's epochに書き込んだepochと同じ値がlimestone epochから読み込まれるまで繰り返すことで、
channel's epochに関する制約を満たすことを保証する。

<p>
<img src="limestone_race/Fig06.png" width="400">

図6 datastore::switch_epoch()とlog_channel::begin_session()間のrace防止<br>
log_channel::begin_session()が更新前のlimestone epoch(n-1)をreadしてchannel's epochに設定後、再度limestone epochをreadし、それがn-1でない(図ではnの)場合はchannel's epochの設定操作をやり直す。

</p>


このようなlimestone epochの再読み込みを行うと、「channel's epochに関する制約」が満たされることを図7に示す。

<p>
<img src="limestone_race/Fig06.png" width="400">

図7 datastore::switch_epoch()とlog_channel::begin_session()間のrace防止<br>
log_channel::begin_session()が更新前のlimestone epoch(n-1)をreadしてchannel's epochに設定後、再度limestone epochをreadした値がn-1の場合、
そのchannel's epochは、switch_epoch()がn-1をnに更新した時点でn-1であることが保証される。

</p>


なお、このようなリトライを行うと、channel's epochとして最初に書き込んだn-1がswitch_epoch()等からreadされる可能性もある。
この場合、n-1がdurable epochになっているにも関わらず、それはdurable epochではないと誤認識される可能性につながることになる。
但し、この誤認識は安全方向(危険方向の誤認識は「durableでないにも関わらずdurableと認識」される方向)であることと、発生頻度は非常に低い、の2点から実用面での問題はないと考えられる。
Binary file added docs/limestone_race/Fig01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/limestone_race/Fig02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/limestone_race/Fig03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/limestone_race/Fig04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/limestone_race/Fig05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/limestone_race/Fig06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/limestone_race/Fig07.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.