たごもりすメモ

コードとかその他の話とか。

HadoopはApache版0.20.2とCloudera版0.20.2+737で互換性がない

いやまあ、バージョンは合わせろよとか、そのくらい常識だとか、言われそうな話なんですけどね。

症状

別のところで構成されてるHadoopクラスタHDFSにデータを書き込みたくなったので、あるマシン(CentOS5)にSun JDKHadoopを入れることにしました。インストールが面倒だと思ってた*1のでClouderaさんが作っているというrpmを使うことにした。

Log In - Cloudera Wiki

既存のHadoopクラスタではバージョン 0.20.2 を使っているっぽかったのでCDH3でOK。
ここからCDH3(beta3)のリポジトリyum.repos.d に追加して yum update yum 後に yum install hadoop-0.20 hadoop-0.20-libhdfs とかやってインストール。JAVA_HOMEをJDKのパスに設定。CLASSPATHに以下のようにしてhadoop関連のjarを根こそぎぶちこむ。

$ export CLASSPATH=`find /usr/lib/hadoop-0.20 -name '*.jar' | grep -v 'test' | grep -v 'example' | perl -e '@jars=<STDIN>;chomp @jars; print join(":",@jars);'`

その上でhdfsにアクセスできるか試そうと以下のコマンドを実行したところ、残念な感じのエラーになった。

$ hadoop fs -fs hdfs://namenode-server:PORTNUM/ -ls /
Bad connection to FS. command aborted. exception: Call to namenode-server/xx.xx.xx.xx:PORTNUM failed on local exception: java.io.EOFException

このときnamenode側のログには以下のような行が出てた。

2011-01-05 11:29:54,203 WARN org.apache.hadoop.ipc.Server: Incorrect header or version mismatch from yy.yy.yy.yy:NNNN got version 
4 expected version 3

対処

Apache版の 0.20.2 リリースとCloudera版(CDH)の 0.20.2+737 ではRPCの相互接続性が失われています。
Hadoopクラスタ側がApache版のHadoopを使用している場合、接続しにいく側もおとなしくApache版のHadoopをダウンロードして使いましょう。

原因

しかし 0.20.2 どうしなのにRPCのバージョン互換性がないってどういうこと? という誰もが抱く疑問について調べてみた。というか、以下のあたり調査してみて確信が持てたからApache版を試してみたんだけど。

まずエラーメッセージから core/org/apache/hadoop/ipc/Server.java を眺めてみる。

 // core/org/apache/hadoop/ipc/Server.java line 814

          if (!HEADER.equals(dataLengthBuffer) || version != CURRENT_VERSION) {
            //Warning is ok since this is not supposed to happen.
            LOG.warn("Incorrect header or version mismatch from " +
                     hostAddress + ":" + remotePort +
                     " got version " + version + 
                     " expected version " + CURRENT_VERSION);
            return -1;
          } 

これですね。"ok since this is not supposed to happen." じゃねーよ。俺を笑い死にさせる気か。

さてこの CURRENT_VERSION をまず見てみると、Apache版では以下のような感じ。

  // core/org/apache/hadoop/ipc/Server.java line 84

  // 1 : Introduce ping and server does not throw away RPCs
  // 3 : Introduce the protocol into the RPC connection header
  public static final byte CURRENT_VERSION = 3;

CDH3では以下。

  // 1 : Introduce ping and server does not throw away RPCs
  // 3 : Introduce the protocol into the RPC connection header
  // 4 : Introduced SASL security layer
  public static final byte CURRENT_VERSION = 4;

どう見てもバージョンが違います。本当にありがとうございます。あとはこれをRPC Clientが送っていることが確認できればいいわけですね。

  // CDH3版
  // core/org/apache/hadoop/ipc/Client.java line 609
    /* Write the RPC header */
    private void writeRpcHeader(OutputStream outStream) throws IOException {
      DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outStream));
      // Write out the header, version and authentication method
      out.write(Server.HEADER.array());
      out.write(Server.CURRENT_VERSION);
      authMethod.write(out);
      out.flush();
    }

こんな非互換なパッチを同じバージョン番号で当ててリリースするんじゃねーよ。ていうかApache版を見てみるとメソッド名すら元は writeRpcHeader じゃなくて writeHeader だし。本当に勘弁してくれ。

追記(同日19:41)

サーバ側でApache Hadoopリリースの 0.21.0 にした状態でCDH3beta3(0.20.2+737)から繋いでみたら、RPCヘッダのバージョン番号は合うようになったはずだけど、やっぱり "Version Mismatch between client and server... command aborted." とか言われました。まあHadoopのバージョン、確かに違うしね。
じゃあ何のためにClouderaの連中はそんなところをバックポートしやがったんだ。単に通信可能な相手をわざと減らしただけじゃねえか。アホすぎる連中なのか。馬鹿なのか。

感想

こんなんを仕事でがっつり扱うとすぐにハゲそうだ。Javaのコードに依存しないHDFSクライアント書きたいなあ。

*1:けどそんなことなかったと後で判明