ソフトウェアデザイン1月号(2014)もRiak CSとRiakの話

先月も宣伝したが、今月も引き続きRiak CSとRiakの話。分散システムでどうやって削除を実用的に構築するかという非常にマニアックな話と、Riakの上で時系列データをどう扱えばいいかの2本立て。MapReduceの実例もあるので非常によいと思うよ!

はい、非常に混みいった話になってしまい反省しております…

msgpack-erlang 0.2.7 をリリースして新仕様に対応しました

Kyle Academy new extension

MessagePackハッカソン #3が開催され、Bashoで場所を提供したので僕も久しぶりになんかやることにして、そういえばmsgpack-erlang新仕様の実装が途中だったのでやることにした。で、できた。というわけで、さまざまな事件を乗り越えて策定された新仕様をErlangのサンプルコードつきで解説しよう。新仕様の目玉はふたつあるので、それぞれ解説しておきたい。

文字列型

これまでMessagePackは「ほぼJSON互換」だったのだが、その正体は、Cらしくバイナリ型(raw)を <<101XXXXX>> とか 0xDA, 0xDB で表現していた。一方JSONにはバイナリ型はなくて、文字列はUnicodeでなければならないので、生のバイナリをそのままJSONに持っていくことはできなかった。で、新しい仕様ではこれまでバイナリ型に割り当てられていたところを含めて、新しく文字列型をつくった( <<101XXXXX>>, 0xD9, 0xDA, 0xDB)。新仕様では、バイナリ型には新しく 0xC4, 0xC5, 0xC6 が割り当てられた。

で、ライブラリを作る立場としては、まあ最初はオプショナルでこれを実装しようということになる。使い方は簡単で、 enable_str というオプションを一緒に渡してやるだけ(公式のドキュメントは間違っている。私がまだ修正していない)。

  {ok, Bin} = msgpack:pack("埼玉", [{enable_str=true}]),
  {ok, "埼玉"} = msgpack:unpack(Bin, [{enable_str=true}]).

ちなみに、デフォルトではこれは false になっていて、引数を省略したらどちらもこれまでと同様の動作をする。また、これを true にした場合は変換を試みるので自動的に Unicode のバリデーションは走ることになる。失敗するとコケるはず。確か。

拡張型

MessagePackがサポートするのは配列とかMap型とか整数とかそういったプリミティブな型ばかりだが、使っているうちにもうちょっとリッチな型というものがほしくなる。たとえば日付、時刻、その他オレにとっては便利な○○…文字列も初めはそうだったのだが。基本方針は以前と同様「ユーザー側でMessagePack上で好きなように規約やライブラリを作ってやってよー(バイナリレベルではやらない)」だったのだけど、やはり効率重視なのでシリアライザの層でちょっとは効率的にできるようになってほしい。というわけで、ユーザー定義の拡張型を仕様として用意することにした。ライブラリはまだ全然できてないので注意。で、Erlangではユーザー定義型のシリアライザとデシリアライザを関数として渡してやる。

Packer = fun({ref, Ref}, Opt) when is_ref(Ref) -> {ok, {12, term_to_binary(Ref)}} end,
Unpacker = fun(12, Bin) -> {ok, {ref, binary_to_term(Ref)}},
Ref = make_ref(),
Opt = [{ext,{Packer,Unpacker}}],
{ok, {ref, Ref}} = msgpack:unpack(msgpack:pack({ref, Ref}, Opt), Opt).

上の例だと、Erlangではよく使う参照(UUIDのようなもの)はこれまでのMessagePackではシリアライズできなかったのだが、拡張型を使ってコード(ここでは12)を割り当てることができるようになる。これで、すべてのErlang組み込み型がシリアライズできるようになるはず(もう少ししたら便利ライブラリを付け足すはず…)で、BERTやt2bでは多言語とのやりとりが大変だったけど、MessagePackと使い分けるのは面倒だったごく一部の人には朗報かと思います。

あと、拡張型を扱うためのbehaviour (msgpack_ext) も作った。ユーザーの好きなモジュールで、 pack_ext と unpack_ext を実装してbehaviourを定義すればatomを渡すだけで使えるようになる。

-behaviour(native_example).

pack_ext({native, Term}, _) when is_pid(Term) orelse
                                 is_reference(Term) orelse
                                 is_port(Term) orelse
                                 is_tuple(Term) orelse
                                 is_function(Term) ->
    {ok, {42, term_to_binary(Term)}}.

unpack_ext(42, Bin) ->
    {ok, {native, binary_to_term(Bin)}}.

とかしておけば、

  {ok, Bin} = msgpack:pack({native, make_ref()}, [{ext, native_example}]),

とすれば拡張型を使えるようになる。もちろん、既存のリストやMap型に入れ込んでも普通にデコードされる(はず…)。まだproperで網羅的なテストは書けていないので、有志のヘルプ求む。というか、この記事は普通にどっかに英語で書くべきだろうな…

Let's Do MessagePack!!

技術を実用化するということ

技術を実用化するということについて、真剣に考えれば考えるほど分からないことばかりが出てくる。よく基礎科学の分野で何の役に立つかわからないので仕分け対象になってしまうとか、浮世離れした数学者はこの世に必要なのかといった問題で、その技術とか科学は何のためにあるのか?いまそこに投資する価値はあるのか?という至極簡単な問題に到達する。
簡単に言えば何がなんの役に立つのか全くわからない、「生まれたばかりの赤子がなにを成し遂げるのか分かりますか?」という偉人の言葉をどこかで思い出す。

Evolution by Stupidiotic

岡野原さんが書いているように現実世界の問題を解決して、この社会を一歩でも改善すればその技術は役に立った、実用化に成功したということだと思う。そのための経路は沢山あって、OSSの実装から始まるものもある。論文から始まるものもある。そしても、最初にアイディアを誰かに伝えることが第一歩というとてもいい話だ。

私は分散システムの世界の人間を自負しているので、ここ数年で学んだことがある。アカデミアはときに難解になりすぎて現実世界から遠く離れていってしまう場合がある。現在の某広告会社、某流通書店などが画期的な分散システムの実装を世に発表してもうすぐ10年が経とうとしているが、彼らがそのシステムを試行錯誤しながら開発していた頃の状況というと1990年代から2002にかけての時代になるだろう。当時の分散システムのアカデミアというと、私はその場にはいなかったが伝え聞く限りは空理空論で、問題を解決するために問題を作り出していたと聞く。
その頃広告会社や流通書店は、自分たちの問題を解決するためにどうしたかというと、まずアカデミアに興味を持てなくなった分散システムの研究者たちを多く雇い、1990年ころまでには出尽くした理論を使って彼ら自身の問題を解決するためのシステムを作った。それが検索インデックスであったり、分散ストレージであったりするわけだ。こうして1990年代には誰が必要とするか全くわからなかった技術が、密室の中で実用化されていった。

実はそういったことはそれ以前にも起きていたように思う。某電話会社で開発されたOSや、某電話会社で開発されたコンピュータ、国勢調査の集計のために開発されたシステムなど…実世界の問題を解決するために密室の中で論文から実装へと変貌を遂げた技術も多くある。実用化に至った彼らが持っていて、実用化できなかった側が持っていなかったものとは何か?今でもそれを考えることがある。

子守に使える電子デバイスを正しく選ぶ方法

devil ducky on the sea of ikea

先月くらいに切込隊長がわりと真剣に批判していたことがNHKの朝イチで放送された。概要はこうだ。

子供を静かにさせるためのツールとしてiPadをはじめとするタブレットやスマホを与える親がいる。これは発達上、教育上よろしくないと小児科医会がアピールしているので控えるように。
例えば、投稿動画アプリなどを子供に与えてはならぬ、なぜなら列車事故の動画などをウッカリみてしまうためだ。

といって、列車事故の動画をその場で放送したのだ。残念ながらうちの息子はその投稿動画を見てしまった。これでは子供にテレビどころかNHKすら見せられぬのか…?と思ったらNHKさんネットの投稿動画を元ネタにした番組を持ってらっしゃるとはこりゃ実に滑稽でNHKも見せられんという結論にしかならない。

しかもこの話、科学的根拠は皆無という。どこかにあれば、読者諸君に教えていただきたい。参考:

とはいえ子供を持つ親としては、どうしてもそういった子供を静かにさせるオモチャが必要な場面はあり、子供がやりすぎないように注意する必要がある。番組やニュースでは「1日15分まで」といった寝言が飛び交っているが、これは技術的に解決できる問題だと私は知っている。

そこで我が家では SIMを抜いたiPhone4S, Retina iPad などを持たせている。これは、iOSデバイスやMacOSではペアレンタルコントロールが優れているためだ。そこで、子守に使える電子デバイスを正しく選ぶ方法(うちでやってる方法や考え方)をいくつか提示したい。運用については、某所に上がっているので、そちらを拝見するとよい。

Android

すこし調べたところ、Androidにもペアレンタルコントロールの機能はJelly Beanからついたようだ。もう少ししたら実用的になるかもしれないが、未熟だと判断した。またアプリストアにマルウェアがかなり出回っていることからセキュリティリスクも高いと判断した。もし「こうすれば簡単&安全だよ」という方法があれば是非知りたい。

Windows

息子が1歳の頃に最初の端末を与えたが、WP8はまだ日本では出ていない。Windows 7Vistaにそのような機能があったらしいが…

iOS, MacOS

eMacというのを出していたくらいで、90年代から教育用コンピュータの開発に熱心だっただけあって、さすがにペアレンタルコントロールの機能やインターフェースが充実している。システム設定にペアレンタルコントロールというパネルがあって、そこでかなりのカスタムができる。
アプリの設定やSafariがアクセスURLなどは、ホワイトリスト、ブラックリスト、全面禁止などかなり柔軟にカスタムできる。我が家では iTunes Store は禁止、YouTubeはインストールしない、SafariiOSでは表示しないようにしている。息子がお気に入りのアプリはGoogle Mapsだ。こちらの端末の管理は主に妻がしている。

iOSはアプリも豊富だが、これはこの際関係ない。

私がMacBookを触っているときは息子はどうしても触りたがるので、私のお下がりのMacBook Airを与えている。これも厳重にペアレンタルコントロールしていて、起動できるApplicationはかなり制限してある。彼が成長するに従って徐々に解除していく予定だ。Emacsはターミナルはまだ入れていない。もちろん管理者アカウントは与えず、基本的な管理は私が行っている。

Linux

親がLinuxを触っていると、やはり息子もターミナルを叩きたくなるらしい。そこで、Linuxでは彼のアカウントを別途作って、私がログインして使わせるようにしている(わたしの作業中のファイルを壊されてはかなわないので…)。そしてターミナルを開いて、 sl というコマンドをひとつだけ教えてある。

結論

  • こういった端末を買い与えると子育ては非常に楽になる
  • 約束事での制限などの運用をやる必要があるが、きつすぎる制限は意味がない
  • 制限を緩和するにはOSに適切なペアレンタルコントロールを設定すること
  • 私はいまのところ iOSMacOS を推奨

gen_queue というライブラリを公開しました

Queue

とても地味で、どこかにありそうな気しかしないのだけど、gen_queueという小さなライブラリを公開した。これはErlang/OTPのアプリケーションとして動作する小さなライブラリで、デフォルトで起動するとひとつのキューをもった軽量プロセスが一人だけ動きます。

同居する他のErlang/OTPアプリケーションは、このキューにデータを入れたり出したりできます。出すときに空だとブロックして待たせてくれます。待たずにエラーを返す nb_pop を作るのも簡単ですがすぐには必要ないのでやっていません。

そのうち、長さ制限をつけて、 push も blocking にしたり nb_push というAPIも作ろうかと思います。Erlang/OTPシステムで内部的なタスクキューが欲しい場合にこれを使うとよいと思います。

もうひとつ、できると面白いかなと思うのは、複数のキューを supervisor 配下に作ることです。 app.config か何かで名前を教えてあげると起動時にその数だけ名前つきでキュー持ちのプロセスを作る。

まああとは gen_* な名前を持つ者としては behavioural にして、ユーザー定義コールバック handle_hogehoge をするとか、pushされたらトリガー的動作をするとか、夢見がちなことはいくらでもできるんですけどとりあえずこんなところで。

ソフトウェアデザイン12月号はRiak CSとRiakの話の両方

ソフトウェアデザインの連載ももう半年近くなってきて、なかなか感慨が湧くかと思ったがそれどころではない多忙な日々を過ごしています。さて今月の記事はなかなか重量級で、Riak CSの設計から、Riakを使ったアプリケーションの設計技法を解説しています。

設計技法というと、モデリングとか、ORMとか、スキーマとか…?と思われるかもしれませんが、NoSQLはそれ以前の問題。そもそもSQLどころかトランザクションだってないので、データ構造を壊さないようにアプリケーションロジックを設計して動かすそもそもの仕組みを自分でやらなければいけません。RDBMSだとかなりの部分をデータベースに任せてとりあえず適当なスキーマをしてもトランザクションがデータの整合性を守ってくれますが、Riakのようなデータベースではそうはいきません。

そこで、Riak上でCASのようなものとかユーザー管理をとりあえず作りたい場合はどうするか、オブジェクトの状態遷移をVector Clocks(というか、RiakのSiblings…)を使ってどのように実現するか、といったことを、実際にRiakを作ったエンジニアが設計したRiak CSを例にして解説しています。中の人が作ったのでとりあえず正しいというか、安全な方法というか、後から入った僕が見てもよくできてるなと感動するレベルで面白いです。そして面白いということは、かなり難解でして…私の文章が至らぬところもあると思うので、読んでくださって質問があるときは適当に @kuenishi なりに投げてみてください。空中リプだと見逃す可能性もありますがあしからず。

あまりに重量級なので1月では書ききれず、2部に分かれています。そしてその第2部を今まさに執筆中でして、これがなかなか…ちょっとアルコールでも入れるか。

MacPortsのErlang/OTPでRiakをまともにうごかす

ぼく自信はもうソースから手でインストールする暮らしになっているので不要なのだが、最近closeされたらしいのでここに努力の結晶を書き残しておこう。ErlangがHiPE有効な状態でインストールされていると、RiakをHiPE有効な状態でコンパイルしてしまい、中のなんとかいうモジュールがスタックオーバーフローしてRiakがまともに動かなかった時期があった。まあHiPEなしのコンパイル

$ ./configure --disable-hipe

としておけばよいのだが、MacPortsだとこれを入れるためにはvariantを指定しないといけなかった:

$ sudo port install erlang +nohipe

これはMacPortsが + で始まるvariantしか受け付けなかった時代の名残りで、しかもあろうことかバグっていた(のを僕が報告した)。

これが気付いたらいつの間にか本体に入っていたらしいので、MacPorts使いの人は

$ sudo port install erlang -- -hipe

ってやって入れるとR16B02が入る?と思うよ。それにしても -- ってすごいな
Riakの1.4はR16B系だとコンパイルできないので2.0系(preリリースしかないけど)で試してみてください。

モダンErlang/OTP開発環境 2013

とりあえずモダン○○といっとけばいいかなと思ったが、モダニズムとか言ってた頃からモダンという言葉には「最近の」「ナウい」とかその程度の意味しかないのでそんなにありがたい話ではないことにご注意いただきたい。大正とか昭和初期のオバちゃんが「いや〜モダンやわぁ〜」とか言ってたのと同じだ。今ではもうばあちゃんだな。

エディタ

世間にはエディタを巡って宗教対立が発生し、場合によっては宗教戦争になるそうだがErlang/OTPの世界においてはそれは起き得ない。なぜならEmacsがデフォルトの開発環境として推奨されており、メジャーモードが処理系のディストリビューションに含まれているからだ。さまざまな便利ショートカットが用意されているので M-x tempo- と入れて補完されるものを眺めるとよいだろう。

パスを通して

(require 'erlang-start)
(require 'erlang-flymake)

してやるとよい。

ちなみにVimEclipseと同じくらいマイノリティだ。これらのマイノリティを採用する場合は、Emacsに比べて10〜100倍ほど生産性に差が出るレベルでないとやめた方がよい。

ソースコードを読むとき、ちゃんとしたい人はGTAGSか、erlcscopeがよいだろう。erlcscopeはcscopeとは別の実装だが、Emacsのcscope.elと相性がよいので僕もたまに使う。たまに使うが、普段はgrepだ。

OS

Erlang/OTPは現在、Linuxが最もメジャーなOSだろう。商用環境で次に多いのはSolaris系のOSだ。開発環境の場合はMacOSを使える場合もあるが、dtraceを使えるようになる代わりに本番環境との微妙な違いに悩むことが多いだろう。NIFなどのC拡張があると余計に面倒が増える。私は基本的にLinuxを使っている。ちなみにソースをみるとわかるが、他にもマニアックな*nix系OSに対応している。
BSD系もユーザーがいないことはない。Windowsでも動かないことはないが茨の道だろう。

コンパイラと処理系

Ericssonが出しているもの以外の処理系では、Triforkが公開しているErjangが有名だ。組み込み向けのものもあったように記憶している。真面目にErlang/OTPを使いたい場合はローカルで複数のバージョンを用意しておくことをすすめる。rbenvのようなものとしてはkerlがあるが、rbenvとちがってRubygemsのようなエコシステムはないので大して役に経たない。

最初はOSのディストリビューションが提供するものでもよいかもしれないが、本気で使うならソースから入れておくのがよいだろう。僕は /usr/local/erlang 以下に

$ ls /usr/local/erlang
R14B04  R15B01  R15B01.zdss  R15B03  R16B  R16B01  eqc

と、prefix指定したものをいろいろと入れている。PATHをbinに通すだけで使い分けられるので便利だ:

$ export PATH=/usr/local/erlang/R16B01/bin:$PATH

コンパイルオプションはいつもこんな感じだ:

MacOS

$ ./configure --enable-darwin-64bit --disable-hipe --enable-vm-probes --with-dynamic-trace=dtrace --prefix=/usr/local/erlang/R16B01

Linux

$ sudo aptitude install xsltproc fop libssl-dev
$ ./configure --enable-m64-build --disable-hipe  --prefix=/usr/local/erlang/R16B01

この過程でncurses-devなど必要なものを指摘されるので注意しておくこと。wxは別になくてもよいだろう。javaがないとwithout指定か何かが必要だった気がするが、特に気にしてないので記憶していない。

manpagesも、ソースから入れておくとよい。とんぷー氏のブログ記事が参考になるがここにも転載しておく。

$ make docs
$ make install-docs

これを動かすためにはfopやxsltprocなどいろいろと必要になるので都度インストールすること。

ビルドツール

ビルドツールの選定ほど面倒なことはない。しかしErlang/OTPではrebar一択だ。もしもautomakeなど、rebar以外のツールを使っているプロジェクトがあったら、それは少しあやしいので眉に唾をつけるとよいだろう。rebarをちいさなMakefileでラップするのが一般的だ。Makefileのサンプルもあるので参考にするとよい。

rebarは依存ライブラリをバージョンつきで指定することができる。これはとても便利だが、ダイヤモンド依存になったときに依存しているバージョンが異なる場合があるので注意すること。

VCS

rebarがそのサービスをサポートしているものを使うとよい。git, hgなどいくつかあるので、github, bitbucketなどのレポジトリを依存ライブラリとしてバージョン指定するとよいだろう。

テスト環境

ユニットテストはeunitかct(common tests)で書くのが一般的だ。テスト用のライブラリはmeck, properなどが代表的だ。Dialyzerという静的な型解析ツールもある。テストは分野によって書き方や使い方が全然違うので自分に合ったものを模索するしかない。

僕の会社ではeunitでQuickCheck (プロプラ)を流して、結合は riak_test というのを使っている。

人によってはTravis-CIやdrone.ioなども合わせて使うとよいだろう。この辺はErlang/OTPに独特ということはあまりない。

パッケージング

Erlang/OTPはそれ自体、パッケージングやリリースのための仕組みが沢山ある。交換機は無停止でシステムを更新する必要があったから、そういったところも処理系に組み込まれている…のだが、これが滅法使いにくい。なので、rebarがこれをサポートするコマンドを作って、tarball一発まで作れるようになっている。これが便利なのは、Erlangの処理系もリリースパッケージに一緒に入れてしまうので、本番環境で特に環境を作る必要がほとんどないためだ。Erlangを頑張ってインストールする必要はない(goみたいですごいでしょ!)。

しかし、これだけだとtarballなど独特の仕組みになってしまい、設定ファイルなどもまだ扱いにくい。そこで登場するのが、 node_package だ。これを使うことで、APT, yum, SmartOS, ports など様々な環境に向けたリリースパッケージを構築してくれる。設定ファイルやログの出力など、それぞれのOSの流儀に合ったものにしてくれる。コマンドひとつで.debパッケージを作るの、昔からの夢だったんだ…!

ライブラリ

これ!といった定番がいくつかあるので…とはいえ、いろいろありすぎて面倒なのでパス。

ドキュメント

edocというのがあり、これもrebarを使えば一発でHTMLを吐いてくれる。使っている人もいるし使っていない人もいる。たいていはREADME.mdで済ませてしまっている。

デバッガ

基本的には printf というデバッガが最もよく使われている。いちおう、C拡張などをテストするため gdb も使えたりするのだが… distel は emacs と相性がよく昔から使われている。また、最近では redbug というツールがとてもよい。これは予めプロダクションシステムに仕込んでおけば、サービスを止めずにコールフローを追ったりフックを仕掛けたりやめたりできる優れものなので使い方を覚えておくとよいだろう。

まとめ

erlang-users.jpというサイトを最近復活させたので、ヒマがあったらそちらを覗いてみると新しい情報がなにか出ているかも?!