P2P分散ストレージ「Cagra」

id:nyaxt氏との共同開発の分散ストレージ「Cagra」(かぐら)のアルファ版をリリースしました。


Cagraは以下のような特徴を持った(目指した)P2P分散ストレージです。

  • Zeroconf
  • マルチマスタでレプリケーションするWrite
  • 高速な分散Read
  • オプションで高速な非同期Write
  • インターネットレベルよりもLANレベルのマシン台数に特化
  • 巨大データサポート
  • 高速イベント駆動システムコール+軽量スレッド
  • 超アジャ〜イルな開発体制


まだα版で全部が実装されているわけではないですが、とりあえず動きます。

Zeroconf

UDPマルチキャストでノードを自動的に発見するので、一切設定ファイルを書かずに動作せることができます。新たにノードを追加しても、自動的に発見・そのままネットワークに組み込まれるので、設定ファイルを常に最新版に維持する手間も必要もなく、お手軽です。
最初にすべてのノードの数やIPアドレスを把握しておく必要もないので、最初は気楽にネットワークを構築しておいても、後から簡単に拡張できます。


実際の使い方はid:takuma104さんのcagraのα版試してみたよ(ちょと使ってみた編)がとても分かりやすいです。ビルドの方法も載っています。(takuma104さん感謝!)


Global File SystemやOCFSなどのクラスタファイルシステムを使ったことがある方ならよーく分かると思われますが、設定ファイルから解放されることによる精神的お手軽感は絶大です。感動的です。


CagraのデーモンはP2Pで相互に接続しています。デーモンのどれか1つにクライアントプログラム(今のところRuby)を使って接続し、データをput/getします。クライアントはCagraデーモンとは別のホストで動かしてもOKで、ストレージサーバー群とクライアントを分離した運用もできます。

マルチマスタでレプリケーションするWrite

データを書き込むとき、複数のノードに同じデータを書き込むことで、ノードが落ちたときでもデータが失われないように信頼性を高めることができます。


このときに何台のノードにレプリケーションさせるかを指定することができ、データの種類に応じて、書き込み速度と記憶容量を優先させるか、信頼性を優先させるかを変えられます。


Cagraはここで要求された数のレプリケーションが成功することを保証し、レプリケーションできなかったらしっかりエラーを返します。逆にこの挙動を外すこともでき、そのときはWriteがどれくらい成功したかを把握することはできませんが、高速なWriteができます。

高速な分散Read

データはDHT(分散ハッシュテーブル)を使って分散保持されており、Read時にはどのノードが目的のデータを持っているのかを高速に見つけられるようになっています。
データが複数のノードにレプリケーションされていれば、自動的に負荷分散します。

IO戦略

epoll(OSによってepoll/kqueue/selectを自動選択)とcoroutine(=Fiber)を組み合わせたIO戦略を採っています。
カーネルから見ればシングルスレッドのイベント駆動で動いているのですが、coroutineを組み合わせることで、スレッドを使ったブロッキングIOをしているようなプログラミングができるようになっています。
カーネルスレッドの切り替えやmutexロックの奪い合いは発生しませんし、プログラミング中にロックを意識する必要もない上に、ブロッキングIOを連続して回しているような自然なプログラミングができるようになっています。


また、Lighttpdにあるような、tmpfsとsendfileを組み合わせた高速なIOも実装できるようなモデルになっています。

Partty!でリモートペアプロしてみた

Cagraの開発Partty!.orgを使ってリモート・ペアプログラミングをやってみたところ、かなりイイ感じだったので書いてみます。


使ったツール:

  • Partty!.org(ターミナル共有)
  • IRC(コミュニケーション等々)
  • IRCのログ(作業の記録)
  • Mercurialソースコードの同期)
  • Trac(タスク管理)
  • Wiki(議論のまとめ用)


Cagraの開発では、片方がコードを書き、片方が見ているという方法ではなく、両方がコードを書きつつ、困ったらIRCで「ここがワカラン」「ここどうしよう」とヘルプを求めるスタイルになりました。
アイディアを議論するところから、仕様を詰め、コードを書くまで、すべてIRC+Partty!一本で、トンでもない勢いで開発が進みます。


このスタイルが成功したのは、主に以下の理由があるかなーと思っています。

  • 互いに十分なプログラミングの知識がある
  • IRCのログで駆動する
  • 最初に大まかな方針を決めておいた
  • 同じエディタを使っている

互いに十分なプログラミングの知識がある

互いのコードが難なく読めるくらいにプログラミングスキルがあれば、コーディングの様子をずっと見ていなくても、どこが問題なのかすぐに通じます。バージョン管理ツール(Mercurial)のログを見たり、Partty!で直接コードを見てもらえば一発で通じます。

プログラマの共通言語はソースコード」というのは事実で、日本語で説明するよりもソースコードを見せた方が早いです。さらに言えば、口で説明するよりも直接コードを書いてしまった方が早く、互いにキーボードの前にいて、すぐにコードを入力できる状態にあるのがGoodです。


ヘルプを求められる側では、ウィンドウを切り替えるだけで相手のターミナル画面(=エディタ画面=ソースコード)が出てくるというオーバーヘッドの小ささが、集中力を妨げず、イイ感じです。

IRCのログで駆動する

一方で会話のログはIRCのログに残しておいたのは正解でした。後で「あの話は結局どうなんったんだっけ」という話になったとき、同じ議論を2度してしまうことを避けられます。


あるいは、会話になっていなくても、思考をそのままdumpするくらいの勢いでIRCに独り言を書きまくるのもアリです。同じ思考で2度迷うことを避けることができます。IRCのログを読んだ相手からヘルプがもらえたりもします。


ペアがいないときにプログラムを書くときでも、何を/どこを書いているのかをIRCにログを残しておくのがGoodです。これも思考をIRCにdumpする勢いで。後でペアがコードを書き始めたときに、同じ地点に並ぶことができます。IRCのログでペアと脳内コンテキストの同期を取るイメージ。


発見した問題はすぐにTracのチケットにしておくのも正解でした。チケットを消していく快感もまた良い。

最初に大まかな方針を決めておいた

最初に設計やプログラミングの大まかな方針を決めておいたも良かったと言えます。テキストチャットではどうしても的確に意見が伝えられずに、すれ違ってしまうことがあります(ここは要改善)。最初に大体の方針を決めておけば、それに沿って相手の意見を脳内補完できるので、大きくすれ違いにくくなったと思います。


音声チャットを使った方がいいのかもしれません。しかし音声チャットではログを検索しにくいのがイマイチ。


ちなみにCagraの大まかの方針が決まったのは、1000speakers:3の三次会にて、でした。それから86世代開発合宿でも煮詰めています。濃い議論にはテキストチャットでは不足で、やはりリアルで会って話すなどのリッチな通信手段が必要だと思われます。

同じエディタを使っている

「同じエディタを使っている」というのも実は結構重要だと思いました。相手のターミナルを操作してコードを書くときに、「emacsはワカラン!」などのオーバーヘッドがあるとヤな感じです。私もid:nyaxt氏も両方vimだったので、極めてスムーズに事が進みました。


「相手のターミナルを操作する」という状況が特殊に思われるかもしれませんが、Partty!を使っていると普通にできることで、結構頻繁に使います。「そこはこうすればいい」「こうしたらどうか」ということを口で説明するよりも、ちょっとターミナルを借りてサクっと書いてしまった方がずっと早いです。

これはペアプログラミングの「相手の教育になる」という利点から考えると良くないかもしれませんが、書いたコードがそのまま説明に代わるくらいお互いに通じてしまうレベルなら問題ナシです。



最近感じるには、プログラミングの生産性は、いかに些細なオーバーヘッドを無くすかにかかっているのかなーと思っています。モチベーションはデリケートで、ちょっとしたことですぐに削がれてしまいます。
逆に、オーバーヘッドをできる限り減らした上で、ペアプログラミングの様な緊張感がある環境があると、生産性がガツガツ上がる気もします。生産性が上がると言うか、気分が良くなります。
気分のいいコーディングが続けられるといいですねー。