最近の典型的なリレーショナル・データベースは、ある特定のタイプのアプリケーションでのパフォーマンスが低く、現在のインターネット・アプリケーションで必要とされるパフォーマンスおよびスケーラビリティーに対処するのに苦戦しています。このことから、リレーショナル・データベースとは異なる手法が求められます。ここ数年の間、リレーショナル・データベースの欠陥に直接対処する手法として、NoSQL と一般に呼ばれる新しいタイプのデータストアが広く使用されるようになってきています。そのようなタイプのデータストアの 1 つが、Riak です。
世間に出回っている NoSQL データストアは Riak だけではありません。よく使用されている NoSQL データストアには、他に MongoDB と Cassandra があります。この 2 つのデータストアは、多くの点で Riak と似ていますが、いくつか大きな違いもあります。例えば、Riak は分散型システムである一方、MongoDB は単独のシステム・データベースです (Riak にはマスター・ノードの概念がないため、耐障害性に優れています)。Cassandra は Amazon による Dynamo についての記述に基づいていますが、ベクター・クロックなどの特定の機能が省かれています。代わりに Cassandra では衝突を解決するためにタイムスタンプを使用しているので、クライアント側のクロックが同期していることが重要になります。
Riak のもう 1 つの長所は、これが Erlang で作成されていることです。MongoDB と Cassandra を作成するために使用されている言語は汎用言語と呼べますが (それぞれ C++、Java で作成されています)、Erlang はその根本から分散型耐障害性アプリケーションをサポートするように設計されています。NoSQL データストアのようなアプリケーションは、Erlang が作成される理由となった分散型耐障害性アプリケーションと共通する特性を持ちます。したがって、NoSQL データストアを開発するには、Erlang のほうが適しているというわけです。
Map/Reduce ジョブは、Erlang または JavaScript でしか作成することができません。この記事では、JavaScript で map および reduce 関数を作成することにしましたが、Erlang
でこれらの関数を作成することも可能です。Erlang コードのほうがわずかに実行速度に優れているものの、より多くのユーザーが利用しやすいという理由から JavaScript
コードを選びました。Erlang についてさらに詳しく学ぶためには、「参考文献」のリンクを参照してください。
この記事に記載するサンプル・コードを実際に試すには、お使いのシステムに Riak (「参考文献」を参照) と Erlang をインストールする必要があります。
ローカル・マシン上で稼働する 3 つのノードからなるクラスターを作成する必要もあります。Riak に保管されるデータはすべて、クラスター内の複数のノードにレプリケートされます。レプリケート先のノードの数は、データが保管されるバケットの (n_val) プロパティーによって決まります。このプロパティーのデフォルト値は 3 です。そのため、クラスターを有効にするには、少なくとも 3 つのノードを使用してクラスターを作成しなければなりません (3 つ以上であれば、好きなだけノードを使用して構いません)。
ソース・コードをダウンロードした後、そのソース・コードをビルドする必要があります。ソース・コードをビルドする際の基本的なステップは以下のとおりです。
- ソースを解凍する:
$ tar xzvf riak-1.0.1.tar.gz - ディレクトリーを変更する:
$ cd riak-1.0.1 - ビルドする:
$ make all rel
これで、Riak (./rel/riak) がビルドされます。複数のノードをローカルで実行するためには、./rel/riak のコピー (追加されるノードごとに 1 つ) を作成しなければなりません。./rel/riak のコピーを ./rel/riak2、./rel/riak3 といった具合に作成した後、各コピーを以下のように変更します。
- riakN/etc/app.config で、http{} セクションに指定されている handoff_port、そして pb_portの値を固有の値に変更します。
- riakN/etc/vm.args を開き、その名前も同じく固有のものに変更します (例えば、
-name riak2@127.0.0.1)。
以上の変更が完了したら、各ノードを順に起動します (リスト 1 を参照)。
リスト 1. 各ノードを起動する
$ cd rel $ ./riak/bin/riak start $ ./riak2/bin/riak start $ ./riak3/bin/riak start |
最後に、起動したすべてのノードを参加させてクラスターを作成します (リスト 2 を参照)。
リスト 2. クラスターを作成する
$ ./riak2/bin/riak-admin join riak@127.0.0.1 $ ./riak3/bin/riak-admin join riak@127.0.0.1 |
現時点で、3 つのノードからなるクラスターがローカルで実行されていることになります。これをテストするには、コマンド $
./riak/bin/riak-admin status | grep ring_members を実行します。
上記のステップで作成したクラスターを構成する各ノードが、ring_members :
['riak2@127.0.0.1','riak3@127.0.0.1','riak@127.0.0.1'] という形で示されます。
現在、Riak にアクセスする手段としては、HTTP API (RESTful なインターフェース)、プロトコル・バッファー、およびネイティブ Erlang インターフェースの 3 つがあります。このように複数のインターフェースがあると、アプリケーションをどのように統合するかを選択できるというメリットがもたらされます。例えば、アプリケーションを Erlang で作成した場合、ネイティブ Erlang インターフェースを使って、このアプリケーションと Riak とを密接に統合するのが妥当でしょう。使用するインターフェースを決定する際には、パフォーマンスなどの他の要素が関わってくることもあります。例えば、プロトコル・バッファーをインターフェースとして使用するクライアントは、HTTP API を使用してやりとりするクライアントよりもパフォーマンスに優れています。プロトコル・バッファーでは HTTP API と比べ、通信するデータが少ないこと、そして HTTP ヘッダーのすべてを構文解析するのはパフォーマンス面で (比較的) コストがかかることが、その理由です。その一方、HTTP API には、最近の開発者のほとんど (特に Web 開発者) は RESTful なインターフェースを使い慣れているだけでなく、ほとんどのプログラミング言語には (例えば URL をオープンするために) HTTP でリソースをリクエストするためのプリミティブが組み込まれているため、追加のソフトウェアが必要ないというメリットがあります。このことから、この記事では HTTP API に焦点を当てて説明します。
記事で取り上げる例では共通して、Riak の HTTP インターフェースを介して Riak を操作する際に curl を使用します。これは単に、使用する HTTP API をよく理解してもらうためです。各種の言語でさまざまなクライアント・ライブラリーが用意されているので、Riak をデータストアとして使用するアプリケーションを開発するときには、これらのクライアント・ライブラリーのいずれかを使用することを検討してください。クライアント・ライブラリーには、Riak を簡単にアプリケーションに統合できるようにするための API が用意されているので、curl を使用する場合に見られるようなレスポンスを処理するためのコードを自分で作成する必要がなくなります。
Riak の API は通常の HTTP メソッドをサポートするので、オブジェクトを取得、更新、作成、削除するには、それぞれ GET、PUT、POST、DELETE を使用します。この後、これらのメソッドについて順に説明します。
Riak はいわば、キー (ストリング) から値 (オブジェクト) への分散型マップを実装するものだと考えてください。Riak が値を保管する先となるのはバケットですが、オブジェクトを保管する前に、明示的にバケットを作成する必要はありません。オブジェクトの保管先となるバケットが存在しない場合には、そのバケットが自動的に作成されます。
バケットは Riak での仮想概念であり、その主な存在目的は、関連する複数のオブジェクトをグループ化することです。バケットにもプロパティーがあり、これらのプロパティーの値が、そのバケットに保管されたオブジェクトに対する Riak の処理内容を定義します。例えば、バケットには以下のプロパティーがあります。
n_val— オブジェクトをクラスター間でレプリケートする回数allow_mult— 同時更新操作を許可するかどうか
バケットに対して GET リクエストを実行することで、そのバケットのプロパティー (およびそれらの現在の値) を確認することができます。
オブジェクトを保管するには、リスト 3 に記載する URL のいずれかに対して HTTP POST リクエストを実行します。
リスト 3. オブジェクトを保管する
POST -> /riak/<bucket> (1) POST -> /riak/<bucket>/<key> (2) |
キーは、Riak によって自動的に割り当てる (1) ことも、ユーザーが定義する (2) こともできます。
ユーザー定義のキーを割り当ててオブジェクトを保管する場合、(2) に対して HTTP PUT リクエストを行って、そのオブジェクトを作成するという手段もあります。
最新バージョンの Riak では、/buckets/<bucket>/keys/<key> というフォーマットの URL もサポートされるようになっていますが、この記事では前のバージョンの Riak との後方互換性が保たれるように、以前のフォーマットを使用します。
キーが指定されていなければ、Riak が自動的にオブジェクトにキーを割り当てます。一例として、キーを明示的に指定せずに、プレーン・テキストのオブジェクトを「foo」というバケットに保管します (リスト 4 を参照)。
リスト 4. キーを指定せずにプレーン・テキストのオブジェクトを保管する
$ curl -i -H "Content-Type: plain/text" -d "Some text" \ http://localhost:8098/riak/foo/ HTTP/1.1 201 Created Vary: Accept-Encoding Location: /riak/foo/3vbskqUuCdtLZjX5hx2JHKD2FTK Content-Type: plain/text Content-Length: ... |
Location ヘッダーを調べると、Riak がこのオブジェクトに割り当てたキーがわかります。自動的に割り当てられるキーはあまり覚えやすいキーではないので、代わりの方法としてユーザーがキーを指定することができます。今度は artists というバケットを作成して、Bruce という名前のアーティストを追加します (リスト 5 を参照)。
リスト 5. artists バケットを作成してアーティストを追加する
$ curl -i -d '{"name":"Bruce"}' -H "Content-Type: application/json" \
http://localhost:8098/riak/artists/Bruce
HTTP/1.1 204 No Content
Vary: Accept-Encoding
Content-Type: application/json
Content-Length: ...
|
指定したキーを使用してオブジェクトが正しく保管されると、サーバーから「204 No Content」というレスポンスを受け取ります。
上記の例では、オブジェクトの値を JSON として保管していますが、プレーン・テキストやその他のフォーマットでも同じく簡単に保管することができます。重要な点として、オブジェクトを保管するときには、Content-Type ヘッダーを正しく設定してください。例えば JPEG の画像を保管する場合には、コンテンツ・タイプを image/jpeg に設定する必要があります。
保管されているオブジェクトを取得するには、バケットに対して、取得するオブジェクトのキーを使用して GET
リクエストを実行します。オブジェクトが存在すれば、そのオブジェクトがレスポンスの本体に返されます (リスト 6 を参照)。存在しなければ、サーバーから「404 Object Not Found」というレスポンスが返されます。
リスト 6. バケットに対して
GET を実行する
$ curl http://localhost:8098/riak/artists/Bruce
HTTP/1.1 200 OK
...
{ "name" : "Bruce" }
|
オブジェクトを更新する場合は、オブジェクトを保管するときと同じく、Content-Type ヘッダーが必要です。リスト 7 に Bruce のニックネームを追加する例を示します。
リスト 7. Bruce のニックネームを追加する
$ curl -i -X PUT -d '{"name":"Bruce", "nickname":"The Boss"}' \
-H "Content-Type: application/json" http://localhost:8098/riak/artists/Bruce
|
前述したように、Riak はバケットを自動的に作成します。バケットにはプロパティーがあり、そのうちの allow_mult プロパティーが、同時書き込み処理を許可するかどうかを決定します。デフォルトでは、このプロパティーは false に設定されます。同時更新処理が許可されている場合には、更新を行うたびに、X-Riak-Vclock ヘッダーも送信する必要があります。このヘッダーの値は、クライアントがそのオブジェクトを前回読み取ったときの値に設定しなければなりません。
Riak は、オブジェクトに対する変更の理由を判断するためにベクター・クロックを使用します。ベクター・クロックが機能する仕組みについては、この記事では説明しませんが、同時書き込み処理が許可されている場合には衝突が発生する可能性があるため、そのような衝突の解決がアプリケーションに任されることになるとだけ言っておきます (「参考文献」を参照)。
オブジェクトを削除する場合のコマンドは、これまで説明したコマンドと同様のパターンに従います。つまり、削除対象のオブジェクトに対応する URL に対し、例えば $ curl
-i -X DELETE http://localhost:8098/riak/artists/Bruce のような HTTP DELETE を実行するだけで、オブジェクトを削除することができます。
オブジェクトが正常に削除されると、「204 No Content」というレスポンスをサーバーから受け取ります。削除しようとしているオブジェクトが存在しない場合、サーバーからは「404 Object Not Found」レスポンスが返されます。
ここまでは、オブジェクトを後で取得できるように、特定のキーを関連付けてオブジェクトを保管する方法を説明してきました。この単純なモデルを拡張して、オブジェクト間の関係 (およびその有無) を表現できたとしたら、役に立つはずです。オブジェクト間の関係を表現することは可能であり、Riak ではそのために、リンクを使用します。
ではリンクとは何でしょうか?リンクを使用すると、ユーザーはオブジェクト間の関係を作成することができます。UML クラス図に馴染みがある読者の方は、リンクとは、ラベルでその関係を記述するオブジェクト間の関連と考えることができます。リレーショナル・データベースの場合で考えると、関係は外部キーによって表現されます。
リンクは「Link」ヘッダーによってオブジェクトに「アタッチ」されます。以下のリストに、リンク・ヘッダーの例が示されています。関係のターゲット
(例えば、リンク先とするオブジェクト) は、不等号括弧で囲まれています。関係のタイプ (以下の例では “performer”) は、riaktag
プロパティーで表現されます。以下の例では、リンク・ヘッダーは Link: </riak/artists/Bruce>;
riaktag="performer" となっています。
今度はアルバムを追加して、これらのアルバムで演奏しているアーティスト Bruce をアルバムに関連付けます (リスト 8 を参照)。
リスト 8. アルバムを追加する
$ curl -H "Content-Type: text/plain" \ -H 'Link: </riak/artists/Bruce> riaktag="performer"' \ -d "The River" http://localhost:8098/riak/albums/TheRiver $ curl -H "Content-Type: text/plain" \ -H 'Link: </riak/artists/Bruce> riaktag="performer"' \ -d "Born To Run" http://localhost:8098/riak/albums/BornToRun |
いくつかの関係を設定したので、早速リンク・ウォーキングによって関係を照会してみます。リンク・ウォーキングとは、オブジェクト間の関係を照会するプロセスに付けられた名前です。例えば、アルバム「The
River」で演奏していたアーティストを検索するには、$ curl -i
http://localhost:8098/riak/albums/TheRiver/artists,performer,1 を実行します。
末尾のビットは、リンク仕様です。リンク・クエリーの構成内容を説明すると、最初の部分 (artists)
でクエリーの実行対象を限定するバケットを指定し、2 番目の部分 (performer)
で結果を絞り込むために使用するタグを指定します。そして最後の部分 (1) で、クエリーのこの特定のフェーズの結果を含めることを指示します。
推移的関係のクエリーを実行することも可能です。例えば、アルバムとアーティストとの間の関係を図 1 に示すように設定したとします。
図 1. アルバムとアーティストとの関係の例
上記のように関係が設定されていれば、「The River を演奏したアーティストとコラボレーションしたアーティストは誰か?」というクエリーとして、$ curl -i
http://localhost:8098/riak/albums/TheRiver/artists,_,0/artists,collaborator,1 を実行することができます。リンク仕様に含まれるアンダーバーはワイルドカード文字のような役割をし、どのような関係であるかは無視することを意味します。
Map/Reduce は、大規模なデータ・セットでの分散型計算を並行して実行するためのフレームワークとして Google によって広められました。Riak も Map/Reduce をサポートし、クラスターに保管されたデータに対して、さらに強力なクエリーを実行できるようになっています。
Map/Reduce 関数は、map フェーズと reduce フェーズの 2 つのフェーズからなります。map フェーズはデータの一部に適用されて、ゼロ個以上の結果が生成されます。これは、関数型プログラミングの場合で言うと、「リスト内の各項目に関数をマッピングすること」に相当します。map フェーズは並行して行われます。reduce フェーズは、map フェーズによって生成されたすべての結果を取得して、それらを 1 つに結合します。
例えば、大量の文書セットで、単語の各インスタンスの数をカウントする場合を考えてください。この場合、それぞれの文書の中で各単語が何回出現したかが map フェーズごとに計算されます。これらの小計が計算されると、reduce 関数に送信されて合計が計算され、文書セット全体の結果が出力されるという仕組みです。Google による Map/Reduce の資料へのリンクが「参考文献」にあるので参照してください。
この記事のために、これから Riak に保管された文書のセットで分散型 grep を行う Map/Reduce 関数を開発します。grep と同じように、この関数では最終的な結果として、指定されたパターンと一致する一連の行が出力されるようにします。さらに、それぞれの結果には、文書内でパターンと一致した行の番号も示します。
Map/Reduce クエリーを実行するために、/mapred リソースに対して POST
リクエストを実行します。リクエストの本体は、クエリーの JSON 表現です。前の例と同じく、Content-Type ヘッダーが必要であり、このヘッダーは常に
application/json に設定する必要があります。リスト 9 に分散型 grep を行うために実行するクエリーを記載し、その後、クエリーの各構成部分について順に説明します。
リスト 9. Map/Reduce クエリーの例
{
"inputs": [["documents","s1"],["documents","s2"]],
"query": [
{ "map": {
"language": "javascript",
"name": "GrepUtils.map",
"keep": true,
"arg": "[s|S]herlock" }
},
{ "reduce": { "language": "javascript", "name": "GrepUtils.reduce" } }
]
}
|
各クエリーは、例えば計算を行う文書のセット、map フェーズと reduce フェーズで実行する関数の名前などの入力で構成されています。名前の代わりにソース・プロパティーを使って、map 関数と reduce 関数の両方のソースをクエリーに直接インラインで組み込むこともできますが、ここではその方法を採りません。Riak のデフォルト構成を変更するために必要な名前付き関数を使用するためです。リスト 10 のコードを任意のディレクトリーに保存してください。クラスター内の各ノードで etc/app.config ファイルを見つけてそれを開き、js_source_dir プロパティーをこのコードの保存先ディレクトリーに設定します。この設定の変更を適用するには、クラスター内のすべてのノードを再起動する必要があります。
リスト 10 に、map フェーズと reduce フェーズで実行される関数を含むコードを記載します。map
関数は文書の各行を調べ、その行が指定のパターン (arg パラメーター) と一致するかどうかを判別します。reduce 関数は、この特定の例では大した処理を行いません。恒等関数のように振る舞って、入力をそのまま返すだけです。
リスト 10. GrepUtils.js
var GrepUtils = {
map: function (v, k, arg) {
var i, len, lines, r = [], re = new RegExp(arg);
lines = v.values[0].data.split(/\r?\n/);
for (i = 0, len = lines.length; i < len; i += 1) {
var match = re.exec(lines[i]);
if (match) {
r.push((i+1) + “. “ + lines[i]);
}
}
return r;
},
reduce: function (v) {
return [v];
}
};
|
このクエリーを実行するには、あらかじめデータを用意しておかなければなりません。そのために、Project Gutenberg Web サイト (「参考文献」を参照) から「Sherlock Holmes」の電子書籍をいくつかダウンロードしておきました。最初のテキストは「documents」バケットにキー「s1」で保管され、2 番目のテキストはこの同じバケットにキー「s2」で保管されます。
このような文書を Riak にロードする方法の一例をリスト 11 に示します。
リスト 11. 文書を Riak にロードする
$ curl -i -X POST http://localhost:8098/riak/documents/s1 \ -H “Content-Type: text/plain” --data-binary @s1.txt |
文書のロードが完了したら、これらの文書を検索することができます。この例では、正規表現 “[s|S]herlock”
と一致するすべての行を出力します (リスト 12 を参照)。
リスト 12. 文書を検索する
$ curl -X POST -H "Content-Type: application/json" \
http://localhost:8098/mapred --data @-<<\EOF
{
"inputs": [["documents","s1"],["documents","s2"]],
"query": [
{ "map": {
"language":"javascript",
"name":"GrepUtils.map",
"keep":true,
"arg": "[s|S]herlock" }
},
{ "reduce": { "language": "javascript", "name": "GrepUtils.reduce" } }
]
}
EOF
|
文書で grep 検索するパターンは、クエリー内の arg プロパティーに格納します。この値が、arg
パラメーターとして map 関数に渡されます。
リスト 13 に、サンプル・データでMap/Reduce ジョブを実行した結果、生成された出力を記載します。
リスト 13. Map/Reduce ジョブの実行による出力例
[["1. Project Gutenberg's The Adventures of Sherlock Holmes, by Arthur Conan Doyle","9. Title: The Adventures of Sherlock Holmes","62. To Sherlock Holmes she is always THE woman. I have seldom heard","819. as I had pictured it from Sherlock Holmes' succinct description,","1017. \"Good-night, Mister Sherlock Holmes.\"","1034. \"You have really got it!\" he cried, grasping Sherlock Holmes by" …]] |
Map/Reduce に関するセクションの締めくくりとして、Riak に用意されている Map/Reduce のストリーミング機能について簡単に取り上げます。この機能は、map フェーズが完了するまでに長い時間がかかるジョブで効果を発揮します。結果をストリーミングすることで、各 map フェーズで結果が使用可能になると同時に、reduce フェーズが実行される前に、それらの結果にアクセスできるようになるからです。
このストリーミング機能は、分散型 grep クエリーにも効果的に適用することができます。この例では、reduce ステップで行う処理はほとんどありません。実際、reduce
フェーズを完全に取り除き、各 map フェーズの結果をそのまま直接クライアントに出力することも可能です。そのためには、クエリーを変更して reduce ステップを削除し、URL
の最後に ?chunked=true
を追加する必要があります。この変更によって、結果をストリーミングする必要があることを示します (リスト 14 を参照)。
リスト 14. クエリーを変更して結果をストリーミングする
$ curl -X POST -H "Content-Type: application/json" \
http://localhost:8098/mapred?chunked=true --data @-<<\EOF
{
"inputs": [["documents","s1"],["documents","s2"]],
"query": [
{ "map": {
"language": "javascript",
"name": "GrepUtils.map",
"keep": true, "arg": "[s|S]herlock" } }
]
}
EOF
|
これで、各 map フェーズが完了すると同時に、map フェーズごとの結果 (この例では、クエリー・ストリングと一致する行) がクライアントに返されるようになります。この手法は、クエリーの中間結果が使用可能になると同時にその結果を処理しなければならないアプリケーションに役立ちます。
Riak は、記事「Amazon's Dynamo」で説明されている原理に基づいた、オープンソースの極めてスケーラブルなキー・バリュー型データストアです。Riak は、デプロイするのもスケーリングするのも簡単で、ノードをクラスターにシームレスに追加することができます。リンク・ウォーキングなどの機能や、Map/Reduce のサポートにより、複雑なクエリーにも対応することができます。HTTP API の他、ネイティブ Erlang API もあり、プロトコル・バッファーもサポートされています。この連載の第 2 回では、さまざまな言語で使用できる各種のクライアント・ライブラリーについて探り、Riak を極めてスケーラブルなキャッシュとして使用する方法を紹介します。
学ぶために
- 「Riak 入門: 第 2 回 Web アプリケーションの頼れるキャッシング・サーバーとして Riak を統合する: Riak をキャッシング・サーバーとして使用して、アプリケーションおよびデータベース・サーバーの負荷を軽減する」(Simon Buckle 著、developerWorks、2012年5月): 負荷の高い Web サイトのキャッシング・ソリューションとして Rjak を使用することで、アプリケーションおよびデータベース・サーバーの負荷を軽減、あるいは取り除いてください。
- 3 つのノードからなるクラスターをセットアップする方法について詳しくは、「Basic Cluster Setup」および「Building a Development Environment」を参照してください。
- Google の「MapReduce: Simplified Data Processing on Large Clusters」を読んでください。
- 連載「Erlang プログラミング入門」(Martin Brown 著、developerWorks、2011年5月) では、Erlang の関数型プログラミング・スタイルと他のプログラミング・パラダイム (命令型、手続き型、そしてオブジェクト指向のプログラミングなど) との違いを説明しています。
- Riak がベースとしている「Amazon's Dynamo」に関する記事を読んでください。一読することをぜひお勧めします!
- 記事「Analyze Apache Logs with Riak」を読んで、Riak を使ってサーバー・ログを処理する方法を学んでください。
- ベクター・クロックについて、その概要と、思っていたよりも簡単に理解できる理由を説明しているブログを読んでください。
- Riak のウィキで、ベクター・クロックについてのわかりやすい説明と、リンク・ウォーキングの詳しい説明を読んでください。
- 実験用にテキスト・リソースが必要な場合には、Project Gutenberg サイトが絶好のソースです。
- developerWorks Open source には、オープンソース技術を IBM 製品で使用して開発する際に役立つハウツー情報、ツール、プロジェクト更新が広範に揃っています。
- developerWorks Web development に、多種多様な Web ベースのソリューションを話題にした記事が揃っています。
- developerWorks podcasts ではソフトウェア開発者のための興味深いインタビューや議論を聞くことができます。
- Twitter で developerWorks をフォローしてください。
- developerWorks demos で、初心者向けの製品のインストールおよびセットアップから熟練開発者向けの高度な機能に至るまで、さまざまに揃ったデモを調べてください。
製品や技術を入手するために
- basho.com から Riak をダウンロードしてください。
- Erlang プログラミング言語をダウンロードしてください。
- 開発者向けのソフトウェアを使用して、次のオープンソース開発プロジェクトを革新してください。IBM 試用版ソフトウェアは、ダウンロードまたは DVD で入手できます。
議論するために
- developerWorks コミュニティーで、他の developerWorks ユーザーとつながりを持つとともに、開発者によるブログ、フォーラム、グループ、ウィキを調べてください。さらに、実際のオープンソース・グループの構築を手伝ってください。

Simon Buckle は独立したコンサルタントです。分散型システム、アルゴリズム、並行性に関心を持っています。インペリアル・カレッジ・ロンドンでコンピューティングの修士号を取得しました。彼の Web サイトには simonbuckle.com からアクセスできます。