Home

タムタムの日記

AvocadoDBの概要説明とJavaからの扱い方

今回はAvocadoDBの簡単な概念とJavaからの扱い方を説明します。そういえばAvocadoDBはmrubyの人柱に名乗りを上げています。mrubyを使ってみる理由は彼らのBlogに書いてあります。前回の記事でマルチスレッドですと書きましたが、V8をマルチスレッドで扱うのめんどくね?とちょっと思っていたのですが、彼らも思っていたようですw またAvocadoDBのQueryLanguageはRFC化を目指しているようです。 とっても意欲的ですね。

※Version0.3.12を元に記載していますので、今後変わる可能性があります。

さて、まずはAvocadoDBの用語説明をします。全部は解説しません。一部だけです。

用語 説明
Collection RDBで言うところのテーブルです
Collection Identifier コレクションを識別するための内部ID(整数型です)
Collection Name コレクションを識別するための名前です(文字列です)。Collection Identifierと内部的に1:1に対応しています。命名規則は先頭が英字で他は英数かアンダーバー、ハイフンで構成されます。ハイフンを除けば常識的なプログラミング言語の変数名の規則と同じです。
Document RDBで言うところのレコードです
Document Identifier Documentを識別するための内部ID(整数型です)。これを直接使うことはほとんどありません。↓のDocumentHandleを通常は使います。
Document Handle Documentを識別するためのHandle名(文字列です)。書式はCollectionID + "/" + DocumentIDとなります。
Document Revision MVCCのMV(Multi Version)のバージョン番号にあたるものです。整数型です。これとDocument Etagを併せて説明しないといけないのですが、今回は省略します・・。

HTTP-RESTのInterfaceではCollectionNameもCollectionIDも両方サポートしています。またDocumentの実際の値はJSONになります。JSONObjectでも、JsonArrayでも大丈夫ですし、AvocadoDBはKVSでもあるのでJSONの要素(文字列、整数、Boolean、NULL)も受け付けます。

ちょっとだけGraphDBとしてのAvocadoDBも説明します。
Graph構造はVertexとEdgeで構成されます。VertexはDocumentに相当します。Edgeは専用で作るAPIが存在します。有向グラフか無向グラフ(要はエッジの向きがあるか無いか)はEdgeを作るときには指定できません。基本は有効グラフとしてEdgeを作ります。Edgeを取得する時にany, in, outを指定して取ります。VertexはDocumentなので任意の属性を保存する事ができ、Edgeも同様に任意の属性を保存する事ができます。 

Indexの種類は今は3つあります。Hash Index, Geo Index, Skip Listsです。(B-treeが無い理由はVideo見ればわかったりするのかな?)。複合インデックスに出来ますが、Geo Indexは要素は2つまでの制約があります。Indexはユニーク制約を付けることができます。HashとSkipListsの特性の違いの説明は必要ありませんよね。

CollectionやDocumentを作るときに、Diskへの書き込みを同期化するかのオプションがあります。InnoDBのFlushパラメータみたいなのですが、これは基本的にAsyncにしないと性能が出ません。(※性能テストはRoadMapの1.0betaで行う事になっているので、今の段階で性能云々を言うのはちょっとお門違いです)

さて、次はJavaで扱う方法です。Javaのドライバはこちらにあります。JARファイル作っていません。作った方がいいかな?
※prototypeですので、サーバが落ちている時のハンドリングを入れていません。

今できる事は、HTTP-RESTを叩いてCollection, Document, Cursor, Index, Edgeの操作ができます。

使い方はREADMEかExampleに書いてあります。単体テストを見るのが良いと思います。
以下はDocumentを1000個適当に作ってクエリで検索するサンプルです。Cursorを扱いやすくするためにResultSetを作ってみました。

public class Example1 {

  public static class ExampleEntity {
    public String name;
    public String gender;
    public int age;
  }

  public static void main(String[] args) {

    AvocadoConfigure configure = new AvocadoConfigure();
    AvocadoDriver driver = new AvocadoDriver(configure);

    try {
      for (int i = 0; i < 1000; i++) {
        ExampleEntity value = new ExampleEntity();
        value.name = "TestUser" + i;
        switch (i % 3) {
        case 0: value.gender = "MAN"; break;
        case 1: value.gender = "WOMAN"; break;
        case 2: value.gender = "OTHER"; break;
        }
        value.age = (int) (Math.random() * 100) + 10;
        driver.createDocument("example_collection1", value, true, null, null);
      }

      HashMap<String, Object> bindVars = new HashMap<String, Object>();
      bindVars.put("gender", "WOMAN");

      CursorResultSet<ExampleEntity> rs = driver.executeQueryWithResultSet(
          "select t from example_collection1 t where t.age >= 20 && t.age < 30 && t.gender == @gender@",
          bindVars, ExampleEntity.class, true, 10);

      System.out.println(rs.getTotalCount());
      for (ExampleEntity obj: rs) {
        System.out.printf("  %15s(%5s): %d%n", obj.name, obj.gender, obj.age);
      }

    } catch (AvocadoException e) {
      e.printStackTrace();
    } finally {
      driver.shutdown();
    }

  }

}

グラフも扱えます。 グラフの扱い方は以下のようになります。ただし効果的にトラバーサルする方法がまだ提供されていません。TinkerPopのBluePrintsとGremlinに対応するらしいので、そのあたりで出来ることは将来的にできるようになると思います。(私はGremlinでGraphを操作した事がないので詳細はわかりません。)
以下のサンプルは、Vertexを10個作って、エッジを3つ(e1=v0->v1, e2=v0->v2, e3=v2->v3)作り、v0のエッジを求めています。 

public class Example2 {

  public static class TestEdgeAttribute {
    public String a;
    public int b;
    public TestEdgeAttribute(){}
    public TestEdgeAttribute(String a, int b) {
      this.a = a;
      this.b = b;
    }
  }
  public static class TestVertex {
    public String name;
  }

  public static void main(String[] args) {

    AvocadoConfigure configure = new AvocadoConfigure();
    AvocadoDriver driver = new AvocadoDriver(configure);

    final String collectionName = "example";
    try {

      // CreateVertex
      ArrayList<DocumentEntity<TestVertex>> docs = new ArrayList<DocumentEntity<TestVertex>>();
      for (int i = 0; i < 10; i++) {
        TestVertex value = new TestVertex();
        value.name = "vvv" + i;
        DocumentEntity<TestVertex> doc = driver.createDocument(collectionName, value, true, false, null);
        docs.add(doc);
      }

      // 0 -> 1
      // 0 -> 2
      // 2 -> 3

      EdgeEntity<TestEdgeAttribute> edge1 = driver.createEdge(
          collectionName, docs.get(0).getDocumentHandle(), docs.get(1).getDocumentHandle(),
          new TestEdgeAttribute("edge1", 100));

      EdgeEntity<TestEdgeAttribute> edge2 = driver.createEdge(
          collectionName, docs.get(0).getDocumentHandle(), docs.get(2).getDocumentHandle(),
          new TestEdgeAttribute("edge2", 200));

      EdgeEntity<TestEdgeAttribute> edge3 = driver.createEdge(
          collectionName, docs.get(2).getDocumentHandle(), docs.get(3).getDocumentHandle(),
          new TestEdgeAttribute("edge3", 300));

      EdgesEntity<TestEdgeAttribute> edges = driver.getEdges(collectionName, docs.get(0).getDocumentHandle(), Direction.ANY, TestEdgeAttribute.class);
      System.out.println(edges.size());
      System.out.println(edges.get(0).getAttributes().a);
      System.out.println(edges.get(1).getAttributes().a);

    } catch (AvocadoException e) {
      e.printStackTrace();
    } finally {
      driver.shutdown();
    }

  }

}

Hiveのパッチ投稿のやりかた

こんにちは、ポテトサラダを極めようと心に決めたタムタムです。
(昨日作って食べたら満足しました(・w・))

今日はHiveのパッチの書き方をまとめます。
Hadoop本体とはちょっと作法が違います。Hadoop本体はパッチを投げると自動でJenkinsがテストしてくれたりするらしいのですが、Hiveはそんなことやってくれません。またパッチもJIRAに添付するだけではダメで、Phabricator(Reviewボードみたいなもの)というツールを使ってDiffをアップしてReviewしてもらう必要があります。

今回は実際にHIVE-2703のパッチを投げるまでの手順を書いていきます。
http://mt.orz.at/archives/2012/02/hivejira.html こちらの内容と被っていますが、こっちでは説明しきれなかったところも書いていきます。
なお、手順はsubversionの手順になっています。ぶっちゃけgitの方をオススメします。subversionだと複数のパッチを書くときにローカルでブランチを切れないのでとってもとってもつらいです・・。(私がsubversionを使っているのは、gitだとphabricatorのarcコマンドがうまく動作せずにarc diff --jira HIVE-xxxでパッチをあげることができなかったからです)

なお私はFedora16をVirtualBoxの上で動かして開発しています。(MacbookとかオシャレなPC持っていません・・)
あと、Hadoopの環境は普通に作っておいてください。ローカルモードで良いです。これも省略。

今やいろんな本が出ていますが、Hadoop徹底入門がオススメです。

まずざっくりとした手順は以下のようになります。

  • ソースを落とす
  • ソースをいじる
  • テストする
  • (JIRAで適宜コメント書いたり、やりとりしたり)
  • Phabricatorでreviewして貰う
  • 最終版のDIFFをJIRAに添付する(ASFライセンス付ける)

細かい手順を説明していきます。

まず、ソースを落としてEclipseのプロジェクトを作ります。(Eclipse使わなくてもパッチは投稿できます。ただJavaのソースをIDE無しで書くのは結構つらいと思います。)
なお、ソースを落としてきたら、HIVE-2902, HIVE-2904のパッチを当てましょう。今のバージョンだと正しいEclipseのテンプレートを作ってくれません。

# ソースを落としてくる
svn checkout http://svn.apache.org/repos/asf/hive/trunk hive-trunk
cd hive-trunk
# パッチを当てる(※1)
wget https://issues.apache.org/jira/secure/attachment/12523301/HIVE-2902.2.patch.txt
wget https://issues.apache.org/jira/secure/attachment/12522738/HIVE-2904.D2487.3.patch
patch -p0 < HIVE-2902.2.patch.txt
patch -p0 < HIVE-2904.D2487.3.patch
# ビルドする
ant clean package
# Eclipse用のプロジェクトを生成する
ant eclipse-files
# テストドライバとか生成する
cd metastore
ant model-jar
cd ../ql
ant gen-test

※1 追記(2012/04/19 13:30) パッチのバージョンを変更しました。最新のソースだとDDLTaskがjson形式で出力できるようになっていて、JsonMetaDataFormatterがjacksonに依存するようになっていました。

これでhive-trunkにeclipseのprojectファイルが出来ています。あとはEclipseからインポートするだけです。なお、Eclipseの使い方は今更説明するようなものではないので省略します。注意点としては、ワークスペースの文字コードをUTF-8に、JDKは6を使いましょう。

また、プロジェクトの設定で「Javaエディタ → 保管アクション」の設定でインポートの編成が行われるようになっています。なのでパッチを書いた時にdiffを取ったらいじっていないところも変更されている事があります。これは気にしたら負けです。
もう1つ、プロジェクトの設定の「ビルダー」で「Hive_Ant_Builder」のチェックは外した方が良いです。保存する度にAntが走って重めのビルドが走ることになってちょっとイラッとします。

ここまでで、開発環境は整いました。
というわけで、普通に開発をしましょう。なお、パッチを出す時は基本的にテストケースも一緒に追加します。(パッチの内容にもよりますけど)。

次にテストの方法です。単体テストはJUnitで書かれています。Eclipse用のプロジェクトを生成した時に、実行構成も既に設定されています。例えばJdbcDriverTestという単体テストの実行構成を見ると、以下のように既に必要な設定(VM引数、環境変数、作業ディレクトリなど)が入っています。


どのクラスがどの単体テストが担当しているのかは、クラスの参照を調べればなんとなく推測できます。まずは関連する単体テストがピンポイントで通るようにするのが良いです、なお、UDF関数やクエリのテストはまたちょっと違います。後者のテストの方法は以下のようにします。 このテストは、「ql/src/test/queries/clientpositive/timestamp_udf.q」を実行した時に「ql/src/test/results/clientpositive/timestamp_udf.q.out」の結果になるかというテストです。前回の記事にも書いてあります。 -Dqfileを省略すると全部のテストを実行します。

# ポジティブテスト
ant -Dtestcase=TestCliDriver -Dqfile=timestamp_udf.q -Dtest.silent=false test
# ネガティブテスト
ant -Dtestcase=TestNegativeCliDriver -Dqfile=timestamp_udf.q -Dtest.silent=false test

このあたりをピンポイントで実行してから、最後に全体のテストを実行します。非情に時間がかかります。4,5時間は覚悟してください。寝る前に実行しましょう。

ant package test
ant testreport

これで build/test/junit-noframes.html というファイルが生成されます。このファイルにテストの結果が書いてあります。

パッチを投げる前にsvn diffを取って差分を確認してください。(※最初に環境構築をするためにパッチを当てたやつは除外する必要があります。)

# LANGが日本語になっていると「リビジョン」という日本語が入るので
export LANG=C
svn diff > HIVE-xxxx.1.patch.txt

gitの場合は、以下のような感じです。

git diff --no-prefix > HIVE-xxxx.1.patch.txt

これでパッチを投げる準備ができました。(後述するPhabricatorを使わない場合は、このDIFFをJIRAにファイル添付すれば良いです。)

チケットの開き方は前回の記事を参考にしてください。
※こんな事を書くと怒られるかもしれませんが、ぶっちゃけ英語なんて適当で通じます(たぶん)。英語が出来なくてウジウジするくらいならコードを書いてパッチを投げましょう。(もしくは知り合いに英訳して貰いましょう)。自分たちが外国人のニンジャ語を理解できるように向こうの人もきっと理解してくれます。たぶん(・3・)。

パッチの投稿の仕方です。Phabricatorをインストールする必要があります。以下のURLを参考にします。
http://www.phabricator.com/docs/phabricator/article/Arcanist_User_Guide.html
https://cwiki.apache.org/confluence/display/Hive/PhabricatorCodeReview

# どこかのディレクトリで以下を実行
git clone git://github.com/facebook/libphutil.git
git clone git://github.com/facebook/arcanist.git
# BashのTab Completion(arc[TAB]で引数を表示してくれる)を有効にする
source arcanist/resources/shell/bash-completion

その後、以下のコマンドを叩いて準備をします。
arcanist/bin を環境変数PATHに通しておくと良いです。

ant arc-setup
arc install-certificate

Phabricatorにパッチを投げる手順は以下になります。これは実際にsvnかgitでdiffを取ってPhabricatorに投稿します。これでJIRAにパッチが添付されます。

# svnの場合
arc diff --jira HIVE-xxxx
# gitの場合(gitの場合はarc diffで--previewオプションが使えます)
git checkout HIVE-xxxx-dev-branch
ソース編集
git commit -a --amend --reset-author -C HEAD
arc diff

以上でJIRAにパッチが投稿されるので、「Submit Patch」を押します。するとステータスがPatch Availableになります。 パッチがNGな場合は、レビューの内容やコメントの内容を見て修正しましょう。

レビューが終わったら最終バージョンのパッチをJIRAに添付します。添付する時はASFライセンスを付与しましょう。この時に新機能の追加や互換性が無くなる変更の場合はReleaseNoteを各必要があります。 

私はHiveにはそんなに詳しくない初級者ですが、何か困ったことがあれば協力できるかも。

なお、Hadoop Hackathonというものがあります。
http://www.zusaar.com/event/250002

私が参加した時は、JIRAの使い方とパッチの書き方を最初に教えていただきました。基本は黙々とハッカソンするわけですが、
お菓子を食べながらプロジェクターにソースを映してアレやコレやと楽しかったです。
開発環境とJavaさえ書ければ、英語は出来なくてもみんな英語が出来るので誰かが助けてくれます。

Hadoopのここが気にくわないわー。Hiveのここが気にくわないわー。あのBug放置されてるわー、直したいわー。という人は参加を考えてみると良いかも。

AvocadoDBについて

こんにちは、某所で豆腐ハンバーグを食べないといけないくらい貧乏ということになっているタムタムです。(なんか面白かったのでこのネタしばらく使います)

English translation is here. Very thanks to @makoto_inoue!!
https://gist.github.com/2359195

今回は AvocadoDB というものを紹介したいと思います。
間違いや勘違いがあったらご指摘くださいませ。

一言でいうと、MongoDB と OrientDB を足したようなものです。Webサイトに掲載されている特徴を列挙すると以下のようになります。

  • スキーマレスです
  • クライアント・サーバモデル(アプリケーションサーバとして動作)です。そしてRESTインターフェースを提供しています。
  • JavaScriptエンジン(V8)を内蔵していて、それでデータを操作できます(もっと言えば、自分でJS追加すると機能を拡張できる!! のでKTのLuaバインディングっぽい事がもっと簡単にできます)
  • マルチスレッドで動作します
  • MVCCモデルです。ドキュメントは(内部的には追加オンリー)でバージョン管理していて、古いバージョンは自動でガベコレされます。(PostgreSQLみたいなやつ)
  • 柔軟なデータ構造を提供します。単純なKVS、DocumentDB、グラフDB(これはまだRESTで扱えないけど、シェルからはJSを介して使えます)
  • インデックスを選択できます。スキップリストやn-gramインデックスとか。(将来的にはHashIndexやGeoIndexも?)
  • 永続性の設定ができます。
  • モダンなストレージに対応しています。(※意味が良くわからない・・SSDとかに最適化してるってこと?誰か教えてエロい人)
  • オープンソースです(Apache Licence 2.0)

(追記)
※MVCCについて、Frank Celler(@fceller) さんからコメントがあったので記載しておきます。
@fceller said:

currently we have implemented Multi-Versions (aka Revisions) of documents. CC will come, when we have replication.

ちょっと英語とMVCCについての理解が浅いので勉強してくる(( T_T)
(追記ここまで)

AvocadoDB の何が熱いかと言うと、開発者のレスポンスの早さと開発速度がちょっとクレイジーなくらいはやいところ。今だとAvocadoDBについてTweetすれば関係者がレスポンスくれます。日本語でもだいたい反応してくれます。(というか、TwitterをAvocadoDBで検索すると、開発者か日本人しかつぶやいていません・・) 
あと、絶賛開発中なのにドキュメントが綺麗・豊富に揃っています。なので、Wiki を見ればだいたいの事は載っています。(目次からたどれないページがいくつかあるように感じるので、ページ一覧から行く方が早いです)
常識的なC++で書かれているので(一部Cのコードもあります)ソースコードは読みやすいです。コメントもきちんと書いてあります。RESTに関してはコアな機能がC++で実装されていて、ほとんどがJSで実装されておりコアな機能を呼び出しているという形式になっています。

開発者がどれくらい熱いかと言うと、アレな会社で一般人なイケメンをやっている人のTweetに対してVideoで返信してくるとか。



http://www.avocadodb.org/2012/04/03/skip-list-indices-in-avocadodb

各言語のDriverというかClientというかバインディングみたいなものは、公式ではPHPのものがあります。他の言語については、皆様が絶賛開発中です(なぜかみんな日本人)。現在の対応状況がロードマップに記載されています。

なお、JavaのDriverは私が絶賛開発中でございます。

進捗としては、DocumentDBとして使えるところまでは出来ています。今ユニットテストをいっぱい書いてます・・。じゃないとサーバの挙動が変わった時にregression testが出来なくて死ぬので。
概要だけ書いておくと、AvocadoClient, AvocadoDriver, AvocadoJDBCDriverという構成になります。AvocadoClientはオブジェクト指向的に扱うためのインターフェースでDocumentクラスがcreateとかを持つようになります。AvocadoDriverはREST-APIとほぼ1:1に対応した低レイヤーのインターフェースになります。AvocadoJDBCDriverはJDBCドライバとして実装しているものになります。今作っているのはAvocadoDriverです。Client/Driverと分けたのは、CommonsHttpClient4やsvnkitのような感じの低レイヤーのものと高レイヤーのものとで分離したかったからです。

こんな感じで使いますというのをGistに簡単なサンプルを載せました。(検証コードがあちこちにあって見づらいですが、使い方は至ってシンプルだと思います。)
まだgithubにはpushしてません。もうしばらくお待ちくださいませ。

AvocadoDBのインストールの方法や簡単な使い方、グロッサリーの説明は別の記事に書きます。

TwitterJapanの綺麗なオフィスに遊びに行ってきました

書こう書こうと思って今更ながら記事を書きます。
TwitterJapanに遊びに行ったので、その時の写真を載せておきます。

先月はハッカソンイベントのために行ってきました。2,3時間で成果物出すのはきついw とりあえず動くもの2個つくった。(Twitterの内容をIRCへ垂れ流すやつと、Stream中のURLをリアルタイム集計していくやつ)。RedisとTwitter4jサマサマ。

このクッション欲しい・・。

4/3(風強すぎ・・)にも行ってきました。
こっちはメディアの方もいっぱい来ていたのであちこちで記事になっていると思います。聞いた内容をずらずらと書いても良いのですが、他の人が綺麗にまとめているので省略。

RobがJavaOneのために来日していて、Twitterのアーキテクチャの話をしてくれました。写真はこの前のバルスの時の負荷に関するもの。

個人的に気になるのは、Staging環境とPreFlight環境でのテストデータについて。Production環境から引っ張ってきていると思うんだけど、そこがどういうアーキテクチャになっているのかが気になる。ストレージのレイヤがMySQLと言っていたけど、ストレージのレイヤで解決しようとすると、単純にレプリケーションでStaging用のデータ作ってる場合はWriteしたら色々おかしくなるだろうし。Thriftでうにうにやってるレイヤーでやってるのかな・・。

というあたりがモヤモヤ。

JavaOneでのRobのセッションのつぶやきのまとめはこちら、Twitter Tokyo Open Houseのまとめはこちら

 

そういえば、Oracle Open WorldのさくらさんのZFSの話ってどこかに載ってないのかな。すごく気になる。
(Open Worldという名前なのに全然オープンじゃn(ry)

退職しました

このたび、株式会社スクウェア・エニックスを退職しました。
正確には4月2日が最終出社、4月30日付けで退職となります。

2006年に入社し、約6年勤めた事になります。
PlayOnlineというプロジェクトに始まり、FinalFantasyシリーズに関われた事はとても貴重で刺激の多い体験をする事ができました。(他にも兼任していたけど省略します)

 

エンジニア業務としては、
サーバも書かせて頂きましたし、Webのお仕事も立ち上げから運用まで一貫して携わらせて頂きました。独自のプロトコルを実装したり、フロムスクラッチでサイトを立ち上げたり。特にサイト(SNSっぽいもの)の立ち上げは、実装全般、開発環境やらステージング環境の構築も含めてサーバ構成なども良い経験でした。さっき数えたらそのサイトのために実装したクラスは1000ちょっとでした(意外と少ない)。Webサイトと言ってもSNSとしての機能をだいたい実装し、ゲームと連動させるためにMMOのサーバやスクリプトを読み解いたり、モデルの仕様を聞いてキャラクタの画像を表示できるようにしたり。なぜかゲームのサーバからクライアント、スクリプト、モデル仕様等までわかるようになっていました。
いろいろと問題の多いプロジェクトでもありましたが、逆にそのおかげで領域、職種関係なく自分でやらないと物事が進まないことも多く、様々な知見を得ることができました。
本当に0~10までやった感じです。

そういえばマイニング的な事もしてました。統計やマイニングって楽しいですよね。自分しか知らない秘密の性質を発見したり。自分で企画書書くときに、刺さるレイヤーがどんな感じかというのを知りたいためにやっていました。
ちなみに、こういうのは目的ありきでやらないと意味がないですし、その結果やその先にあるものを誰に(何に)フィードバックするかが重要だと考えています。データは(読み方を間違えなければ)嘘をつきませんが、データが全てではありません。経験や直感も大切です。

 

Webディレクター的な事やプランナー的な事もやらせて頂きました。
自分で企画書を書き、トップレベルのレイヤーに「やっていいよね?」程度の軽く承認を取っただけで、他セクションのいろんな人を巻き込んで物事を進めていったのは良い思い出です。直近では、あの企画がノリノリで一番楽しかったです。自分のスキルセットではどうにもならない部分(BG、フェイシャル、キャラ、ライト、モーション等)を協力してくれた方に特に感謝してます。
「こんな事考えているんですけど楽しそうじゃないですか?」 → 「実は動く物あります。作っちゃったー(てへぺろ)」 → 「でも私の実力だとMSペイントが限界だなぁ!デザイナさん達がやったら凄く良くなるんだろうなぁ!!(チラッチラッ)」 → 「わぁい 協力者げっとー」 という感じで巻き込んでいったのが多かった気がします。
良いやり方とは言えませんが(リスクが高いですし)、世の中にはやりたいしか言わない人も多いので「やりたいじゃないんだよ、やるんだよ!」という感じで自分から動けば物事を進めやすい環境でした。

 

仕事をする上で重点を置いていた事がいくつかあります。(細かく言うと他にもたくさんありますがきりが無いので。)
・自分より後に作業をする人には徹底的に配慮する。(デザイナ、QAチーム、運用チームなど)
・ユーザーサポート系の問い合わせ(ユーザーへ返信が必要なもの)については最優先(障害対応を除く)で対応する。
・ルールや規則が何度も弊害になる場合、ルールが間違っている事を疑う。
・拘るところには徹底的に拘る。そうじゃないところは(良い意味で)手を抜く。
・タスクの締め切りに曖昧な言葉(今日中、今週中 等)を使わない。
・最初にやりたい事のMAXを出してどうあるべきかの理想型を考える。いきなり内容をそぎ落としたりしない。
・「やりたい、やりたい」じゃなくて「やるんだよ!」
・出来ない理由を探して「出来ません」じゃなくて、「どうやったら出来るのか」を考える方が遙かに生産的。

 

ところで、私が毒吐きキャラというのは、まぁ周知の事実だとは思います。
(※ただし私が毒を吐くときは、"なんでこんなこともできないんだ"という事で吐くことはありません。基本的には自分でやるって言ったことをやらない場合に毒を吐きます。できないと言ったことはそもそも要求しません。)
それでも大人な対応でサポートしていただき、自分は周りに恵まれていたと思います。
本当に自由にやらせて頂きました。この会社では珍しいほど自由に動き回れました。これも周りの人のサポートがあったからだと思います。本当に多くのご迷惑をおかけしました。そして本当にありがとうございました。

ある上司に言われた「やりたいようにやれ。やっちゃいけない事をやったら叱るから、叱られるまでやれ(意訳)」という言葉は印象的でした。その結果こんな人材ができあがりました(笑)
辞めた人が在籍していた会社をDisっているのを時々見かけますが、私はこの会社が好きでしたし今でも好きな会社です。私がたまたま環境に恵まれていただけかもしれませんが、少なくとも私には働きやすい良い会社でした。
楽しい楽しい充実した日々を駆け抜ける事ができました。

 

やり残した事も正直多いです。
ただ、それ以上にやりたい事も多くなりました。

退職の理由は「いい年してなにゆってるの?」と言われそうなので恥ずかしいのですが、同じ方向性のスキルや問題意識を持ったエンジニアの方々と肩を並べてお仕事をしたい、自分もトップエンジニアの領域まで登り詰めたいと思った事です。
そのために暫く充電期間を取ろうと思いました。

 

なお、今まで多くの企業からお声をかけていただきました。在職中にお話をしたところも多々あります。その時は状況とタイミングが悪かったため全てお断りをしていました。
今は心身ともに余裕があります。
お食事、飲み、勉強会、情報共有、またはそういうお話などなど、カジュアルにいつでもWelcomeです(・ω・)ノ

それでは、今後ともよろしくお願いします。

 

MurmurHash Java版

リハビリのためにMurmurHashをJavaに移植してみました。
Version1,2,3全てありますので、32bit,64bit,128bitのハッシュを求めることができます。

ソースはこちらです。
https://github.com/tamtam180/MurmurHash-For-Java

オリジナルと同じ結果を返すことは確認済みです。

MurmurHashってなんぞという人はオリジナルのサイトをどうぞ。
https://sites.google.com/site/murmurhash/
http://code.google.com/p/smhasher/

github便利だけど、バージョン管理して配布するような場合ってGoogle Projectの方が良いような気がしてる。パッケージをダウンロードさせたい場合、Githubではどうやるのかわかんないや。

HiveをEclipseを使って開発する時の手順で躓いた時の対処法

HiveをEclipse使って開発しようとするとき、こちらのページには以下のようにしましょうねと書いてあるのですが、

$ ant clean package eclipse-files
$ cd metastore
$ ant model-jar
$ cd ../ql
$ ant gen-test

最後の ant gen-test を実行すると、以下のエラーが発生します。(trunkのrev 1293919で確認)

BUILD FAILED
/home/tamtam/work/hive-trunk/ql/build.xml:116: Problem: failed to create task or type if
Cause: The name is undefined.
Action: Check the spelling.
Action: Check that any custom tasks/types have been declared.
Action: Check that any <presetdef>/<macrodef> declarations have taken place.

<if>というタスクが無いよーというエラーです。 ${hive.root}/testlibs/ant-contrib-1.0b3.jar このJARファイルのIfTaskは中に入っており、build.xml中でtaskdefしてあげないといけません。以下の定義を追加してあげる必要があります。${hive.root}/build.xmlには定義されているんですけどね。。

<taskdef resource="net/sf/antcontrib/antcontrib.properties">
  <classpath>
    <pathelement location="${hive.root}/testlibs/ant-contrib-1.0b3.jar"/>
  </classpath>
</taskdef>

この定義をql/build.xmlのgen-testのtarget配下に書いてもいいのですが、いろんなbuild.xmlがbuild-common.xmlをimportしていて、さらにbuild-common.xmlでも<if>タスクを使っているので、build-common.xmlに定義するのが良いかと思います。

diffはこんな感じです。

Index: build-common.xml
===================================================================
--- build-common.xml    (リビジョン 1293919)
+++ build-common.xml    (作業コピー)
@@ -89,6 +89,12 @@
   </condition>
   <import file="build-offline.xml"/>

+  <taskdef resource="net/sf/antcontrib/antcontrib.properties">
+    <classpath>
+      <pathelement location="${hive.root}/testlibs/ant-contrib-1.0b3.jar"/>
+    </classpath>
+  </taskdef>
+
   <!--this is the naming policy for artifacts we want pulled down-->
   <property name="ivy.artifact.retrieve.pattern" value="[conf]/[artifact]-[revision](-[classifier]).[ext]"/>

つか、誰も困って無いのかな・・。
それっぽいチケットも無いんだけど。

HiveのUDF一覧を自動生成してみた

発熱でDOWNしているタムタムです。
インフルエンザのようなすごい高熱というわけでもなく、プリン食べて家でしょぼーんとしてます。

今回はHiveのUDF一覧を自動生成してみました。(Hive0.8)
http://mt.orz.at/archives/hive_udf_0.8.html
(追記) Hive0.8用はURLを変更して、こちらにtrunk(rev1293519)のバージョンを起きました。

reflect関数とかWikiには載っていないと思います。
builtinと書いてあるやつはFunctionRegistryのstaticイニシャライザで関数登録しているやつです。Alias名がある関数はそれぞれ1つの項目に作ってあります。

作り方は至ってシンプルで、UDF関数はDescriptionというアノテーションが定義されています。desc function xxxxという構文で表示される説明は、このアノテーションの情報を表示しているだけです。というわけで、SpringFrameworkのClassPathScanningCandidateComponentProviderを使ってClassPath中のDescriptionアノテーションが定義してあるクラスを取得して情報を拾っているだけです。

こんな感じです。

ClassPathScanningCandidateComponentProvider scanner =
        new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(Description.class));
Set<BeanDefinition> beans = scanner.findCandidateComponents("");

取得したBean定義を回して、リフレクション使ってAnnotation情報を取得します。
nameをカンマで区切っているのは、Aliasがある場合はカンマ区切りで入っているからです。あとはdescから情報を普通に引っこ抜けます。

for (BeanDefinition def : beans) {
    Class<?> clazz = Class.forName(def.getBeanClassName());

    Description desc = AnnotationUtils.findAnnotation(clazz, Description.class);
    String[] funcNames = desc.name().split(",");
}

簡単ですね (=ω=)b

Hiveのテスト方法とJIRAチケットの開き方

Hiveのチケットを開いてBug報告をしてみたのでその手順をメモメモ。せっかくなのでテストの方法とかも書いておきます。
(チケットの内容はほとんどshiumachiさんに書いたもらったものですけどね(ノ∀`))
shiumachiさん、丁寧に教えていただきましてありがとうございます。

https://cwiki.apache.org/confluence/display/Hive/HowToContribute
Wikiにも書いてあるので、こちらを参考にすると良いと思います。

■ trunkで再現するかの確認について
Eclipseを使ってやる場合は、こちらのページが参考になると思います。 今回は簡単な修正だったのでコンソール上だけで行いました。 trunkを落としてきてビルド、Hiveを起動する手順を以下に書きました。設定ファイルを特に作っていないのでMetastoreはカレントディレクトリに作られます。MetastoreをMySQLに入れるところがーとかを修正する場合は、ちゃんとconfの下に設定ファイルを作って設定しましょう。 (あ、HADOOP系の環境変数は設定済みの前提です)

$ git clone git://git.apache.org/hive.git
$ cd hive
$ ant clean package
$ cd build/dist
$ export HIVE_HOME=`pwd`
$ cd bin
$ ./hive

今回初めてgitを使いました。 Hiveの規模であれば、--depthオプション無くてもいけます。 svnの場合はwikiにある通りに、以下のようにソースを持って来ます。

svn co http://svn.apache.org/repos/asf/hive/trunk hive-trunk

■ソースを修正したらテストをします。
私のPCではテスト全部通すと4時間程度かかりました。

全てのテストを実行する場合は以下のように実行します。テストが終わったらテストレポートも作ります。 テストレポートはbuild/testディレクトリにjunit-noframes.htmlというファイルが作られます。このレポートファイルを見ると、何のテストが失敗したのかと、簡単な情報(例えばExceptionが出ているとか)がわかります。

$ ant test
$ ant testreport

個別にテストをする場合は、以下のようにします。qファイルはフルパスではなくファイル名を指定します。(以下の場合はql/src/test/queries/clientpositive/timestamp_udf.qのテストをしています)

$ ant test -Dtestcase=TestCliDriver -Dqfile=timestamp_udf.q -Dtest.silent=false

なお指定したqueryのテストに対応する正解データは.q.outファイルです。上記の場合は、ql/src/test/results/clientpositive/timestamp_udf.q.outのファイルが正解データとなります。なお、正解データを実行結果で上書きする事ができます。まぁこれ手で正解データ作るのは無理ですw 以下のコマンドで上書きできます(-Doverwrite=trueを追加)。

$ ant test -Dtestcase=TestCliDriver -Dqfile=timestamp_udf.q -Doverwrite=true -Dtest.silent=false

私は今回上記のテストケースファイルに追加する形で対応しました。
テストする時は、上記のように影響があるテストをピンポイントで実行して最後にテストを全部通すと良いと思います。
全体テストは時間がかかるので、並列実行する方法があります。(直列のまま実行する場合は、寝る前に実行すれば良いと思うよ!!)

テストに関する情報は、こちらのWikiのページのTestingの項目が参考になります。
(パッチを投稿する場合)テストが通ったらパッチを作ります。

■パッチの作り方について

パッチの作り方はもHow To Contributeのページに書いてあるとおり、以下のように作ります。 パッチファイルはテキストファイルとして作った方がブラウザで見れるのでその方が良いです。「Appending '.txt' to the patch file name makes it easy to quickly view the contents of the patch in a web browser.」とWikiに書いてありますし、shiumachiさんからもその方がいいよーとの事です。

git diff --no-prefix > HIVE-xxxx.1.patch.txt

■JIRAのチケットをOPENします。

ちなみに、 https://gist.github.com/1797071 これを参考にすると良いよ!
shiumachiさんの添削が載っているので、とっても参考になると思います。
(githubのアカウントもこの時に作ったので、今度からちょこちょこ何かソースアップしていきます。)

実際にOPENする方法は、まずはJIRAのアカウントを作ります。(アカウントの設定でLanguageを日本語に出来ますが、逆にわかりづらくなって発狂するのでEnglishのままのが良いです。(HiveのJIRAのページはここです)
右上のCreateIssueを押して「Hive」「Bug」を選択して「Create」を押すか、その下のCreate: Bugを押します。(下の絵を参照)

20120213_jira_ticket_01.png

Issueの内容ですが、IssueTypeにはBugが設定されていると思うので、他の項目を入力していきます。

Summary: いわゆるタイトルです。
Priority: この項目は以下を参考にします。

Blocker: Blocks development and/or testing work, production could not run
Critical: Crashes, loss of data, severe memory leak.
Major: Major loss of function.
Minor: Minor loss of function, or other problem where easy workaround is present.
Trivial: Cosmetic problem like misspelt words or misaligned text.

Component/s: 対象のコンポーネントを選択します。今回は「UDF」を選択しました。
Affects Version/s: 発生するバージョンを選択します。今回はHive0.8を選択しました。Trunkの場合は選択しないようです。選択されているチケットが少ない気がするのですが、リリースされているバージョンで発生するので、ちゃんと選択してみました。
Fix Version/s: 選択しませんでした。(これはコミッターが後で選択するもの?)
Environment: なんか書いている人が少ない。。Hadoopのバージョンとか書くのかな・・。空欄にしちゃいました。
Description: チケットの内容を書きます。

あとは空欄にしちゃいました。(ノ∀`)

パッチを添付する場合は一度チケットを作ってから添付します。
More ActionsのAttach Filesを選択すると添付できます。

実際に作ったチケットはこれです。
https://issues.apache.org/jira/browse/HIVE-2803

何か作法が間違っていたら追記していきます(・x・;)

Hive0.8のfrom_utc_timestamp, to_utc_timestampの不具合を修正する

HadoopアドベントカレンダーでTimestampの機能について書いた時に見つけた不具合を調査してみました。 自分の備忘録用にメモメモ。

from_utc_timestampまたはto_utc_timestampをSELECT句に複数書くと値がおかしくなるというものです。正確には、1つ書いても壊れます。まずは再現コード。

create table ts1(t1 timestamp);
load data local inpath '/mnt/iscsi/test1/data4.txt' overwrite into table ts1;
select t1 from ts1;
2011-12-25 09:00:00
select from_utc_timestamp(t1, 'JST') from ts1 limit 1;
2011-12-25 18:00:00

うまくいっているように見えますが、、

select t1, from_utc_timestamp(t1, 'JST'), t1 from ts1 limit 1;
2011-12-25 18:00:00     2011-12-25 18:00:00     2011-12-25 18:00:00

おや、全部の値が変わってしまいました。さらに、from_utc_timestamp(t1, 'JST')を追加してみます。

select t1, from_utc_timestamp(t1, 'JST'), t1, from_utc_timestamp(t1, 'JST') from ts1 limit 1;
2011-12-26 03:00:00     2011-12-26 03:00:00     2011-12-26 03:00:00     2011-12-26 03:00:00

SELECT句で書いたt1が全て同じ値になります。
次に実装クラスを見てみます。org.apache.hadoop.hive.ql.udf.genericパッケージのGenericUDFFromUtcTimestampとGenericUDFToUtcTimestampです。GenericUDFToUtcTimestampはGenericUDFFromUtcTimestampを継承していて、フラグに応じてOffset値の正負を反転させているだけです。で、問題はGenericUDFFromUtcTimestampのapplyOffsetメソッドです。ソースを下に書きました。

  @Override
  public Object evaluate(DeferredObject[] arguments) throws HiveException {
    Object o0 = arguments[0].get();
    TimeZone timezone = null;
    if (o0 == null) {
      return null;
    }

    if (arguments.length > 1 && arguments[1] != null) {
      Text text = textConverter.convert(arguments[1].get());
      if (text != null) {
        timezone = TimeZone.getTimeZone(text.toString());
      }
    } else {
      return null;
    }

    Timestamp timestamp = ((TimestampWritable) timestampConverter.convert(o0))
        .getTimestamp();

    int offset = timezone.getOffset(timestamp.getTime());
    if (invert()) {
      offset = -offset;
    }
    return applyOffset(offset, timestamp);
  }

  protected Timestamp applyOffset(long offset, Timestamp t) {
    long newTime = t.getTime() + offset;
    int nanos = (int) (t.getNanos() + (newTime % 1000) * 1000000);
    t.setTime(newTime);
    t.setNanos(nanos);

    return t;
  }

行を評価中、timestamp変数は同じインスタンスを毎回返してきます。そのインスタンスに対してapplyOffsetでsetTimeしちゃってます。つまり、その行を評価中はt1は同じインスタンスを使い回してしまいます。なのでSELECT句に書いたOffset値全てが計算された結果が出力されるわけです。そして、1列評価→出力→1列評価→出力ではなく、全ての列を評価→出力なので、出力時には全て同じtimestampインスタンスを指すことになって、全て同じ値が表示されるわけです。(ここで言う全てはt1を参照している列という意味です)

修正方法ですが、結局のところは同じインスタンスを指せないという事で、インスタンスを生成する必要が出てきます。というわけで、手っ取り早く修正するのであればapplyOffsetを修正します。以下が修正版です。

  protected Timestamp applyOffset(long offset, Timestamp t) {
    long newTime = t.getTime() + offset;
    int nanos = (int) (t.getNanos() + (newTime % 1000) * 1000000);
    Timestamp t2 = new Timestamp(newTime);
    t2.setNanos(nanos);

    return t2;
  }

(追記)上記のコードはダメでした。nano時間のところがそもそもバグっていて秒未満の値がずれます。以下のコードの方が良いです。(timestamp_udf.q.outの秒未満部分もずれてました・・)

  protected Timestamp applyOffset(long offset, Timestamp t) {
    long newTime = t.getTime() + offset;
    Timestamp t2 = new Timestamp(newTime);
    t2.setNanos(t.getNanos());

    return t2;
  }

hiveをビルドしなおして再度実行してみます。 

select t1, from_utc_timestamp(t1, 'JST'), t1, from_utc_timestamp(t1, 'JST') from ts1 limit 1;
2011-12-25 09:00:00     2011-12-25 18:00:00     2011-12-25 09:00:00     2011-12-25 18:00:00

直りました。
さて、Hadoop、Hiveのコミュニティっていきなりチケットを開いてもいいのかな。ちょっとMLとJIRAを漁って作法を調べよう。

 

チケット開く前にパッチを置いておこう。Javaソースとテスト用のqファイル、q.outファイルのパッチは分離して2つに分けた方が良かったかな・・。
http://mt.orz.at/archives/HIVE-xxxx.1.patch.txt

 

Home

Search
Feeds

Return to page top