株式会社クリアコード > ククログ

ククログ


FirefoxでInternet Explorerのwindow.open()周りの挙動を再現する

日本におけるFirefoxの法人利用の代表的なニーズは「Internet Explorer向けに作られた古いWebベースの社内システムがあるためにIEを捨てられず、社外のWebを見に行くためにFirefoxを使いたい」というものです。しかし、それとは逆のパターンとして、「IE向けに作られたWebベースのシステムをFirefoxで使いたい」という場面もあります。後者の場合にはIEとFirefoxの細かな挙動の違いが問題となることがあり、window.open() でウィンドウを開くときの挙動の違いもその一つです。

名前付きの子ウィンドウ(タブ)の挙動の違い

window.open() はJavaScriptから利用できるブラウザの機能(いわゆるDOM0)で、子ウィンドウ(タブ)を開く物です。第2引数には「子ウィンドウの名前」を指定することができ、この方法を使って名前を付けて開かれた子ウィンドウ(タブ)は、<a target="ウィンドウ名" href="..."> のようにtarget属性を明示したリンクの読み込み先や、window.open() で同じウィンドウ名を指定した場合の読み込み先として再利用されることになります。

Internet Explorerでは、このような場面で以下のような挙動を取ります。

  • すでにその名前の子ウィンドウ(タブ)が開かれていた場合、とにかくそれを読み込み先として使う。
  • 読み込み先の子ウィンドウが最小化状態であれば、通常の状態に復帰させる。他のウィンドウの背後に隠れている場合であれば、最前面に持ってくる。

他方、Firefoxでは同じ場面で以下のような挙動を取ります。

  • 子ウィンドウはそれぞれのタブに対して個別に管理される。2つのタブそれぞれで子ウィンドウを名前を指定して開いた場合、それぞれのタブに対して別々の子ウィンドウが開かれる。その後のウィンドウ名を指定した読み込みも同様に取り扱われる。
    • →IEの挙動を前提に作られたWebアプリだと、無駄にたくさん子ウィンドウが開かれてしまう。
  • 読み込み先の子ウィンドウが最小化状態や他のウィンドウの背後に隠れている場合でも、そのままの状態で読み込みを行う。
    • →IEの挙動を前提に作られたWebアプリだと、「リンクやボタンをクリックしても(最小化された、または背後にあるウィンドウの中に読み込まれる場合があり)結果を画面上で確認できないという事が起こる。

Firefoxアドオンでの解決

Webアプリとしては、このような挙動の違いを吸収したり、Firefoxのような挙動でも問題が起こらないようにしたりと対策を取れればそれに越したことはありません。しかし、様々な事情からそれができない場合もあります。

そのようなお客さまからのご相談を受けて、Firefox上でIEの上記の挙動を再現するためのアドオンを開発しました。以下のリンクからインストールできます。

  • Merge Named Browser Windows:ウィンドウ名を指定して子ウィンドウ(タブ)が開かれた場合に、親ウィンドウ(タブ)を問わずその名前の既存のタブを探して閉じ、特定の名前のタブが1つだけ開かれているという状態を維持する。
  • Unminimize Browser Window On Load:最小化状態や背面にあるウィンドウにコンテンツが読み込まれた場合に、ウィンドウを復帰させフォーカスを与える。

主にセキュリティ向上のために、現代のWebブラウザはウィンドウ(タブ)間での情報の分離を徹底する方向で進化しています。また、迷惑な広告やいわゆるブラクラの類にユーザーが煩わされる事が無いよう、コンテンツ側からの操作によるウィンドウのフォーカス制御も制限されるようになっています。それらの変化は一般的なWebサイトを閲覧するユーザーにとって好ましいものですが、特定のWebアプリの使用頻度が高くなる法人利用では却って困った結果になる事もあります。

そのような特殊な場面での暫定的な対処のための方法として、一般的なWebアプリよりも強力な権限が与えられているアドオン(拡張機能)は有用な選択肢となる場合があります。Firefoxの法人利用で似たような事にお悩みの企業担当者さまがいらっしゃいましたら、お問い合わせフォームより是非ご連絡下さい。

タグ: Mozilla
2019-11-26

2019年、fat gemをやめる

fat gemを簡単に作れるようにするgemであるrake-compilerをメンテナンスしている須藤です。過去にfat gemの作り方をまとめたこともあります。

fat gemが有用な時代もあったのですが、今はメリットよりもデメリットの方が大きいのでfat gemをやめたらどうか、という話をします。

fat gemについて

fat gemとはビルド済みバイナリーが入ったgemのことです。Pythonで言えばwheelのようなものです。

RubyはC言語でRuby用のライブラリーを実装することができます。これを拡張ライブラリーと呼びます。拡張ライブラリーの用途は主に高速化(Rubyで実装したよりCで実装した方が速い)とバインディング(C・C++言語で実装されたライブラリーをRubyから使えるようにするライブラリー)です。

拡張ライブラリーをインストールするにはC言語のプログラムをビルドしなければいけません。ビルドするためにはインストール時にC言語のビルド環境が必要になります。ユーザーの環境にC言語のビルド環境がないとインストールできません。つまり、インストールの敷居が高くなります。

このインストールの敷居が高い問題を解決するものがfat gemです。fat gemにはビルド済みのバイナリーが含まれているためユーザーはC言語のビルド環境を用意しなくてもいいのです。やったー!

fat gemの問題点

fat gemのおかげでユーザーはハッピーになれそうですね。でも、本当にそうでしょうか?実際に、fat gemを作ってきた経験からfat gemの問題点を説明します。

新しいRubyを使えるまでにタイムラグがある

Rubyは毎年クリスマスに新しいバージョンがリリースされます。(メンテナンスリリースは必要に応じて随時行われています。)

fat gemにはビルド済みのバイナリーが含まれています。これは、だれかが事前にビルドしてくれているということです。だれかというのはgemの開発者です。

Rubyはバージョン間でのABIの互換性を保証していません。たとえば、Ruby 2.6用の拡張ライブラリーはRuby 2.7では使えません。そのため、新しいバージョンのRubyがリリースされたらそのバージョンのRuby用にビルドしないといけません。(メンテナンスリリースでは互換性があるのでRuby 2.6.0用の拡張ライブラリーはRuby 2.6.5でも使えます。)

つまり、新しいRubyがでてもgemの開発者が新しいRuby用のfat gemをビルドしてくれていなければ新しいRubyを使えません。すべてのgemがRubyのリリースにあわせて開発スケジュールを立てているわけではないので、クリスマスから数ヶ月遅れて新しいRuby用のfat gemをリリースすることも十分ありえます。

リリースされるならまだいいですが、メンテナンスモードになっているgemは重大な問題がなければ数ヶ月経ってもリリースされないかもしれません。もし、リリースされないgemが変更無しで新しいRubyでビルドできたとしてもユーザーは使えません。(RubyのC APIはそんなに劇的に変わらないのでだいたいビルドできます。)

もし、ユーザーが複数のfat gemに依存している場合はすべてのfat gemが新しいRubyに対応しなければ新しいRubyを使えません。1つでも新しいRubyに対応していないとインストールできないからです。

脆弱性対応までにタイムラグがある

バインディングをfat gemにするということはバインディング対象のライブラリーもfat gem内に入れていることになります。もし、バインディング対象のライブラリーに脆弱性があった場合は迅速に修正版をリリースするべきです。そうしないとユーザーが危険な状態が伸びてしまうからです。

しかし、すべてのfat gem開発者がそんなにタイミングよく作業できるわけではありません。そのため、脆弱性対応リリースが遅くなりがちです。

念のため補足しておくと、これはfat gemにしていない場合でも発生しえます。たとえば、Nokogiriのようにデフォルトで特定バージョンの依存ライブラリーをビルドするタイプのgemでも発生しえます。指定したバージョンのライブラリーに脆弱性があったらバージョンを更新してリリースしないと脆弱なバージョンを使ったままのユーザーが増えてしまいます。明示的に--use-system-librariesを指定すればNokogiriのバージョンを上げなくても対応できるのですが、残念ながら多くのユーザーはそこまで頑張ってくれないでしょう。

新しい依存ライブラリーを使えるまでにタイムラグがある

これもバインディングの場合ですが、バインディング対象のライブラリーが新しいバージョンをリリースしてもfat gemを更新しなければユーザーは新しいバージョンを使えません。

fat gemに対応するとrequireが遅くなる

fat gemに対応するには次のようなコードを入れる必要があります。2.6/io/console.so(ビルド済みバイナリー)があればそっちを優先し、なければio/console.so(自分でビルドしたバイナリー)を読み込むというロジックです。

begin
  require "#{RUBY_VERSION[/\d+\.\d+/]}/io/console.so"
rescue LoadError
  require 'io/console.so'
end

すべてのケースでfat gemを使うなら↓だけで大丈夫です。

require "#{RUBY_VERSION[/\d+\.\d+/]}/io/console.so"

しかし、それではユーザーが自分でビルドして使うという選択肢がなくなります。逆に言うと、開発者がすべてのプラットフォーム向けにfat gemを用意する覚悟を決める必要があります。

それは現実的ではないので、普通は前述のように自分でビルドしたバイナリーにフォールバックします。

そうすると、fat gemを提供していない環境では必ずfat gem用のrequireが失敗します。この分requireが遅くなるということです。$LOAD_PATHにたくさんのパスが入っている環境では無視できないくらい遅くなります。gemをたくさんインストールしているとその分$LOAD_PATHも大きくなります。たとえばRuby on Railsアプリケーションではたくさんのgemを使うことになるので影響が大きいです。

これを回避するために、fat gemを提供している環境でだけフォールバックする対策をとっているgemもあります。(ありました。)

fat gemのリリースを忘れる

多くのgemはrake releaseだけでgemをリリースできるようにしています。そしてこれはすぐに完了します。.gemファイルを作ってrubygems.orgにアップロードするだけだからです。(他にもgit tagをするとかちょろっとしたことをしています。)

しかし、fat gemをリリースするにはもう一手間必要です。各環境用のバイナリーをビルドしてそれぞれの環境毎にfat gemを作り、それらをrubygems.orgにアップロードします。

各環境用のバイナリーは大変です。rake-compiler-dockを使えば楽になりますが、それでも面倒です。

その結果どうなるかというとリリースが億劫になったり、fat gemのリリースを忘れたりします。たとえば、Ruby-GNOMEはリリースが億劫だなと思っていました。たとえば、io-console 0.4.8はfat gemのリリースを忘れていました

fat gemのリリースが大変

fat gemのリリースは大変なんです。特にバインディングのfat gemのリリースは大変です。

私はRuby-GNOMEでWindows用のfat gemを作っていました。バインディング対象のGTKなどのライブラリーはLinux上でMinGWを使ってクロスコンパイルしていました。これがすごく大変です。というのは、クロスコンパイルしている人がほとんどいないので、バインディング対象のライブラリーのバージョンを上げるとビルドエラーになることがよくあるからです。Ruby-GNOMEをリリースするたびにアップストリームにパッチを送っていたものです。ただ、librsvgがRustを使うようになってクロスコンパイルできなくなったときにfat gemをやめる決心をしました。

fat gemはそんなにポータブルじゃない(気がする)

fat gemは主にWindows向けに提供されていますが、Linux向けに提供している野心的なgemもあります。たとえば、sasscです。

Windowsはバージョンが限られていますし、後方互換性があるので古いWindows向けにビルドしていればいろんなWindowsでもだいたい大丈夫です。

しかし、Linuxディストリビューションはたくさんあり、使っているlibcも違います。スタティックリンクしたバイナリーを用意すれば大丈夫なのかというとそうでもない気がします。どうなんでしょうか。。。?

Pythonのwheelではmanylinuxという(だいたいの)Linux環境で動く仕組み(?)を用意しているので、このくらい頑張れば大丈夫なのかもしれません。が、私としては、この方向で頑張っちゃうの。。。?という気持ちになります。RubyGemsはそうなって欲しくないなという気持ちです。

fat gemの問題点の解決方法

ここまででfat gemの問題点をまとめました。それではfat gemの問題点を解決する方法を示します。それはfat gemをやめることです。どーん!

そもそもfat gemが必要だったのはユーザーがビルド環境を持っていないことが多かったからです。しかし、今は状況が変わっています。ユーザーがビルド環境を持っていないプラットフォームの代表はWindowsでしたが、今はRubyInstaller for Windowsがほぼ標準でDevKitを提供しています。Ruby 2.3以前はそうではなかったですが、Ruby 2.3がEOLになったので、今はWindowsユーザーでもビルド環境があるのです。

Linuxではパッケージをインストールすればすぐにビルド環境を整えられます。

macOSでもXcodeをインストールすればビルド環境を整えられます。Homebrewを使っている人はすでに整っているはずです。

他の環境(たとえば*BSD)でもビルド環境はすぐに整えられるでしょう。

つまり、今はユーザーがビルド環境を持っていると仮定してもよい状態になっています。そのため、fat gemを提供しなくてもユーザーがインストールできる状態が整っています。実際、私はWindowsユーザーがいるRuby-GNOMEでfat gemをやめましたが、最近はWindowsでのインストールトラブルはほとんど報告されていません。

それではfat gemをやめるとうれしいことをまとめます。

新しいRubyをすぐに使える

新しいバージョンのRubyは以前のバージョンのRubyとC APIが変わっている可能性があります。たとえば、Ruby 2.7ではrb_f_notimplement()が変わります。(Ruby 2.7の対応が必要な例)

しかし、多くのC APIは互換性があるのでなにも変更しなくても新しいRubyで動くことが多いです。その場合は、特になにもしなくてもすぐに新しいRubyを使えます。ユーザーは単に新しいRubyを使ってgemをインストールすればよいだけだからです。

また、もしRuby 2.7で動かない場合でも事前にプレビュー版で動作確認し、Ruby 2.7より前にRuby 2.7対応版をリリースしておくこともできます。こうすればクリスマス後にリリースしなくてもよくなるのでgem開発者に余裕があります。

fat gemを使った場合でも、プレビュー版でバイナリーを作ってリリースしておくことができなくはありませんが、rake-compiler-dockなど各種ツールが事前に対応していないと難しいです。

脆弱性対応をシステムに任せられる

バインディングをシステムのライブラリーを使ってビルドするようにしていた場合、バインディング対象のライブラリーに脆弱性があってもシステムのライブラリーを更新すれば対策できます。gemの開発チームよりシステムのライブラリーをメンテナンスしている人たちの方が層が厚いいので迅速に脆弱性に対応してもらえます。

なお、Nokogiriのようにデフォルトで依存ライブラリーを自前で管理するタイプのgemはfat gemでもそうでなくても関係ありません。

新しい依存ライブラリーをすぐに使える。。。こともある

バインディングをシステムのライブラリーを使ってビルドするようにしていた場合、バインディング対象のライブラリーの更新はシステムのパッケージシステムが面倒をみてくれます。Debian GNU/Linux sid、Fedora Rawhide、ArchLinux、Homebrew、MSYS2などのように最新のバージョンに随時アップデートされるシステムではgemの更新を待たずに新しいライブラリーを使えます。

ただし、ライブラリーのバージョンアップでAPIが変わった場合はgemの更新が必要です。

requireが遅くならない

fat gem用のrequireがいらなくなるので失敗するrequireを実行しなくてもよくなります。これによりrequireが遅くなりません。

bigdecimalがfat gemのサポートをやめたのはこれが理由です。

開発コストが下がる

面倒なfat gemのリリースをしなくてよくなるので開発者は本来の開発にリソースを注力できます。

最適化ビルドできる

fat gemは事前にビルドしたバイナリーをすべてのユーザーが共通で使うことになるので最大公約数の最適化しかできません。

しかし、fat gemをやめて各ユーザーごとにインストールする場合はその環境毎に最適化できません。たとえば、速度が非常に重要な拡張ライブラリーをGCCでビルドする場合は-O3 -march=nativeというオプションをつけてビルドするとその環境向けに最適化されます。たとえば、CPUがSIMDをサポートしていればSIMDを使ったバイナリーを生成することもあります。

fat gemをやめたときの問題点と解決策

fat gemをやめるとユーザーも開発者もハッピーになれそうですね。でも、本当にそうでしょうか?fat gemをやめたときの問題点とその解決策をまとめます。

インストール時間が長くなる

fat gemの場合はビルド済みのバイナリーをコピーするだけなのですぐにインストールは完了します。しかし、fat gemをやめるとインストールするたびにビルドすることになるので時間がかかります。

解決策は。。。特にありません。。。

依存ライブラリーがなくてインストールが失敗しやすくなる

バインディングはバインディング対象のライブラリーがないとインストールに失敗します。たとえば、RMagickはImageMagickがないとインストールに失敗します。Nokogiriがデフォルトで自分で依存ライブラリーをビルドするようになっているのはこの失敗を防ぐためです。

たしかに、自分でビルドしてしまうというのはこの問題の解決策の1つではあります。ただ、そんなに筋がよいとは思えません。脆弱性があったときの対応に関する問題があるからです。

私がオススメする方法はシステムのパッケージシステムを使って自動で足りない依存ライブラリーをインストールする方法です。このための便利gemがnative-package-installerです。私が開発しています。

native-package-installerはpkg-config gemと一緒に使うことを想定していて、extconf.rbに次のように書いておけば、cairoがインストールされていなければ自動でインストールします。

require "pkg-config"
require "native-package-installer"

unless PKGConfig.have_package("cairo")
  unless NativePackageInstaller.install(:arch_linux => "cairo",
                                        :debian => "libcairo2-dev",
                                        :homebrew => "cairo",
                                        :macports => "cairo",
                                        :msys2 => "cairo",
                                        :redhat => "cairo-devel")
    exit(false)
  end
  unless PKGConfig.have_package("cairo")
    exit(false)
  end
end

なお、RubyInstaller for Windows用のRubyではgemのメタデータにMSYS2のパッケージを指定しておくことで同じ機能(自動で依存ライブラリーをインストールする機能)を実現できます。以下はcairo.gemspecでの例です。

gemspec.metadata["msys2_mingw_dependencies"] = "cairo"

参考:MSYS2 library dependencies - For gem developers - onclick/rubyinstaller2 Wiki

ビルドに失敗してインストールが失敗しやすくなる

fat gemではすでにビルド済みなのでビルドが失敗することはありません。開発者が用意した環境でビルドが成功すればOKです。

一方、ユーザーの環境でビルドする場合は、開発者の環境では成功しているのにユーザーの環境では失敗することがあります。

解決策は、CIでサポートしている環境を常にテストすることです。Travis CIやGitHub Actionsなどを使えば、いろんな環境でテストできます。Linuxの亜種はDockerを使うとよいでしょう。

まとめ

検討するべき項目が他にもある気がしますが、一通りまとめたので公開します。fat gemをやめたくなりましたか?それともfat gemはやめないで!という気持ちになりましたか?

もし、これはどうなの?という項目があったらなんらかの手段で私に聞いてください。回答します。

タグ: Ruby
2019-11-22

「Fluentd」のサポートサービスを開始しました。

クリアコードでは2015年からFluentdの開発コミュニティに参加し、Fluentd本体とプラグインの開発、サポート、各種ドキュメントの整備を行っています。
2016年以降、既存の取引先様に対してFluentdのサポートを提供していましが、この度満を持してサポートサービスの開始を宣言しました。
サービスの概要はこちらです。
先日プレスリリースも出しましたので、併せてご参照ください

Fluentdに関するお問い合わせはこちらまで。

タグ: Fluentd
2019-11-20

PostgreSQL Conference Japan 2019:Amazon RDS + Amazon EC2 + ロジカルレプリケーションを使った低コスト高速全文検索 #pgcon19j

2019年11月15日(金)にPostgreSQL Conference Japan 2019が開催されました。
私は、「Amazon RDS + Amazon EC2 + ロジカルレプリケーションを使った低コスト高速全文検索」という題名で、既存の技術を組み合わせて、なるべく楽に高速、高機能な全文検索ができる仕組みを紹介しました。

当日、使用した資料は以下に公開しています。

関連リンク:

内容

RDBMSのマネージドサービスにAmazon RDSがあります。Amazon RDSはマネージドサービスなので、RDBMSの管理や運用などのコストを大幅に下げるような機能(自動レプリケーション、自動フェイルオーバー、自動バックアップ、設定の最適化などなど)がたくさんあり、これらを使うことで、DBの管理、運用はとても楽になります。もちろん、PostgreSQLにも対応しておりPostgreSQLの管理、運用も大幅に楽になります。

Amazon RDSはとても便利なのですが、Amazon RDS上のPostgreSQLには決められた拡張しかインストールできません。
したがって、Amazon RDS上のPostgreSQLで全文検索をしたい場合、pg_trgmを使った全文検索をすることになります。(pg_trgmはAmazon RDS上で使用できます)
しかしながら、pg_trgmはアルファベットと数字にしか対応していません。日本語や中国語などの全文検索には使用できません。

PostgreSQLで使用できる全言語対応の全文検索の拡張には、PGroonga(ぴーじーるんが)があります。
PGroongaは、全言語対応の超高速全文検索機能をPostgreSQLで使えるようにする拡張で、安定して高速で、かつ高機能(同義語、表記ゆれや異体字への対応、類似文書検索など)ですが、前述の通り、Amazon RDS上では使用できません。

Amazon RDS上のPostgreSQLには直接PGroongaをインストールできないので、PostgreSQL10から追加されたロジカルレプリケーションと、Amazon EC2を使用してAmazon RDSのメリットである、管理、運用コストの低減を享受しつつ、高速、高機能な全文検索を実現する構成を紹介しました。

PostgreSQLでは、ストリーミングレプリケーションというレプリケーションがありますが、これは、複製元と複製先のDBはまったく同じものになります。
一方、ロジカルレプリケーションでは、複製元と複製先でテーブルの構造を一致させなくても良いという特徴があります。
この特徴を利用すると、複製先のテーブルにのみインデックスを設定するということができます。

つまり、複製元をAmazon RDSとし、複製先にはAmazon EC2を用意します。Amazon EC2にはPostgreSQLとPGroongaをインストールしておき、更新はAmazon RDS、検索はAmanzon EC2で行うという構成を取ることができます。

この構成では、Amazon RDSにさえデータがあれば、検索用のEC2はいくらでも作れるという特徴があります。
そのため、Amazon RDSのデータは大事に保護しなければなりませんが、DBの管理、運用に便利な機能があるAmazon RDSを使っているので、データの保護は今までよりも大幅に楽にできます。

一方で、Amazon EC2では、データをAmazon RDSからロジカルレプリケーションで同期するため、PGroongaを使ってAmazon RDSにあるデータを高速に全文検索できます。

このように、ロジカルレプリケーションの複製元と複製先のテーブル構造を一致させなくても良いという特徴を使って、Amazon RDSのメリットを享受しつつ、高速で高機能な全文検索を実現できるのです。

まとめ

Amazon RDSのメリットを活かしつつ、PGroongaを使った高速で高機能な全文検索を実現する構成を紹介しました。
Amazon RDSを使いたいけど、PGroongaが使えないので、しかたなく自前でサーバーを用意している。という方や、PGroongaを使って、高速で高機能な全文検索をしたいけど、Amazon RDSを使っているのでPGroongaが使えないとお悩みの方は、ここで紹介した構成を検討してみてはいかがでしょうか?

PGroongaを使った全文検索について、興味、疑問などございましたら、是非、お問い合わせください。

タグ: Groonga
2019-11-18

他のチームの知見を活かす

はじめに

弊社では複数のチームがあり、それぞれのチームで日々案件をこなしています。
今回、あるチームで課題を解決した方法を別のチームでも使える可能性があるのでは?ということで、チーム間の情報共有を行う取り組みを始めました。

チーム間の情報共有がなされにくいという課題

弊社は、それぞれのチームで案件が独立しているため、チーム間で情報を交換する必要性をあまり感じません。各案件についての背景知識がないと課題解決できないと考えがちなためです。そのため、現状では、チーム間の情報共有はとても少ないです。

しかしながら、案件によらず、どのチームにも共通する課題は存在します。あるチームで解決した課題と同じような課題で、別のチームが悩んでいるということがありえるのです。

他のチームから知見を得る機会を作ってみた

これは、非常にもったいないので他のチームの知見を取り入れられないかと考えました。

チームミーティングへの参加

チーム内にどんな課題があって、それをどうやって解決しているかは、チーム内で情報を共有する場で話題になることなので、まず、手始めに、各チームの情報共有の場に参加することで、他のチームでは、どうやって問題解決しているのかなど、自分のチームにはない知見を探すことにしました。

チームはそれぞれ、定期的にチーム内でミーティングを実施しています。そこでは、チームの現状や課題等が共有されるはずです。この場に参加することで、情報を収集しようと考えました。

今回は、Mozillaのサポートをしているチーム(以下、Mozillaチームと呼びます)のミーティングへ参加しました。

Mozillaチームのミーティングのやり方

Mozillaチームのミーティングでは、現在動いている案件がどんな状況か、また、新しく来た案件はどれで、誰がどう進めるかなどについて各々がどう理解しているかが共有されていました。
これは、個々人のタスク管理力を過信してチェックをしなかった場合に、漏れが発生した時のダメージが大きくなるので、早めにリカバリーできるようにすることを優先しているためです。

このようにすると、情報を同期する時間を取らないといけないので効率は若干落ちますが、そのコストよりもタスク漏れによる対応にかかるコストの方が大きいと判断して、情報の同期する時間を設けているとのことでした。

Groongaチームの現状

私は、全文検索エンジンGroongaの開発、サポートを行うチーム(以下、Groongaチームと呼びます)に属しています。
Groongaチームでも、現状の案件の状況を把握し、新しい案件は担当を決めて進めていますが、各々が確認した内容をチーム間で共有はしていませんでした。

Groongaチームは全員別々の場所で働いているので、普段は顔を合わすことがありません。
やり取りは、チャット上(Zulipという自由なソフトウェアを使っています!)かRedmineのチケット上でやり取りしていて、チャットとチケットを確認してタスク漏れがないかを各自で判断しています。

Groongaチームはこまめに情報共有するというスタンスで作業しているため、基本的にまとまった時間をとって情報共有をしません。Groongaチームの作業は、GitHub、GitLab、Gitter、メール、Redmineのチケット等、様々な入り口から問合せがあり状況が刻々と変わっていくので、まとまった時間を取るより、各作業についてこまめに情報共有するほうが効率が良いためです。

ただ、この方法では、タスクを網羅しているかどうかを確認できません。
そのため、自分が気がついていない案件を見落とすということが起きてしまいますし、作業に集中しすぎて、こまめな情報共有をおろそかにすると、自分の理解が誤っていて、間違った作業や非効率的な作業をしてしまうことがあります。

他のチームの知見を活かすポイントの発見

このように効率が良いと思って、こまめに情報共有するスタイルにしていましたが、それだけでは不十分なのではないかと感じました。案件を見落としてしまうと、そのリカバリーのために多くの時間が必要になるので、結局効率を落としてしまいます。

Mozillaチームでは、各々自分が理解したことをまとまった時間を取って共有することで、その時点で、抜け、漏れ、誤りを指摘、修正できているので、GroongaチームもMozillaチームに倣って、まとまって情報を共有する時間が必要だと考えました。

別のチームの知見を活かすポイントを見つけられたのです。

まとめ

他のチームの知見を得るために、別のチームのミーティングに参加する取り組みを実行し、その内容を紹介しました。
実行した結果、自分のチームには無かった知見を得ることができました。

チーム間の情報共有があまり行われずに悩んでいる方は、他のチームのミーティングに混ざってみると、今回のようにミーティングのやり方等から意外な知見がえられるかもしれません。

2019-11-12

redmine.tokyo第17回勉強会:Redmine検索の未来像 #redmineT

Redmineの全文検索プラグインを開発している須藤です。

2019年11月2日にredmine.tokyo第17回勉強会が開催されました。私は参加していないのですが、この半年一緒に全文検索プラグインを開発してきた島津製作所の赤羽根さんが全文検索プラグインの導入結果を報告しました。私はこれに合わせて技術面からの補足として次の資料を作成しました。

関連リンク:

内容

従来の検索システムのなにが課題でそれをどう解決したかについてまとめました。しかし、従来の課題を解決すればすごく明るい未来があるわけではありません。少し明るい未来があるだけです。すごく明るい未来にするためにはさらになにに取り組めばよいのかについてもまとめています。そして、その取り組みはすでに始まっています。

今はクリアコードと島津製作所さんで一緒に取り組んでいますが、できれば他の会社も巻き込んですごく明るい未来を目指したいと思っています。私たちの成果はRedmine本体同様ユーザーが自由に使えるソフトウェアとして公開しています。もし、他の会社も一緒に取り組んだ場合もその成果はユーザーが自由に使えるソフトウェアとして公開します。一緒に取り組まなくても使えるように公開しますが、一緒に取り組むことですごく明るい未来がより早く実現できます。

一緒にすごく明るい未来を目指したい方はお問い合わせください。

まとめ

クリアコードと島津製作所さんは一緒にRedmineの全文検索プラグインを開発していました。その成果を島津製作所の赤羽根さんがredmine.tokyo第17回勉強会で紹介しました。私も紹介する資料を用意しました。

Redmineの検索機能をよりよくして、Redmineに蓄積した知識をより活用するために一緒に取り組みたい企業を募集しています。興味が湧いてきた方はお問い合わせください。

タグ: Groonga
2019-11-05

Apache Arrow Flightの日本語情報を公開

Apache Arrowの最新情報をまとめた須藤です。

先日、Apache Arrowの公式ブログでIntroducing Apache Arrow Flight: A Framework for Fast Data TransportというApache Arrow Flightを紹介する英語の記事が公開されました。この記事はApache Arrow Flightのことを理解する上で非常に役に立つ情報だと思ったので、翻訳し、Apache Arrowの公式ブログでApache Arrow Flightの紹介:高速データトランスポートフレームワークという記事として公開しました。

翻訳にあたり、Speeeの@mrknレビューアーとして協力してくれました。ありがとうございます。

さて、そんなApache Arrowですが、そろそろ1.0.0がリリースされます。それにあわせてApache Arrow東京ミートアップ2019というイベントを2019年12月11日(水)の夜に開催します。去年もApache Arrowイベントを開催しましたが、今年も開催します。去年と同様、今年もSpeeeさんに会場・飲食物を提供してもらいます。ありがとうございます!

去年はApache Arrow関連プロダクトの開発に参加する人を増やすことを目的に設計しましたが、今年はApache Arrowユーザーを増やすことを目的に設計しています。そのため、次のような構成にしています。

  • Apache Arrowそのものの情報を紹介
  • Apache Arrowの利用事例を紹介

この構成を実現するためにApache Arrowの利用事例を紹介してくれる人をすごく探しています。特に次のように使っている人をすごく探しています。

他の使い方でもいいんですが、Apache Arrowをすでに使っている人は@ktouに連絡してください!ぜひApache Arrow東京ミートアップ2019で事例紹介をしてApache Arrowユーザーを増やすことに協力して欲しいのです!

2019-10-28

EPELでパッケージをリリースできるようにするには

はじめに

SentencePiece・fastTextをEPELのパッケージを使って簡単にインストールできるようになりますでEPELでパッケージをインストールできるようにしたことを紹介しました。
SentencePiece・fastTextを簡単に使えるようにすると嬉しい人向けの記事だったのですが、それを実現するまでにどんなことをやったのかは詳しく説明しませんでした。
今回はEPELでパッケージをリリースできるようにしたいという人向けの記事です。

実際にEPELでパッケージをインストールできるようにしたこと

以前Fedoraプロジェクトで新規パッケージをリリースする方法Fedoraプロジェクトでパッケージを更新するにはとしてFedoraプロジェクトでパッケージをリリースしたり、パッケージを更新したりするやりかたについて説明しました。

Fedoraだけでなく、EPELでもパッケージをリリースできるようにするには概ね次のような手順を踏みます。(SentencePieceもfastTextもFedoraにパッケージがないので新規パッケージをリリースするところからになります。)
Fedoraプロジェクトで新規パッケージをリリースする方法はやや古くなっていますが、基本的な流れはそれほど変わっていません。
そのため、一度Fedoraでパッケージをリリースしたことがあり、packagerグループの権限をもっているという前提で変わったところを中心に説明します。

  • パッケージを準備する
  • パッケージをレビュー依頼する
  • レビューが承認されたらSCMリクエストを送る
  • 必要なブランチをリクエストする
  • 各ブランチでリリースする
パッケージを準備する

レビュー依頼をするときには、specファイルとSRPMを提出する必要があります。
fedora-reviewを使うとある程度事前にパッケージの体裁をチェックすることも可能です。

$ fedora-review --rpm-spec --name (SRPMのファイル)

あらかじめチェックをしておくとよいでしょう。

パッケージをレビュー依頼する

手元でパッケージの用意ができたら、レビュワーが参照できるようにspecやSRPMを公開の場所にアップロードします。
アップロードができたら、パッケージのレビューを依頼します。

レビュー依頼には、request for review in bugzillaを使って、雛形から作成するとよいでしょう。

すでにpackagerグループに入っている場合、FE-NEEDSPONSORフラグの設定は不要です。
SentencePieceの場合以下のようなレビューリクエストを作成しました。

レビューが承認されたらSCMリクエストを送る

SCMリクエストはリポジトリを作成してもらうために行います。fedpkgコマンドを使ってパッケージ名と、承認されたBugzillaの番号を指定します。
SentencePieceの場合、次のようにコマンドを実行しました。

$ fedpkg request-repo sentencepiece 1758036

上記コマンドを実行すると、以下のようなissueが自動的に作成されます。

このissueはリポジトリが作成されると閉じられます。

必要なブランチをリクエストする

SCMリクエストが承認されたら、次は作業に必要なブランチをリクエストします。fedpkgコマンドのrequest-branchに必要なブランチを指定します。
SentencePieceの場合、f31とepel8、epel7のリクエストを行いました。

$ fedpkg clone sentencepieces
$ cd sentencepiece
$ fedpkg request-branch f31
https://pagure.io/releng/fedora-scm-requests/issue/18148
$ fedpkg request-branch epel8
https://pagure.io/releng/fedora-scm-requests/issue/18149
https://pagure.io/releng/fedora-scm-requests/issue/18150
$ fedpkg request-branch epel7
https://pagure.io/releng/fedora-scm-requests/issue/18151

リクエストを行うと、上記のようにブランチごとにissueが作られます。ブランチが作成されるとissueが閉じられる運用になっています。

各ブランチでリリースする

ブランチが作成されたら、次は各ブランチでリリースを行います。
specファイルやソースアーカイブをインポートしたら、fedpkg buildでパッケージをビルドします。その後、fedpkg updateを行うことで対象ブランチでリリースを行えます。
SentencePieceの場合、epel7ブランチのリリースは次のようになりました。

既定では2週間ほどでリポジトリに反映されるようです。

まとめ

今回はEPELでパッケージをリリースできるようにしたいという人向けに手順を説明しました。
手元で必要に迫られてパッケージ化したものがあれば、ぜひアップストリーム(Fedoraプロジェクト)へフィードバックしていきませんか?

2019-10-18

SentencePiece・fastTextをEPELのパッケージを使って簡単にインストールできるようになります

はじめに

SentencePieceは、言語に依存することなく学習データから文章をトークナイズすることができるものです。
MeCabでは言語に依存した辞書が必要でしたが、SentencePieceは言語非依存なので、辞書が不要になります。
SentencePieceについて詳しくは開発者による記事があるのでそちらを参照されると理解がより深まるでしょう。

一方のfastTextは文書から学習してテキストを分類し、類似する単語を単語の類似度を計算することができます。
類似度をうまくつかえば、似たような文章をサジェストすることができそうです。

どちらもデータを学習させて活用するためのものですが、SentencePieceやfastTextのことを知って、ちょっと試そうとするにもやや手間が必要でした。これまではソースコードを取得してコンパイルしたりする手順が必要だったからです。
今後はSentencePieceやfastTextをEPELのパッケージとして簡単にインストールできるようになります。ただし、まだepel-testingリポジトリからで、epelリポジトリからインストールできるようになるのはしばらく(2週間ほど)先になる見込みです。

epelリポジトリにパッケージが入ると以下のように簡単にSentencePieceやfastTextをパッケージでインストールできるようになります。

$ sudo yum install epel-release
$ sudo yum update
$ sudo yum install fasttext-tools
$ sudo yum install sentencepiece-tools

EPELでパッケージがインストールできるとどう嬉しいか

冒頭で述べたように、SentencePieceやfastTextをこれから触ってみようという人は、簡単にインストールして試せるようになるので嬉しいです。
他にもGroongaという、クリアコードが開発に関わっている全文検索エンジンGroongaのユーザーにもメリットが(今後)あります。

というのも、これらをうまく使えばGroongaでいい感じの類似文書検索(例えばRedmineで似たようなチケットをサジェストするようなこと)ができるようになるかもしれないからです。

Groongaにはトークナイザーをプラグインとして組み込む仕組みがあって、使用するトークナイザーを使い分けることで、検索時の振る舞いを調整する(いい感じに検索する)ことができます。
利用可能なトークナイザーにはTokenBigramTokenMecabなどがあります。TokenBigramだと漏れなく検索できますが、TokenMecabのように意味のある単位での検索は不得手といったような特色があります。逆にTokenMecabは意味のある単位でトークナイズできるのでよいかというと、辞書に依存する部分が大きく、未知の語句の検索に弱いため、必ずしも適切とは限りません。
また、TokenMecabは既存の辞書を利用するので、言語に依存した辞書が必要です。

より精度の高い検索を実現するには、SentencePieceやfastTextなどを使って、学習結果をもとにトークナイズできるトークナイザーを使えるようにするのがよさそうです。
学習結果をもとにトークナイズするので、MeCabほどそれらしい単語にトークナイズできるわけではなさそうです。ただし、類似文書検索の場合には、特徴的な言い回しがあるかどうかで判断することになるので、そのようなトークナイザーがあると有用です。

(まだそのようなトークナイザーはできていませんが)仮に新しいトークナイザーが使えるようになったとして、インストールに手間がかかるのでは、Groongaのユーザーにとってあまり嬉しくありません。
TokenMecabのようにパッケージでインストールできるようになっていると、インストールが容易で更新の手間も少なくできます。
パッケージ化する場合でも、Groongaプロジェクトで提供している独自リポジトリからインストールできるというのでは、Groongaをまだ使っていない人にはあまり嬉しくありません。(リポジトリを追加で登録しないといけないからです。)
また、独自のリポジトリでメンテナンスし続けないといけないというのも手間がかかります。それよりかはアップストリームでメンテンナンスするほうがより誰でも簡単にインストールできるようになって嬉しいので、EPELでパッケージを公開することにしました。*1

まとめ

今回は、SentencePiece・fastTextをEPELのパッケージを使って簡単にインストールできるようになることを紹介しました。
SentencePieceやfastTextを使ってみたい人だけでなく、(今後)Groongaでいい感じの類似文書検索をできるようにするのにも必要になってくるのでEPELのパッケージとしてインストールできるようにしました。独自リポジトリにパッケージを用意して終わりにするのではなく、クリアコードではアップストリーム(Fedoraプロジェクト)へフィードバックしていくことでフリーソフトウェアの推進を実施しています。

*1 EPELでパッケージを公開するためにはいろんな手順が必要でした。

2019-10-17

Apache Arrowの最新情報(2019年9月版)

Apache ArrowPMC(Project Management Commitee、プロジェクト管理チームみたいな感じ)のメンバーの須藤です。

みなさんはApache Arrowを知っていますか?Apache Arrowは数年後にはデータ処理界隈で重要なコンポーネントになっているだろうプロジェクトです。データ処理界隈に興味がある人は知っておくと役に立つはずなので1年ほど前にApache Arrowの最新情報(2018年9月版)をまとめました。この1年ですごくよくなったので2019年9月現在の最新情報を紹介します。

私は、PMCの中では唯一の日本人*1で、コミット数は2番目に多い*2ので、日本ではApache Arrowのことをだいぶ知っている方なはずです。Apache Arrowの情報は日本語ではあまりないので日本語で紹介します。

ちなみに、英語ではいろいろ情報があります。有用な情報源はApache Arrowの公式ブログ公式メーリングリストやそれぞれの開発者のブログ・発表などです。開発者のブログの中ではUrsa Labs Blogの隔月の開発レポートがオススメです。Ursa Labsはスポンサーを集めてフルタイムでApache Arrowの開発をしている非営利の組織です。(という説明でそんなに間違っていないはず。)

この記事ではそれらの情報へのリンクも示しながら最新情報を紹介するので、ぜひ英語の情報も活用してください。

Apache Arrowが実現すること

Apache Arrowが実現することは1年前と変わっていないので、Apache Arrowの必要性から知りたいという方はApache Arrowの最新情報(2018年9月版)を参照してください。まとめると次の通りです。

Apache Arrowは効率的に大量のデータをメモリー上で処理することを目指しています。そのためにしていることは次の通りです。

  1. データ交換・高速処理しやすいApache Arrowフォーマットの仕様を定義
  2. 各種言語用のApache Arrowフォーマットを読み書きするライブラリーを開発
  3. 大量のメモリー上のデータを高速処理するためライブラリーを開発

Apache Arrowが向いている用途は次の通りです。

  • 大量データの交換
  • メモリー上での大量データの分析処理

Apache Arrowの現状

それでは、2019年9月現在、Apache Arrowのデータフォーマットの仕様と実装がどのようになっているかを説明します。

データフォーマットの仕様

データフォーマットの仕様は去年中に固められるといいねと進んでいましたが、まだ固まっていません。しかし、今年中には固まるはずです。

現時点での最新リリースは0.14.1です。10月中に0.15.0がリリースされる予定です。順調にいけば、0.15.0の次が1.0.0になります。

1.0.0をリリースする段階でデータフォーマットの仕様が固まります。固まるというのはどういうことかというと互換性が保証されるということです。

互換性には後方互換性と前方互換性があります。

後方互換性が保証されるというのは新しい実装では新しい仕様のデータだけでなく古い仕様のデータも読めるということです。

前方互換性が保証されるというのは古い実装でも新しい仕様のデータを読めるということです。もし読めなくても読めないことがわかります。中途半端に読めるとか壊れたデータとして読めるということにはなりません。

データフォーマットの互換性は大事なので、今後、仕様を変更するときは、少なくともC++とJavaで実装し、それぞれで相互に正しくやりとりできるかを確認するテストを追加しなければいけなくなりました。そのため、C++でだけ実装しやすいがJavaでは実装しづらいような仕様は入りにくくなります。少なくともC++とJavaとなっているのは今のところC++とJavaの実装が進んでいるからです。今後、他の言語での実装が進んできたらC++とJava以外も候補に入ってくるかもしれません。

参考:[DISCUSS] Format changes: process and requirements

これまで、Apache Arrowフォーマットのデータはストレージに保存するのには向いていないという扱いでした。しかし、仕様が固まったら(1.0.0がリリースされたら)データが失われる心配をせずにストレージに保存することができます。

ただし、Apache Arrowフォーマットよりもストレージ保存に向いたフォーマットはたくさんある(たとえばApache Parquet)ので、ストレージに保存したい場合は、本当にApache Arrowフォーマットが適切なのかを考えてからどのフォーマットにするかを決めてください。たとえば、ストレージにはすごく空きがあるからデータの読み込みをすごく速くしたいということであればApache Arrowフォーマットを使ってもよいでしょうが、書き込むデータ量はApache Parquetなど他のストレージ向けのフォーマットよりも大きくなりがちなので書き込み速度はそこそこ(I/Oネック)になるかもしれません。

データフォーマットのバージョン

1.0.0がリリースされたらデータフォーマットのバージョン付けにはセマンティックバージョニングを採用する予定です。つまり、互換性が壊れるような変更がなければメジャーバージョンは同じまま(1.X.Yのまま)になります。互換性がある仕様変更の場合はマイナーバージョンが上がります。1.0.0のあとに互換性のある仕様変更があったら1.1.0になります。今のところ、パッチバージョンは使う予定はありません。

今までは明示的にデータフォーマットにバージョンを振っていませんでした。振っていませんでしたが、メタデータバージョンというものがありました。これは今のところV1, V2, V3, V4があります。データフォーマットのメジャーバージョンが上がったらここに新しい値が増えます。2.0.0になったらV5が増えます。

少し紛らわしいかもしれませんが、1.0.0以降は「ライブラリーのバージョン」と「データフォーマットのバージョン」は別々に採番されます。ライブラリーのバージョンは2.0.0でデータフォーマットのバージョンは1.0.0というような状況になります。

参考:[VOTE] Adopt FORMAT and LIBRARY SemVer-based version schemes for Arrow 1.0.0

ライブラリーのバージョン

1.0.0からはライブラリーのバージョンもセマンティックバージョニングに従います。つまり、互換性が壊れるような変更があればメジャーバージョンが上がるということです。

現時点ではリリースするたびに互換性が壊れる可能性があるので毎回メジャーバージョンが上がる予定です。2ヶ月くらいごとにリリースする予定なので2ヶ月くらいに1回メジャーバージョンが上がる予定です。

ライブラリーのメジャーバージョンは頻繁に上がりますが、データフォーマットのメジャーバージョンはそんなに上がらないはずです。そのため、次のようにライブラリーのバージョンとデータフォーマットのバージョンはズレます。

  • 1.0.0の次のリリース
    • ライブラリーのバージョン:2.0.0
    • データフォーマットのバージョン:1.0.0
  • 1.0.0の次の次のリリース
    • ライブラリーのバージョン:3.0.0
    • データフォーマットのバージョン:1.0.0

Apache Arrowにはたくさんのプログラミング言語向けのライブラリーがありますが、すべてこのルールで一緒にリリースされます。

扱えるデータ

バージョンの話はこれくらいにして、Apache Arrowが扱えるデータについて説明します。基本的なデータについてはApache Arrowの最新情報(2018年9月版)を参照してください。ここではこの1年で新しく扱えるようになったデータについて説明します。

新しく次のデータを扱えるようになりました。

また、ユーザーが独自に型を追加できるようになりました。これを拡張型と呼びます。

拡張型は次のように実現しています。

  • 生データはApache Arrowが提供するプリミティブ型を使って表現する
  • 生データにメタデータを付与して追加情報を表現する

たとえば、UUID型は次のように実現します。

  • 生データは16バイトの固定長バイナリーデータ型を使って表現する
  • 生データに次のメタデータを付与
    • 拡張型名:uuid
    • UUIDのバージョン:1

この実現方法のメリットは、使っているApache Arrowのライブラリーが対象の拡張型のことを知らなくても(UUID型について知らなくても)データを扱えるという点です。対象の拡張型を知らなかったら単に生データ(UUID型ならただの16バイトの固定長バイナリーデータ)として扱えばよいからです。もちろん、その拡張型固有の操作はできませんが、要素数を数えたり、複数のデータチャンクをまとめて次のデータ処理モジュールにデータを渡すといった基本的な操作はできます。

データ処理

Apache Arrowは各種データ処理ツールが共通で使える高速なデータ処理機能の開発も重視しています。去年の時点ではデータフォーマットの方に注力していたためあまり進んでいませんでした。しかし、この1年でデータ処理部分の実装が進んでいます。

特に進んだのがC++とRustです。C++のバインディングとして実装されているPython、R、Rubyもその恩恵を受けています。

それではそれぞれの言語毎にデータ処理部分がこの1年でどう改良されたかを紹介します。去年の時点で実装されている処理についてはApache Arrowの最新情報(2018年9月版)を参照してください。

C++

C++でこの1年で実装された処理は次の通りです。

  • 指定した値と各要素の比較
    • 結果:真偽値の配列
    • 比較方法:==, !=, <, <=, >, >=
    • 例:Compare([null, 1, 2, 3], 2, >=)[null, false, true, true]
  • 非NULLの要素数のカウント
    • 結果:要素数(数値)
    • 例:Count([null, 1, 2, 3])3
  • 真偽値配列による要素の選択
    • 結果:対応する真偽値配列の要素が偽ではない要素の部分だけが残った配列
    • 例:Filter([null, 1, 2, 3], [true, false, null, true])[null, null, 3]
  • 値ごとの要素数のカウント
    • 結果:構造体({"Values": 入力の配列と同じ型, "Counts": 64ビット整数})の配列
    • 例:ValueCounts([null, "a", "b", "a"])[{"Value": "a", "Counts": 2}, {"Value": "b", "Counts": 1}]
  • 対象の配列の中に指定した配列にある要素が含まれているか
    • 結果:真偽値の配列
    • 例:IsIn([null, 1, 2, 3], [1, 3])[null, true, false, true]
  • 算術平均(NULLを無視)
    • 結果:平均値(64bit浮動小数点数)
    • 例:Mean([null, 1, 2, 3])2.0
  • ソートし、ソート後の各要素がソート前は何番目の要素だったかを返す(NULLは最後になる)
    • 結果:64bit整数の配列
    • 例:SortToIndices([null, 3, 1, 2])[2, 3, 1, 0]
  • 合計(NULLを無視)
    • 結果:合計値(64bit整数または64bit非負整数または64bit浮動小数点数)
    • 例:Sum([null, 1, -2, 3])2
  • 配列から指定したインデックスの要素を抽出
    • 結果:対応する要素だけが残った配列
    • 例:Take([null, "a", "b", "c"], [2, null, 1])["b", null, "a"]

さらにGandivaという名前の式コンパイラーが取り込まれました。Apache Arrowの最新情報(2018年9月版)の段階で取り込まれようとしていたものが正式に取り込まれたということです。

前述の処理は配列単位の処理ですが、Gandivaはレコードバッチ(レコードの集合)に対する処理を扱います。たとえば、「record.column1 > 100 && record.column2 == "XXX"」のような処理を扱います。このような処理をLLVMを使って実行時にネイティブコードにコンパイルしてから実行するので高速に式を評価できます。

Gandivaは単に条件で絞り込むような簡単な式だけでなく、SQLで記述できるような集約処理や条件分岐(CASE相当)などの複雑な式も扱います。

参考:Gandiva: A LLVM-based Analytical Expression Compiler for Apache Arrow | Apache Arrow

現時点でGandivaはC++だけでなくJava、Python、Rubyから使えます。

現時点では前述の配列単位の処理(内部では計算カーネルと呼んでいます)とGandivaは特に連携していません。算術演算のような基本的な処理はどちらにもあります。では、どうやって使い分けるのかが気になりますね。おそらく、次のような使い分けになるでしょう。

  • 中間結果(最終結果じゃない)のレコードバッチの処理:Gandiva
    • 例:レコードのフィルター
  • 最終結果を生成する処理:計算カーネル
    • 例:合計値の計算

参考:Compute kernels and Gandiva operators

もう少し先になると、ユーザーはもう少し高レベルのAPIを使うことができるようになるはずです。どこかからデータを読み込み、読み込みながら指定したクエリーでデータを絞り込めるようなAPIです。

データ読み込み処理はデータセットAPIとして実装が始まっています。

今のところローカルにあるApache Parquetフォーマットのデータだけ読み込めますが、今後、CSVやJSONフォーマットのデータも読み込めるようになったり、S3上のファイルなどリモートにあるデータも読み込めるようになる予定です。条件のプッシュダウン(指定した条件のデータだけ読み込む)も対応します。プッシュダウンなしで、読み込んだ後に不要なデータを捨てる方法もありますが、それよりも、そもそも読まない方が圧倒的に高速なのです。

参考:

読み込んだデータはクエリーエンジンで処理できるようになる予定です。クエリーエンジンは内部で計算カーネルやGandivaを利用して効率的に処理します。

参考:

ユーザーがより扱いやすいAPIとしてデータフレームAPIも実装する予定です。

参考:

楽しみですね!

Rust

Rustでこの1年で実装された処理は次の通りです。

  • 要素ごとの算術演算(SIMD対応)
  • 要素ごとの論理演算(AND・OR・NOT)
  • 要素ごとの比較(SIMD対応)
  • キャスト(型の変換)
  • 配列から指定したインデックスの要素を抽出
  • 時刻配列の各要素を時間(hour)に変換

Rustでもクエリーエンジンの実装が始まりました。DataFusionという名前です。

C++のクエリーエンジンはSQLなど特定のフロントエンドを用意しない*3設計*4ですが、DataFusionにはSQLのフロントエンドがあります。

まだ簡単なSQLしか処理しかできませんが、より複雑なSQLも処理できるように開発を進めています。

参考:DataFusion: A Rust-native Query Engine for Apache Arrow | Apache Arrow

この1年でRustのApache Parquet実装がApache Arrowに取り込まれたので、Apache Parquetフォーマットのデータもデータソースとして扱えます。

現時点ではすべてRustで実装していますが、どうにかしてGandivaとつなげられないかという検討もしています。

参考:[DISCUSS] [Gandiva] Adding query plan to Gandiva protobuf definition

DataFusionの開発をしている@andygrove73はDataFusionベースの分散計算プラットフォームBallistaの開発をはじめました。PoCのプロジェクトだそうなので、ここでの実験の成果がDataFusionに還元されていくのだと思います。

データ処理のまとめ

データ処理まわりについては、この1年で特に進んだC++実装とRust実装を紹介しました。

Arrow Flight - 高速RPCシステム

この1年Arrow FlightというApache Arrowベースの高速RPCシステムの開発が始まりました。gRPCベースのプロトコルですが、できるだけ高速になるように設計されています。たとえば、データのコピーができるだけ少なくなるようになっています。

ここでもう少しArrow Flightについて紹介できればよかったのですが、私がまだ触っていないのでそんなに説明できることがありません。残念。

Java実装を主に実装したDremioの人たちが書いたArrow Flightの記事Understanding Apache Arrow Flightもあるのですが、ちょっとこれだけだとピンとこないと思います。

現時点でArrow Flightを使えるのはC++とJavaとPythonです。

来年にはRubyでも使えるようにして紹介できるようにしたいな。

追記:Apache Arrow Flightの日本語情報を公開

各言語での実装の完成度

各言語での実装の完成度もApache Arrowの最新情報(2018年9月版)からの差分を紹介します。

まず、次の言語が増えました。

  • C#
  • R
  • MATLAB

C#はネイティブ実装で、RとMATLABはC++実装のバインディングです。

C#実装は基本的な型にだいたい対応しています。前述の新しく扱えるようになったデータはまだ対応していません。NuGetでインストールできます。

参考:NuGet Gallery | Apache.Arrow

R実装はかなりC++実装をカバーしています。Python実装やRuby実装ほどではありませんが、完成度が高めです。Apache Parquetの読み込みもサポートしました。RからApache Parquetを読み込めるとうれしい人が多いはずです。CRANからインストールできます。

参考:CRAN - Package arrow

MATLAB実装はFeatherフォーマットを読み書きできるだけです。今後、開発が活発になるかどうかはよくわかりません。

Apache Arrow利用者

Apache Arrowの利用者が増えています。

TensorFlowはApahche Arrowフォーマットのデータをデータセットとして使えるようにしました。

参考:TensorFlow with Apache Arrow Datasets

BigQuery Storage APIもApache Arrowフォーマットのデータをサポートしました。

参考:

PG-StromというPostgreSQLの拡張モジュールもApache Arrowフォーマットのデータをサポートしました。PG-StromはGPUを使って高速にデータ処理するための拡張モジュールです。PG-Stromの関連モジュールとしてArrow_Fdwがあり、これを使うとストレージに置いてあるApache ArrowフォーマットのデータをPostgreSQLから読み込めます。読み込んだデータはPG-Stromで高速に処理できます。

参考:PostgreSQLをどこまで高速化できるのか?〜ハードウェアの限界に挑むPG-Stromの挑戦〜

PG-StromにはPostgreSQLに接続して結果をApache Arrowフォーマットのデータとして保存するpg2arrowというコマンドもあります。この機能はC++のデータセットAPIで実現しようとしている機能の1つでもあります。この機能をApache Arrow本体に取り込まないか?という話もあるので楽しみですね。

参考:Contributing to Apache Arrow? · Issue #9 · heterodb/pg2arrow

1.0.0がリリースされたらもっとApache Arrowの利用者が増えるでしょう。

Apache Arrowの今後

ここまででApache Arrowのこの1年の最新情報を説明しました。最後に今後のことを説明します。

1.0.0リリース

前日の通り、近いうちに1.0.0がリリースされます。

去年、Apache Arrow東京ミートアップ2018というApache Arrowイベントを開催しましたが、1.0.0に合わせて今年もApache Arrowイベントを開催する予定です。楽しみにしていてください!

Apache Arrow利用者の増加

1.0.0がリリースされるとこれまで「様子見」だった人たちが使うようになるはずです。そうするといろんなところでApache Arrowを利用した高速なデータ交換ができるようになります。

次の1年でApache Arrow対応プロダクト・サービスはたくさん増えるでしょう。

データ処理機能の拡充

前述の通りC++実装ではデータセット・クエリーエンジン・データフレームなどデータ処理機能の実装に注力していきます。次の1年で使える機能がかなり増えるでしょう。

まとめ

2019年9月時点のApache Arrowの最新情報を、2018年9月からの差分という形でまとめました。Apache Arrowは数年後にはデータ処理界隈で重要なコンポーネントになっているだろうプロジェクトです。日本でもApache Arrowのことを知っている人が増えるといいと思うので日本語でまとめました。Apache Arrowを使う人が増えるといいなぁと思います。さらに言えば開発に参加する人も増えるといいなぁと思います。

私が知っていることはまとめたつもりですが、もしかしたらカバーできていない話があるかもしれません。もし、「○○についても知りたい!」という方がいたらApache Arrowのことを日本語で話せるチャットで声をかけてください。この記事に追加します。

Apache Arrowについて講演して欲しいという方はお問い合わせフォームからご連絡ください。

私はデータ処理ツールの開発という仕事をしたいと思っています。その中にはもちろんApache Arrowの開発も含まれています。一緒に仕事をしたい!(自社サービスをApache Arrow対応したいとか)という方はお問い合わせフォームからご連絡ください。

*1 去年はPMCメンバー・コミッター含めて日本人は私だけでしたが、今は私の他に2人日本人がいます。

*2 去年は3番目でした。

*3 そのようなフロントエンドのバックエンドになる

*4 前述の設計文書の「Non-goals」を参照。少なくとも今のところはそうしない設計。

2019-09-30

タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|