Hadoopのorg.apache.hadoop.io.Text.getBytes()にハマる
テキストファイルから次々と行を読んで処理していくだけの単純なプログラムだったのに,読み込んだ値にゴミが入っているという不具合にハマりました.マルチスレッド関係かと思ったのですが,既に書いて下さっている方がおりました.
org.apache.hadoop.io.Text.getBytes()を使用するときの注意 - yustam.jp
MRUnitだと不具合は発生しなかったので,気づくのに時間がかかりました.
以下,検証コードです.
エラーチェックも何もない最小限ドライバ.
public final class TestDriver { public static void main(final String[] args) throws IOException, ParseException, ClassNotFoundException, InterruptedException { final Configuration conf = new Configuration(); final String input = "/user/hadoop/testInput"; final String output = "/user/hadoop/testOutput"; final Job job = Job.getInstance(conf); job.setJarByClass(TestDriver.class); job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(NullWritable.class); job.setMapperClass(TestMapper.class); FileInputFormat.addInputPath(job, new Path(input)); FileOutputFormat.setOutputPath(job, new Path(output)); job.waitForCompletion(true); } }
不具合の出るマッパー.
public class TestMapper extends Mapper<LongWritable, Text, Text, NullWritable> { public final void map(final LongWritable key, final Text values, final Context output) throws IOException, InterruptedException { output.write(new Text(values.getBytes()), NullWritable.get()); } }
入力ファイル.
$ hdfs dfs -cat testInput AAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBB CCCCCCCCCC DDDDDDDDDDDDDDDDDDDD EEEEEEEEEEEEEEE
出力ファイル.
$ hdfs dfs -cat testOutput/part-r-00000 AAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBAAAAA CCCCCCCCCCBBBBBAAAAA DDDDDDDDDDDDDDDDDDDD EEEEEEEEEEEEEEEDDDDD
次に不具合の出ないマッパー.
public class TestMapper extends Mapper<LongWritable, Text, Text, NullWritable> { public final void map(final LongWritable key, final Text values, final Context output) throws IOException, InterruptedException { output.write(new Text(values.copyBytes()), NullWritable.get()); } }
入力ファイルは同じ.
$ hdfs dfs -cat testInput AAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBB CCCCCCCCCC DDDDDDDDDDDDDDDDDDDD EEEEEEEEEEEEEEE
出力ファイル.
$ hdfs dfs -cat testOutput/part-r-00000 AAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBB CCCCCCCCCC DDDDDDDDDDDDDDDDDDDD EEEEEEEEEEEEEEE
非常に冗長ですが,一度文字列にしても大丈夫でした.まぁ問題はbyte[]にしたいことであるので,Textに出来たからと言ってあんまり意味は無いのですが...
public class HadoopSpecificationTestMapper extends Mapper<LongWritable, Text, Text, NullWritable> { public final void map(final LongWritable key, final Text values, final Context output) throws IOException, InterruptedException { output.write(new Text(values.toString()), NullWritable.get()); } }
背景としては,バイト列にしてKVSにブッコミたかったというところです.
ソフトのバージョンは次のような感じです.
> java -version java version "1.6.0_24" OpenJDK Runtime Environment (IcedTea6 1.11.1) (rhel-1.45.1.11.1.el6-x86_64) OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode) > hadoop version Hadoop 2.0.0-cdh4.4.0 Subversion file:///var/lib/jenkins/workspace/CDH4.4.0-Packaging-Hadoop/build/cdh4/hadoop/2.0.0-cdh4.4.0/source/hadoop-common-project/hadoop-common -r c0eba6cd38c984557e96a16ccd7356b7de835e79 Compiled by jenkins on Tue Sep 3 18:52:51 PDT 2013