昨日の続き。
前にも書いたとおり、削除は哲学的な問題だ。更新も、削除ほどではないが、かなり哲学的だ。
研究レベルでは、「削除も更新もしない」というポリシーが大流行中らしい。すべて追記オンリーで済ませる。当然、HDDの記憶容量は消費する一方で、使い方によってはあっという間にゴミで埋まってしまうが、それは無視するのが研究レベルというものらしい。過去の状態がそのまま取り出せるので便利だが、ゴミで埋まるような使い方があることを考えると、MVCC的な方法のほうが汎用性がある。
私としては、もうひとつ疑問がある。
変化するデータを追記オンリーのストレージ上で表現しようとすると、なんらかの形で、アドレスの予約が必要になる。アドレスを予約できなければ、データが追記されたとき、そのデータのアドレスを知る方法がない。アドレスが予約されているということは、そのアドレスについて「まだデータが書き込まれていない」という状態が存在する。更新つまり状態の変更はないはずなのに、「まだデータが書き込まれていない」という状態の変更だけは許している。
これは深刻な破れ目のように感じる。
予約アドレスA1への追記が生じたときには、おそらく別の新しい予約アドレスA2が生成されるだろう。でなければデータの変化のたびに予約アドレスが減っていく。そしておそらくA1とA2は意味的に同じものだろう。だが、そうでないこともありうる。どこかで一度に100個の予約アドレスを確保して、それを99個まで使ってから、また100個の予約アドレスを確保する、というポリシーもありうるからだ。A1とA2が意味的に同じであると保証できないだけでなく、A2が常に生成されるともいえない。
それがなにか問題なのか? 分散環境では問題になる。
分散環境では可能なかぎりポーリングを減らす必要がある。予約アドレスが使われたときに、そのことを通知する仕組みが必要だ(予約アドレス追記通知)。この通知が、事実上、キャッシュ無効化通知と同じ役割を果たす。予約アドレス追記通知とキャッシュ無効化通知、どちらがより効率よく機能するか? おそらく、キャッシュ無効化通知だ。
なぜか。キャッシュ無効化通知なら、同じデータが複数回変更されたとき、それを1つのキャッシュ無効化通知として処理できる。それに対して予約アドレス追記は、常に異なるアドレスについて起こる。複数の追記を1つの通知として処理するには、その予約アドレスの意味を知っていなければならない。もちろん、キャッシュ無効化通知も、データの意味と切り離されたやりかた(ブロックデバイスのアドレスなど)でデータを指し示すなら同じことになる。
だから分散永続化システムは、エントリを指し示すのにsemantic IDを使い、エントリへの操作として更新を提供する。
更新は、「データの枠組みはそのままで内容を変化させた」という状態を作り出す。たとえばRDBMSのインデックスは、INSERTやDELETEやUPDATEのたびに更新される。これは意味的に更新であるだけではない。キャッシュ無効化通知の効率からも、この更新を、削除と作成で置き換えることはできない。
性能と意味がくっついているときには、できるだけ切り離さないようにすべきだ。ネットワークはHDDより柔軟なところがあるのだから、どこで切るかをよく考え直す必要がある。
昨日の修正。
マルチキャストアドレスで定められる枠をもっと使うべきだ、との結論に達した。ネットワークのトラフィックに局所性を作る方法がないと辛い。
分散永続化システムの上で動くもの(ファイルシステムやRDBMS)を、分散永続化システムとの関係において、「サービス」と呼ぶことにする。サービスは必ず1個のservice IDと1個のマルチキャストアドレスを持つ。無効化発行ノードの決定にservice IDを使う。システム上の全ノードのリストは用いず、かわりにサービス内の全ノードのリストを用いる。
ガベージコレクションがあると楽だと気づいた。ただしマークアンドスイープをするのは、分散永続化システムではなく、サービスだ。エントリのメタデータに、service IDと、マークアンドスイープのマーク用の領域を設ける。
昨日の補足。わからなかった人がいるようなので蛇足ながら。
1つのエントリに対して取得要求が集中しても、簡単に負荷分散できる。
ノードXに対してノードA、B、C、Dから同時に取得要求が来たとしよう。問題のエントリのバックアップノードはYとする。これらのノードを、UUID距離がもっとも近いもの同士が隣り合うように並べると、A・B・X・Y・C・Dとなるものとする。このときXは、Bの要求に応える。同時に、Aに対しては「Bに中継してもらえ」、Cに対しては「Yのバックアップをもらえ」、Dに対しては「Cに中継してもらえ」と返答する。
ネットワークがGbEであれば、分散永続化システム自体はかなり高速に動く。総ノード数2でも、ローカルHDDと遜色ないスループットと遅延が期待できる。しかしだからといって全体性能が約束されるわけがない。鎖の強さはもっとも弱い環で決まる。たとえば、各ノード上で動く分散処理が、ローカルキャッシュにできるだけヒットするような局所性の高い処理になっているかどうか。局所性がゼロなら、すぐにネットワークが飽和してしまうし、遅延も短くできない。