分散ファイルシステムで中間プロトコルに9pを使うアイディア

9pは非常にシンプルで素晴らしい。


LinuxのDocument/filesystems/9p.txtを読むと、マウントオプションにrfdno=nとwfdno=nというのがある。

  rfdno=n       the file descriptor for reading with proto=fd

  wfdno=n       the file descriptor for writing with proto=fd


proto=というのもある。

  proto=name    select an alternative transport.  Valid options are
                currently:
                        unix - specifying a named pipe mount point
                        tcp  - specifying a normal TCP/IP connection
                        fd   - used passed file descriptors for connection
                                (see rfdno and wfdno)


rfdnoとwfdnoを指定すれば、通信につかうファイルディスクリプタは、任意のものが使えるようだ。pipeも使えるはず。



そこで、ローカルで9pサーバーを動かし、そこから他のマシンへと通信を中継することは、それほど難しくないはず。

kernel -> local 9p server -> ネットワーク -> 9p server
kernel <- local 9p server <- ネットワーク <- 9p server


これができれば、レプリケーションは簡単にできるはず。書き込みを複製すればいい。読み込みは好きなホストから読み込む。
(実際のところ、サーバーがクライアントの情報を保持するようなプロトコルだと、これはマズイ。そこのところ未調査)

                                       -> 9p server 1
kernel -> local 9p server -> ネットワーク -> 9p server 2
                                       -> 9p server 3

kernel <- local 9p server <- ネットワーク <- 9p server 3


↓こう書いてもいい。

      -> node1
node0 -> node2
      -> node3

node0 <- node3


これの問題は、複数のノードが同じファイル(またはディレクトリ)に同時に書き込みを行うと、どちらの状態になるか分からないこと。分散トランザクションが必要になってしまう。

分散ロック(DLM)だのという話はここから出てくるのだけど、その実装は限りなくやってられない。おそらく効率(パフォーマンス)も良くない。


そこで、数珠つなぎ型を考える。

node0 -> node1 -> node2 -> node3

書き込みを行う際は、必ず node1 -> node2 -> node3 という順番で書き込まなければならず、前の書き込みを追い抜いてはいけない。これなら分散ロックは要らない。

さらに利点として、数珠つなぎ型の方がスループットが出る。1台->複数台だと、1本の回線の帯域が台数分の1に制限されてしまうが、数珠つなぎ型ならフルに使える。全二重の場合。
逆に、レイテンシは大きくなってしまう。そこはトレードオフ。でも良い買い物だと思う。



しかし数珠つなぎ型にも問題はある。node1とnode2の2台で冗長化にしているときに↓

node1 -> node2

node2がファイルを書き込もうとすると、

node2 -> node1 -> node2

↑こうなってしまう。ムダが多い。


この問題は、書き込む順序が決まっていることに起因する。でもそこは譲れない。書き込む順序を順不同にしてしまうと、同時に書き込まれたときに、状態が不定になる。


この解決案はまだ浮かんでいない。
しかし実際のところは、ファイルサーバーの役割をするノードは、データを書き込むことはあまりないと思われる。それならこのムダは発生しない。




別の問題として、新しいノードが加わるときにどうするか、という問題がある。


第1案として、「新しいノードは必ず数珠の一番後ろにつく」ことにする。たとえばnode2とnode3で冗長化しているときに、node1が加わると、

node2 -> node3 -> node1

になる。


この方式の良い点は、書き込みが行われている真っ最中にノードが追加されたときでも、問題なく追従できる点。書き込みは前の書き込みを追い抜かないので、ノードの追加情報は、それより後の書き込みに追い抜かれることはない。逆にノードの追加情報は、それより前の書き込みを追い抜かない。
つまり、ノードの追加中にデータが変更されることはない。同時にノードが追加されても問題ない。これは素晴らしい。


しかしこの案では、数珠を維持するために、どのノードがどの順番で繋がっているのか、ずっと覚えておかないといけない。各ノードが知らなくてはならない情報が多ければ多いほど、分散システムの実装は難しくなる。




では第2案として、「数珠のノードはIPアドレスの順に繋がる」ことにする。
node1, node2, node3のIPアドレスの大きさが、node1 < node2 < node3だとする。IPアドレスなので、同じになることはない。

そうすると、node2とnode3で冗長化されているときに、node1が加わると、↓このようになる。

node1 -> node2 -> node3

node1はIPアドレスが最小なので、先頭につく。


この方法の良い点は、数珠に繋がっているメンバーだけを覚えていれば済む点。繋がっている順番を覚えておく必要はない。
もし忘れてしまっても、「数珠Aに参加しているノードは返事してくれ!」と聞けばいい。(←実際のところ、信頼性のあるブロードキャストは実現困難なので、これは役に立たない)




なにやら現実味を帯びてきた。
さらに素晴らしいことに、9pを実装するためのライブラリというものが存在する。と言っても実装されていない部分が非常に多いが…。しかしそこは自分で実装したっていいと思う。なんと言ってもカーネルモードじゃないし、プロトコルはとてもシンプル。

そしてクライアント機能はLinuxカーネルに組み込まれていると来ている。そろそろ実装を始めてもいいかもしれない。



…一方で、次期RUNESの案も固まってきている。mDNS+DNS-SDでサービス発見 + dRuby + DIの案。

さぁ、どっちからやるんだ!