Rubyを使ったn分散全文検索ミドルウェア

: author

須藤功平

: institution

株式会社クリアコード

: content-source

RubyWorld Conference 2014

: date

2014/11/13

: allotted-time

25m

: theme

.

趣意書

Rubyをn 普通の人々にn 浸透させたい!

この発表の内容

浸透促進案のn 提案

浸透対象の普通の人々

* よくプログラムを書く人
  * ただしRubyとは縁遠い
  * →よくRubyを書くようになる
* たまにプログラムを書く人
  * インフラの人とか
  * →たまにRubyを書くようになる

浸透方法

* 技術的な攻め方
  * 多機能・高機能・高性能
  * 他よりいいですよ
* 心理的・政治的な攻め方
  * 流行り
  * 「みんな」使っていますよ

提案方針

* 技術的な攻め方
  * 多機能・高機能・高性能
  * 他よりいいですよ
* 心理的・政治的な攻め方
  * ((*流行り*))
  * 「みんな」使っていますよ

流行っている状態

((‘tag:center’))シェアを独占している

* キラーアプリがある
  * 例:Ruby on Rails
* 有用アプリの多くがRuby製
  * 例:Chef, Puppet, Vagrant, Serverspec

キラーアプリは難しい

((‘tag:center’))シェアを独占している

* (('del:キラーアプリがある'))
  * 例:Ruby on Rails
* 有用アプリの多くがRuby製
  * 例:Chef, Puppet, Vagrant, Serverspec

多有用アプリ状況を目指す

* Ruby製アプリ使う
  * Ruby製アプリを優先して選ぶ
  * 使っていることを広くアピール
* Ruby製アプリを作る
  * そこそこ有用で十分
  * たくさん作る

Ruby製アプリ作りを促す

* 成功事例(きっかけ)
  * 後続が真似したくなればよい
  * そんなに流行らなくてもよい
* 開発ノウハウ(助け)
  * 後続が開発しやすくなる
  * ライブラリー化されていると尚よし

どんなアプリがよいか

ミドルウェアn ↓n 自作のアプリはn 好きな言語で書けるn ((‘note:(言語による採用障壁が低め)’))n ((‘note:(例:RabbitMQはErlang製だがアプリはErlang以外が多い)’))

ミドルウェア

* データストア(RDBMSやKVS)
* 検索システム
* メールシステム
* メッセージキュー
* ログ活用(分析や監視)

Rubyでミドルウェア

* 成功事例
  * Fluentd, ROMA
  * milter manager
  * 分散全文検索エンジンを開発中\n
    (('note:(Droonga)'))
* 開発ノウハウ
  * →これから紹介

開発ノウハウ:方針

* トレードオフと向き合う
  * どこを強みにするか
  * どこは競合と戦わないか
* 全方位で勝つことはできない
  * Rubyだって速さじゃCに勝てない
  * でも楽しさならCに勝てる

強みの選び方

* 使いやすさで勝負する
* 最高速で勝負しない
* 多機能で勝負しない

ミドルウェアの使いやすさ

* 導入・設定・運用の簡単さ
  * 多くのミドルウェアは大変←ヒント
  * 例:設定なしで動くと簡単
* 止めないことが前提
  * 無停止で設定再読み込み
  * 無停止でアップグレード

強みの選び方 - 使いやすさ

* 使いやすさで勝負する
  * ユーザーの手間を減らす
  * かゆいところに手が届く
* 最高速で勝負しない
* 多機能で勝負しない

性能

* 最高速は目指さない
  * C/C++とかJavaに負ける
* 十分な速度は目指す
  * ミドルウェアが\n
    ボトルネックにならない程度

ミドルウェアと性能

* ミドルウェア=サーバー
  * 並行処理をいかにがんばるか
* 評価基準
  * レイテンシー(1リクエストに注目)
  * スループット(単位時間に注目)

ボトルネックの解消方法

* なりやすい箇所
  * CPU
  * ネットワーク
* なるかもしれない箇所
  * I/Oとメモリー

CPUネック

* ((*処理を減らす*))
* マルチプロセス
  * 後述
* Cで拡張ライブラリーを書く
  * Fluentd: MessagePack
  * Droonga: MessagePack, Groonga

マルチプロセス実装

* ServerEngine(ライブラリー)
  * nスレッド+シグナル+fork(('note:(spawn)'))
* Droonga
  * 1スレッド+パイプ+spawn(('note: +イベントループ'))
  * ↑の方がオススメ

ServerEngine

# image
# src = images/serverengine-model.svg
# relative_height = 100

スライドプロパティー

: enable-title-on-image

false

ServerEngineモデル1

* 基本はSupervisor→Worker
* gracefulな再起動をしにくい\n
  (('note:(無停止で設定再読み込みできれば必要ない)'))
  * 新Workerの準備完了を知れない
* 無停止アップグレード×

ServerEngineモデル2

* スレッドは難しい
  * エラーをちゃんと処理しないと\n
    問題を見逃す
* シグナルは難しい
  * 終了中に何度でもSIGINT
* forkは難しい

ServerEngineモデル3

* ライブラリー化していてえらい

Droonga

# image
# src = images/droonga-model.svg
# relative_height = 100

スライドプロパティー

: enable-title-on-image

false

Droongaモデル1

* 通信はSupervisor↔Worker
* gracefulな再起動をしやすい
  * 新Workerが準備完了\n
    →Supervisorに通知\n
    →旧Workerをgracefulに終了
* 無停止更新ができる

Droongaモデル2

* シンプルになる
  * 1スレッド・シグナルなし
  * stop中にn回stopがこない
  * 終了処理中に割り込まれない
* イベント駆動は複雑

ネットワークネック

* 通信量を減らす
  * ((*データを減らす*))(ムリなら圧縮)\n
    (('note:(LZ4で十分ならLZ4、ムリならzlib)'))
* ノンブロッキングI/Oと多重化
  * 拡張ライブラリー必須
  * →Cool.io, nio4r, EventMachine
  * イベント駆動なコードは複雑

イベント駆動なコード

# coderay ruby

Coolio::TCPServer.new(HOST, PORT) do |client|
  n_reads = 0
  client.on_read do |data|
    p data
    client.write(data)
    n_reads += 1
    if n_reads == 2
      client.on_write_complete {client.close}
    end
  end
end

同期っぽく書けるAPI

# coderay ruby

Coolio::TCPServer.new(HOST, PORT) do |client|
  Fiber.run do # <- 並行にしたい処理を明示
    client.extend(Synchronizable) # <- 42行
    2.times do
      data = client.read
      p data
      client.write(data)
    end
    client.close
  end
end

同期っぽく書けるAPI

* ユーザーがFiberを書くのが\n
  カッコ悪い
* 同期っぽい中で\n
  並行に処理したくなったら?
  * pubsubっぽいことをしたいとか

Promiseな世界

# coderay ruby

server.accept.then do |client|
  client.read.then do |data|
    p data
    client.write(data)
  end.then do
    client.close
  end
end.catch do |error|
end

Promiseな世界

* 繰り返しを書きにくい
* メソッドチェーンがカッコ悪い
* catchがカッコ悪い

API案:基本

# coderay ruby

# 同期っぽいAPI
clinet = server.accept
# 非同期API
server.accept do |request|
  begin
    client = request.socket
  rescue
  end
end

API案:組み合わせ

# coderay ruby

server.accept do |request|
  client = request.socket
  2.times do
    data = client.read
    p data
    client.write(data)
  end
  client.close
end

API案

* Fiberが見えない
* 書き方の組み合わせが自然
  * ブロックなし→同期っぽいAPI
  * ブロックあり→非同期API
* 実装していない😜

I/Oとメモリーネック

* データストアをCで書く
  * Droonga: Groonga
  * ROMA: Tokyo Cabinet, SQLite3
* コアの機能もCで書く
  * データコピーも減らしたいとき

強みの選び方 - 性能

* 使いやすさで勝負する
* 最高速で勝負しない
  * でも、十分な速度は目指す
  * ボトルネックにならなければよい
* 多機能で勝負しない

機能

* 多機能をウリにしない
  * 多機能だと遅くなる(ことが多い)
* 組み込みの機能より拡張性
  * →プラグイン機能
  * Fluentd, ROMA, Droonga

プラグイン機能のポイント

* 開発者向け
  * 作りやすい
  * テストしやすい
  * リリースしやすい
* ユーザー向け
  * インストールしやすい
  * 設定しやすい

開発者向け

* 作りやすさ
  * scaffoldいらずのAPI
* テストしやすさ
  * ドライバー・スタブを提供
* リリースしやすさ
  * gem

ユーザー向け

* インストールのしやすさ
  * Rubyをそんなに知らない前提なのに\n
    直接gemを使ってもらうのってアリ?
* 設定のしやすさ
  * できるだけ少なく
  * できればno configuration

強みの選び方 - 機能

* 使いやすさで勝負する
* 最高速で勝負しない
* 多機能で勝負しない
  * 組込機能よりも簡単拡張で勝負
  * Ruby初心者でも開発できる簡単さ
  * →プラグイン開発でRubyデビュー\n
    (('note:(tDiaryスタイルのRuby浸透方法)'))

まとめ1

* 趣意書
  * Rubyを浸透させたい!
* この発表
  * 浸透促進案の提案
  * ミドルウェア分野での促進案

まとめ2:促進案

* たくさんの人が使う
  * 他の方がよさそうでも\n
    Ruby製を優先して使う
  * 使っていることを広める
* たくさん作る

まとめ3:作るノウハウ

* 使いやすさで勝負する
  * ユーザーの手間を減らす
* 最高速で勝負しない
  * ボトルネックにならなければよい
* 多機能で勝負しない
  * 組込機能より拡張性

おまけ

Droongaの紹介

Droongaとは

* Ruby製
* 分散全文検索エンジン
* SPOFなしの構成

Droongaの特徴

* 処理をパイプラインとして\n
  つなげられる(('note:予定'))
* 処理はプラグイン可能
  * Rubyで簡単に(('note:(予定)'))書ける
* Groonga互換API提供
  * Groonga = 既存全文検索エンジン

Droongaの実装:性能

* レイテンシー
  * Groongaより高いけど\n
    気になるほどではない
* スループット
  * ノード数を増やせばGroongaより速い

Droongaの実装:機能

* プラグインで拡張可能

Droongaの実装:使いやすさ

* インストール
  * インストーラー提供で簡易化
* これからがんばる
  * 設定・更新・運用
  * プラグインの作りやすさ

Droonga

droonga.org/n n 11/29(いい肉の日)n Groongaイベント開催((‘note:(東京)’))n ((‘note:groonga.doorkeeper.jp/events/15816’))