S-JIS[2010-06-06/2010-06-12] 変更履歴

Voldemort read-only Hadoop

Voldemortストア(テーブル)定義では、読込専用(readonly)を指定することが出来る。


概要

Voldemortストア(テーブル)定義では、読込専用(readonly)を指定することが出来る。
更新が無いので、効率よいアルゴリズムが使えるから高速になるということだと思う。

readonlyの説明(Javadocとか)では触れられていないが、読込専用のデータを作るには、データ作成用のプログラムを動かす必要があるようだ。このプログラムがHadoopを利用するようになっている。
Hadoopの出力はキーと値という形式であり、KVSであるVoldemortもキーと値を扱う。つまりHadoopの出力がそのままKVSに適用できるということなのかもしれない。

以下、Hadoopのチュートリアルでよく出てくるWordCountの出力結果を読込専用ストアにしてみる。

  1. 読込専用ストアを定義しておく。
  2. WordCountを実行する。
  3. WordCountの実行結果をHadoopのSequenceFileに変換する。
    変換にはHadoop(Map/Reduce)を使うので、変換用Mapperだけ自作する必要がある。
  4. SequenceFileをストアに取り込む

読込専用ストアの定義

まず、読込専用のストアを定義しておく必要がある。

VOLDEMORT_INSTALL/config/single_node_cluster/stores.xml

<stores>
〜
  <store>
    <name>wordcount1</name>
    <persistence>read-only</persistence>
    <routing>client</routing>
    <replication-factor>1</replication-factor>
    <required-reads>1</required-reads>
    <required-writes>1</required-writes>
    <key-serializer>
      <type>string</type>
    </key-serializer>
    <value-serializer>
      <type>json</type>
      <schema-info>"int32"</schema-info>
    </value-serializer>
  </store>
</stores>

WordCountの出力結果は単語(文字列)とその個数(数値)なので、ストアの型もそれに合わせる。
(実際にはWordCountの出力はタブ区切りの文字列だから、個数の方も文字列でも構わないんだろうけど。ストアを使う人から見れば、数値型の方が分かりやすいんじゃないかな)

また、読込専用ストアを使う為には、それを扱うクラスを有効にしておく必要がある。

VOLDEMORT_INSTALL/config/single_node_cluster/server.properties

〜
enable.readonly.engine=true

file.fetcher.class=voldemort.store.readonly.fetcher.HdfsFetcher

storage.configs=voldemort.store.bdb.BdbStorageConfiguration, voldemort.store.readonly.ReadOnlyStorageConfiguration

HdfsFetcherを入れておかないと、swap-store.shでHDFSを扱うことが出来ない。


ストアを定義してVoldemortを再起動すれば、ファイルシステム上に保存領域は勝手に作られる。
したがって、クライアントからアクセスすることは出来る。が、最初は何を取ってきてもnullだし、書き込むことも出来ない。

bin> voldemort-shell.bat wordcount1 tcp://localhost:6666
> get "Hello"
null
> put "Hello" 123
Exception thrown during operation.
voldemort.store.InsufficientOperationalNodesException: No master node succeeded!

↓サーバー側のログ

Exception in thread "voldemort-server-0" java.lang.UnsupportedOperationException
: Put is not supported on this store, it is read-only.
	at voldemort.store.readonly.ReadOnlyStorageEngine.put(ReadOnlyStorageEngine.java:381)

元データの作成

Hadoopを起動し、WordCountを実行して、ストアに保存する元データを作成する。
(まぁたぶん、読込専用ストアを試したいだけなら、わざわざWordCountを実行しなくても、それらしい出力結果ファイルを手で作ればいいだけだけど^^;)

手順 Cygwinから実行するコマンド
Hadoopの起動
$ cd /usr/local/hadoop
$ bin/start-all.sh
WordCountの実行準備
$ cd /home/hadoop/tutorial
$ hadoop fs -put input/ wordcount/input
$ hadoop fs -ls wordcount/
Found 1 items
drwxr-xr-x - hishidama supergroup 0 2010-06-06 18:43 /user/hishidama/wordcount/input
WordCountの実行
$ hadoop jar wordcount.jar jp.hishidama.hadoop.tutorial.WordCount wordcount/input wordcount/output
WordCountの結果確認
$ hadoop fs -ls wordcount/
Found 2 items
drwxr-xr-x - hishidama supergroup 0 2010-06-06 18:43 /user/hishidama/wordcount/input
drwxr-xr-x - hishidama supergroup 0 2010-06-06 18:47 /user/hishidama/wordcount/output

読込クラスの作成

ストアへ取り込むプログラムでは、元データ(今回の例では、WordCountの出力結果)を読み込むクラスを自作する必要がある。
元データのファイル内の形式からストアに取り込む形式に自動的に変換することは出来ないから。

読み込みにはHadoopが使われるので、Mapperを作成することになる。

package jp.hishidama.sample.voldemort;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;

import voldemort.store.readonly.mr.AbstractHadoopStoreBuilderMapper;
public class WordCountImportMapper extends AbstractHadoopStoreBuilderMapper<LongWritable, Text> {

	@Override
	public Object makeKey(LongWritable key, Text value) {
		String line = value.toString();
		String word = line.split("\t")[0];
		return word;
	}

	@Override
	public Object makeValue(LongWritable key, Text value) {
		String line = value.toString();
		String count = line.split("\t")[1];
		return Integer.valueOf(count);
	}
}
コンパイルする為に必要なライブラリー
jarファイル 備考
VOLDEMORT_INSTALL/dist/ voldemort-contrib-0.80.2.jar AbstractHadoopStoreBuilderMapper
Eclipseのソースの添付は「C:/voldemort-0.80.2/contrib」
VOLDEMORT_INSTALL/contrib/hadoop-store-builder/lib/ hadoop-0.18.1-core.jar Hadoop本体(LongWritableやText等)
実際に実行に使うHadoopがこのバージョンでないなら、
対応したバージョン(HADOOP_HOMEにあるjarファイル)
を使うべき。[2010-06-12]

AbstractHadoopStoreBuilderMapperがファイルを読み込むMapper。
型引数は、ファイルを読み込んだ際の出力型なので、TextInputFormatと同じくLongWritableとTextになる。

ファイルの行ごとにmakeKey()makeValue()が呼ばれるので、ストアに格納したいキーと値の型に分割・変換して返す。
WordCountの出力結果はタブ区切りで単語と個数になっているので、makeKey()では単語を、makeValue()では個数部分を切り出している。
そして、個数は今回のストア定義では数値型(int32)にしているので、Integerに変換して返している。

実際に出力される際には、ストア定義のkey-serializervalue-serializerに従って自動的にシリアライズされる。
(上記の例では、makeValue()はIntegerを返しているが、value-serializerに則ってJSONのint32に変換される)


これを実行する際には(Hadoopのジョブだから)jarファイルになっている必要があるので、jarファイル化しておく。

作成する場所をVOLDEMORT_INSTALL/libの下にしておけば、自動的に起動シェル(起動バッチ)で読み込まれる。
また、起動時の引数でjarファイルの場所を指定することも出来る。
(Hadoopの分散環境で実行させることを考えれば、後者の方がいいだろう)

とりあえず「C:/cygwin/home/hadoop/tutorial/voldemort-sample.jar」としてみた。


読み込みデータ(SequenceFile)の作成

読込専用ストアへ読み込ませるデータ(SequenceFile)を作成するには、hadoop-build-readonly-store.shを使う。
内部でHADOOP_HOME/bin/hadoopシェルを呼び出している為、Windowsから実行する際もバッチファイルにはせず、Cygwinから実行する。

環境変数HADOOP_CLASSPATHを使ってVoldemortのクラスパスを指定している為、別のシェル(HADOOP_HOME/conf/hadoop-env.shとか)でHADOOP_CLASSPATHを上書きしない(追加する)ようにしなければならない。

指定する引数input・output・tmpdirは、Hadoopの環境による。つまり単独環境ならローカルディレクトリーだし、擬似分散環境ならHDFSになる。

$ export HADOOP_HOME=/usr/local/hadoop
$ cd /cygdrive/c/voldemort-0.80.2/

$ bin/hadoop-build-readonly-store.sh \
--input wordcount/output \
--output voldemort/wordcount \
--tmpdir hadoop-readonly-tmp \
--mapper jp.hishidama.sample.voldemort.WordCountImportMapper \
--jar $(cygpath -m /home/hadoop/tutorial/voldemort-sample.jar) \
--cluster config/single_node_cluster/config/cluster.xml \
--storename wordcount1 \
--storedefinitions config/single_node_cluster/config/stores.xml \
--chunksize 1073741824 \
--checksum md5
2010/06/06 21:56:32 WARN cluster.Node: admin-port not defined for node:0 using default value(socket_port + 1):6667
2010/06/06 21:56:33 INFO mr.HadoopStoreBuilder: Data size = 26253, replication factor = 1, numNodes = 1, chunk size = 1073741824, num.chunks = 1
2010/06/06 21:56:33 INFO mr.HadoopStoreBuilder: Number of reduces: 1
2010/06/06 21:56:33 INFO mr.HadoopStoreBuilder: Building store...
2010/06/06 21:56:34 INFO mapred.FileInputFormat: Total input paths to process : 1
2010/06/06 21:56:34 INFO mapred.JobClient: Running job: job_201006062155_0001
2010/06/06 21:56:35 INFO mapred.JobClient: map 0% reduce 0%
2010/06/06 21:57:00 INFO mapred.JobClient: map 50% reduce 0%
2010/06/06 21:57:03 INFO mapred.JobClient: map 100% reduce 0%
2010/06/06 21:57:18 INFO mapred.JobClient: map 100% reduce 100%
2010/06/06 21:57:23 INFO mapred.JobClient: Job complete: job_201006062155_0001
$ hadoop fs -ls
drwxr-xr-x - hishidama supergroup 0 2010-06-06 21:57 /user/hishidama/hadoop-readonly-tmp
drwxr-xr-x - hishidama supergroup 0 2010-06-06 21:57 /user/hishidama/voldemort
drwxr-xr-x - hishidama supergroup 0 2010-06-06 21:56 /user/hishidama/wordcount

$ hadoop fs -ls voldemort/wordcount
Found 1 items
drwxr-xr-x - hishidama supergroup 0 2010-06-06 21:57 /user/hishidama/voldemort/wordcount/node-0

読み込み(データ格納)の実行

VOLDEMORT_INSTALL/bin/swap-store.bat

@rem Voldemort swap-storeバッチ by ひしだま 2010-06-06
@echo off
setlocal

set base_dir=%~dp0..

set CLASSPATH=%CLASSPATH%;%base_dir%/dist/*
set CLASSPATH=%CLASSPATH%;%base_dir%/lib/*
set CLASSPATH=%CLASSPATH%;%base_dir%/dist/resources

java -Xmx128M -cp "%CLASSPATH%" voldemort.store.readonly.StoreSwapper %*

↓コマンドプロンプトから実行

> cd c:\voldemort-0.80.2
> bin\swap-store.bat ^
--cluster config/single_node_cluster/config/cluster.xml ^
--file hdfs://localhost:9000/user/hishidama/voldemort/wordcount ^
--name wordcount1
[2010-06-12 04:27:39,062 voldemort.cluster.Node] WARN admin-port not defined for node:0 using default value(socket_port + 1):6667
[2010-06-12 04:27:39,187 voldemort.store.readonly.StoreSwapper] INFO Invoking fetch for node 0 for hdfs://localhost:9000/user/hishidama/voldemort/wordcount/node-0
[2010-06-12 04:27:41,156 voldemort.store.readonly.StoreSwapper] INFO Fetch succeeded on node 0
[2010-06-12 04:27:41,156 voldemort.store.readonly.StoreSwapper] INFO Attempting swap for node 0 dir = C:\WINDOWS\TEMP\hdfs-fetcher\hdfs-fetcher\wordcount1_1276284461046\node-0
[2010-06-12 04:27:41,187 voldemort.store.readonly.StoreSwapper] INFO Swap succeeded for node 0
[2010-06-12 04:27:41,187 voldemort.store.readonly.StoreSwapper] INFO Swap succeeded on all nodes in 2 seconds.
> bin\voldemort-shell.bat wordcount1 tcp://localhost:6666
[2010-06-12 04:29:13,171 voldemort.client.DefaultStoreClient] INFO bootstrapping metadata.
Established connection to wordcount1 via tcp://localhost:6666
> get 'Hello'
version(): 3

--fileは、hadoop-build-readonly-store.shで指定した出力先(--output)の場所を絶対URI形式で指定する。
(ここでHDFSを使うので、server.propertiesでHdfsFetcherを指定しておく必要がある)

また、サーバー側でUNIXコマンド(whoami)を使っているので、voldemort-server.batを(コマンドプロンプトやエクスプローラーからでなく)Cygwinから起動しておく。

$ /cygdrive/c/voldemort-0.80.2/bin/voldemort-server.bat

swap-storeを実行すると、--fileで指定されたディレクトリー(HDFS)をローカルのTEMP/hdfs-fetcher/hdfs-fetcher/〜にコピーし、それをストアの実体に反映させているようだ。[2010-06-12]
反映後、「node-0」というディレクトリーは削除される。(「TEMP/〜/wordcount1_*」は残る…空っぽだから要らんのに(苦笑))


以下のようなエラーが出る場合、Hadoopのバージョンが違っているせいかもしれない。[/2010-06-12]

Exception in thread "main" voldemort.VoldemortException: voldemort.VoldemortException:
 Swap request on node 0 (http://localhost:8081/read-only/mgmt) failed: Error while performing operation: Call failed on local exception

とりあえずはHDFSから直接読み込むのではなく、一旦ローカルディレクトリーに移したら出来るようだが。

$ mkdir -p /home/hadoop/voldemort
$ cd /home/hadoop/voldemort

$ hadoop fs -get voldemort/wordcount .

$ ls -lR
〜
./wordcount/node-0:
total 3
-rwxrwxrwx 1 hishidama なし  45 2010-06-12 03:26 0.data
-rwxrwxrwx 1 hishidama なし 100 2010-06-12 03:26 0.index
-rwxrwxrwx 1 hishidama なし  16 2010-06-12 03:26 md5checkSum.txt
> cd c:\voldemort-0.80.2
> bin\swap-store.bat ^
--cluster config/single_node_cluster/config/cluster.xml ^
--file C:/cygwin/home/hadoop/voldemort/wordcount ^
--name wordcount1
[2010-06-12 03:41:49,640 voldemort.cluster.Node] WARN admin-port not defined for node:0 using default value(socket_port + 1):6667
[2010-06-12 03:41:49,765 voldemort.store.readonly.StoreSwapper] INFO Invoking fetch for node 0 for C:/cygwin/home/hadoop/voldemort/wordcount/node-0
[2010-06-12 03:41:49,921 voldemort.store.readonly.StoreSwapper] INFO Fetch succeeded on node 0
[2010-06-12 03:41:49,921 voldemort.store.readonly.StoreSwapper] INFO Attempting swap for node 0 dir = C:\WINDOWS\TEMP\hdfs-fetcher\hdfs-fetcher\wordcount1_1276281709875\node-0
[2010-06-12 03:41:49,953 voldemort.store.readonly.StoreSwapper] INFO Swap succeeded for node 0
[2010-06-12 03:41:49,953 voldemort.store.readonly.StoreSwapper] INFO Swap succeeded on all nodes in 0 seconds.

> bin\voldemort-shell.bat wordcount1 tcp://localhost:6666
[2010-06-12 03:43:13,171 voldemort.client.DefaultStoreClient] INFO bootstrapping metadata.
Established connection to wordcount1 via tcp://localhost:6666
> get 'Hello'
version(): 3

Hadoopのバージョン

VoldemortにはVOLDEMORT_INSTALL/contrib/hadoop-store-builder/libにHadoopのjarファイルが入っているのだが、
Voldemort0.80.2の場合、hadoop-0.18.1-core.jarとなっている。[2010-06-12]
実行しているHadoopがそのバージョンでない場合(今回はHadoop0.20.2を使っていた)、バージョンが不一致なので何かが不整合になって例外が発生するようだ。

したがって、Voldemort起動用のVOLDEMORT_INSTALL/bin/voldemort-server.bat(voldemort-server.sh)のHadoopのライブラリーを、実行するバージョンのHadoopに置き換える必要がある。

  voldemort-server.sh voldemort-server.bat
変更前
for file in $base_dir/contrib/hadoop-store-builder/lib/*.jar;
do
  CLASSPATH=$CLASSPATH:$file
done
set CLASSPATH=%CLASSPATH%;%base_dir%/contrib/hadoop-store-builder/lib/*
変更後
for file in $HADOOP_HOME/*.jar;
do
  CLASSPATH=$CLASSPATH:$file
done
ちなみにJREのバージョンは1.6だから、
javaコマンドのクラスパスワイルドカードが使えるはずなんだけどね。
シェルのfor文を使ってjarファイルを1つずつ指定しなくてもいい)
if not defined HADOOP_HOME (
  set HADOOP_HOME=C:/cygwin/usr/local/hadoop-0.20.2
)
set CLASSPATH=%CLASSPATH%;%HADOOP_HOME%/*
もしくは
set CLASSPATH=%CLASSPATH%;%HADOOP_HOME%/hadoop-0.20.2-core.jar

Voldemort Administration

下記のURIにアクセスすると、「Voldemort Administration」というタイトルの画面が開く。[/2010-06-12]
swap-storeを使う代わりに、手動で読込専用ストアのフェッチ(HDFSからローカルへのデータコピー)スワップ(データ格納)が出来る。
(この画面の処理を行っているのはReadOnlyStoreManagementServletクラス)

http://localhost:8081/read-only/mgmt


Fetch Data Files

HDFSに入っているデータ(SequenceFile)をローカルにコピーするメニュー。[2010-06-12]

手順 実施内容 備考
データファイルのフェッチ
項目名 内容(設定例)
Store wordcount1
Store Directory hdfs://localhost:9000/user/hishidama/voldemort/wordcount/node-0
C:\WINDOWS\TEMP\hdfs-fetcher\hdfs-fetcher\wordcount1_1276286512437\node-0
ブラウザーから実行する。
「Store」には(読込専用の)ストア名を選択する。
「Store Directory」にHDFSのパスを指定する。
成功すると、ローカルディレクトリーがフルパスで表示される。
このパスをSwap Data Filesメニューに指定する。
フェッチしたデータの確認
> cd C:\WINDOWS\TEMP\hdfs-fetcher\hdfs-fetcher\wordcount1_1276286512437
> dir node-0
2010/06/12  05:01    <DIR>         .
2010/06/12  05:01    <DIR>         ..
2010/06/12  05:01               45 0.data
2010/06/12  05:01              100 0.index
 

Swap Data Files

ローカルにある元データ(SequenceFile)をVoldemortのDBに反映させるメニュー。[2010-06-12]

「Store Directory」には、元データのあるローカルのディレクトリーを指定する。HDFSは指定できない。
したがって、HDFSに作った元データを一旦ローカルにコピーする必要がある。
自分でbin/hadoopコマンドを使ってコピーしてもいいし、Fetch Data Filesメニューでコピーしてもいい。

手順 実施内容 備考
元データの、HDFSからローカルへのコピー
(hadoopコマンドでコピーする場合)
$ mkdir -p /home/hadoop/voldemort
$ cd /home/hadoop/voldemort

$ $HADOOP_HOME/bin/hadoop fs -get voldemort/wordcount .

$ ls -lR
〜
./wordcount/node-0:
total 3
-rwxrwxrwx 1 hishidama なし  45 2010-06-12 02:42 0.data
-rwxrwxrwx 1 hishidama なし 100 2010-06-12 02:42 0.index
-rwxrwxrwx 1 hishidama なし  16 2010-06-12 02:42 md5checkSum.txt

$ echo $(cygpath -ma wordcount/node-0)
C:/cygwin/home/hadoop/voldemort/wordcount/node-0
Cygwinから左記のコマンドを実行する。
「hadoop -fs -get」によってHDFSからローカルに元データをコピーする。
「ls」によってコピーされた事を確認し、
cygpath」によってWindowsのパス名を確認する。
データファイルのスワップ
項目名 内容(設定例)
Store wordcount1
Store Directory C:/cygwin/home/hadoop/voldemort/wordcount/node-0
C:\WINDOWS\TEMP\hdfs-fetcher\hdfs-fetcher\wordcount1_1276286512437\node-0
Swap completed.
ブラウザーから実行する。
「Store Directory」にローカルディレクトリーを指定する。
成功すると、画面に「Swap completed」と表示される。
テーブル内容の確認
> cd \voldemort-0.80.2
> bin\voldemort-shell.bat wordcount1 tcp://localhost:6666
Established connection to wordcount1 via tcp://localhost:6666

> get 'Hello'
version(): 3
> get 'Hadoop'
version(): 2
クライアントシェルを(コマンドプロンプトから)起動して、ストアの内容を確認する。
(読取専用なので、バージョン情報は無いようだ)
ローカルディレクトリーの確認
$ cd /home/hadoop/voldemort
$ ls
wordcount
$ ls -l wordcount/
total 0
スワップを実行すると、スワップ元となったローカルディレクトリーは削除される。

Voldemortへ戻る / Voldemort Javaへ戻る / 技術メモへ戻る
メールの送信先:ひしだま