2014年06月

perl の SQL::Maker (と SQL::QueryMaker) を ruby に移植した

表題のものがこちらにあります https://github.com/sonots/ruby-sql-maker

細かい使い方はドキュメント書いている途中なので少々お待ちを> < 
=> 追記:用意しました

動機

SQL を文字列として自力で組み立てているコードがあって、 そこを綺麗にしたかったんだけど、 Ruby でクエリビルダーというと Arel ぐらいしかなくて、 しかし、Arel で書くと非常につらぽよな感じだったので作った。

ActiveRecord (以下、AR) とか Sequel とかの ORM に丸っと置き換えても良かったんだけど、 そのアプリにとってはちょっと重すぎるというのと、 クエリ生成コードを綺麗にしたい&セキュアにしたいだけだったので、 理想的には使いやすいクエリビルダがあれば良いのになぁという感じだった。 で、理想を求めた。

使い方 - Arel との比較

Arel がどんなかんじに辛いのかは、 Arelで色んなSQLを組み立ててみる - @ryopekoのなにか の記事を見てもらうと想像つくと思うんだけど、 一番シンプルなやつでもこんなかんじで、ちょっと「うっ」と来る。覚えられる気がしない。AR は内部で Arel を使っているわけだけれど、如何に AR がいい感じにしてくれているのかが良くわかる実例。

books = Arel::Table.new :books
books.project(Arel.sql('*')).where(books[:id].eq(1)).to_sql # => SELECT * FROM "books" WHERE "books"."id" = 1

SQL::Maker を使うとこうなる。メソッドチェーンで呼び出すだけになって直感的。

builder = SQL::Maker::Select.new(:quote_char => '"', :auto_bind => true)
builder.add_select('*').add_from('books').add_where('books.id' => 1).as_sql # => SELECT * FROM "books" WHERE "books"."id" = 1

ライセンスについて

SQL::Maker のライセンスは「perlと同じ」と書いてあるので、 GPL と Artistic License のデュアルライセンスになっているのだけど、 移植の場合にこれを果たして MIT ライセンスにしてもいいのだろうか、とか思ったので、 社内の有識者に質問したりしてた。

cf. perl のライブラリ(ライセンスはperlと同じ)をrubyに移植した場合、GPL汚染を受けるのか?

で、まぁ色々悩んだわけですが、「MIT ライセンス」にしましたので、 あとは @zigorou さんによろしくおねがいしたいと思います。╭( ・ㅂ・)و ̑̑

※ ちなみに SQL::QueryMaker は MIT ライセンスでOK

perl 版との違い

perl 版わかる人にしか伝わらないかもだけど、書いておく。

スカラーリファレンスの扱い

perl の SQL::Maker は内部的に文字列と、文字列リファレンスで分岐していて、

  1. 文字列ならクォートする
  2. 文字列リファレンスならクォートしない

というような扱いをしているんだけど、ruby だと全部リファレンスなので、 この区別ができない。なので、ruby 版では、クォートされたくない場合は、 sql_raw メソッド(SQL::QueryMaker由来)を使って、オブジェクトを渡してもらうことにした。

perl 版

builder->add_select(\'COUNT(*)')->add_from('books')->as_sql();
#=> SELECT COUNT(*) FROM "books"

ruby 版

include SQL::Maker::Helper # sql_raw などを生やす
builder.add_select(sql_raw('COUNT(*)')).add_from('books').as_sql
# => SELECT COUNT(*) FROM "books"
SQL::Maker::SelectSet の Export 関数

perl 版では、

use SQL::Maker::SelectSet qw(union union_all intersect intersect_all except except_all);

みたいに use すると、そのスコープに union や intersect 関数が生える。

ruby 版では、これを SQL::Maker::Helper というモジュールにして

include SQL::Maker::Helper

として使ってもらうようにした。 ただし、名前が sql_unionsql_intersect のように変更してある。

perl では qw(union) のようにして取り込む関数を絞ることができるからまだいいんだけど、 ruby の include だとそういうことができないので、重複の可能性が減る方向にもっていった。

SQL::QueryMaker の Export 関数

今回 SQL::Maker に kazuho さんの SQL::QueryMaker も取り込んだ。 SQL::QueryMaker 由来の関数も

include SQL::Maker::Helper

で生えるようにした。sql_raw とか sql_eq とか。

オートバインド

悲しいことに ruby の代表的な Mysql library である mysql2 には、 prepared statement の機能がない。

そこで、:auto_bind => true オプションを渡すと bind して SQL を返すようにした。

:auto_bind => false (デフォルト)

builder = SQL::Maker::Select.new(:quote_char => '"')
builder.add_select('*').add_from('books').add_where('books.id' => 1)
builder.as_sql
#=> SELECT * FROM "books" WHERE "books"."id" = ?
builder.bind
#=> [1]

:auto_bind => true

builder = SQL::Maker::Select.new(:quote_char => '"', :auto_bind => true)
builder.add_select('*').add_from('books').add_where('books.id' => 1).as_sql
#=> SELECT * FROM "books" WHERE "books"."id" = 1

内部的には escape して ? を置換している。ActiveRecord と全く同じことをやっている。

今後

DBI 由来の sql_type とか、プラグインとかまだ移植してないのでボチボチやっていく。 sql_type はどうしようかな〜

おわりに

Ruby には missing だった使いやすい SQL ビルダーができたので、 ORM 使うほどじゃないなぁって場合は、試してもらえるといいかも。

ActiveRecord を使っている場合でも、 1部直接 SQL を自力で組み立てている人とかけっこういると思うので、 そういうところで使っても便利かもしれないですね。Enjoy!

GrowthForecast/RRDtool チューニング秘伝の書を公開します #monitoringcasual

Monitoring Casual Talks #6 で話して来ました。

こちらで公開しています => GrowthForecast/RRDtool チューニング秘伝の書 

実測値としては、うちは SSD なサーバを使っているんですが、6万グラフを 10sec で rrdupdate できています。rrdfile 全部の容量は 30GB ぐらいですね。

それでも全然スピードが足りないってなった場合は、Yohoushi を使って分散 GrowhtForecast してもらえれば良いかと思います。そういう場合じゃなくてもタグ機能とか便利なんで使うといいですね。Enjoy!

Fluentd の forward 系プラグインの比較

なんか @repeatedly 氏に書いて、と言われたので書いておく。╭( ・ㅂ・)و ̑̑

プラグイン名特徴DNS
キャッシュ
keepalive
オン 
heartbeat
オフ
out_forward複数の対向サーバを指定して
(weighted) ラウンドロビン
でランダムに分散させる。
Fluentd 同梱のやつ。
不可不可
keep-forward最初につながる対向サーバは
out_forward と同様にランダム
に決まるが、それ以降は(できる限り)
同じサーバにデータを送るやつ。
keepalive 機能を持つ
hash-forward接続先サーバをハッシュ関数で
一意に決めるやつ
secure-forwardSSLを使ってセキュアにデータ
送信するやつ
オンのみオフのみ
forward2 (仮称)今はなき Fluentd v11 に載る予定
だったやつ。keepalive、heartbeat
オフオプションが増えている。


非推奨になったネットワークコマンド養成ギプス

RHEL7 も出て ifconfig などが入った net-tools は非デフォルトになる ということで、非推奨になったLinuxネットワークコマンドの代替コマンド養成ギプス作った。

以下を .zshrc とか .bashrc の下のほうにでも足してください。Enjoy!
net_tools_deprecated_message () {
  echo -n 'net-tools コマンドはもう非推奨ですよ?おじさんなんじゃないですか? '
}

arp () {
  net_tools_deprecated_message
  echo 'Use `ip n`'
}
ifconfig () {
  net_tools_deprecated_message
  echo 'Use `ip a`, `ip link`, `ip -s link`'
}
iptunnel () {
  net_tools_deprecated_message
  echo 'Use `ip tunnel`'
}
iwconfig () {
  echo -n 'iwconfig コマンドはもう非推奨ですよ?おじさんなんじゃないですか? '
  echo 'Use `iw`'
}
nameif () {
  net_tools_deprecated_message
  echo 'Use `ip link`, `ifrename`'
}
netstat () {
  net_tools_deprecated_message
  echo 'Use `ss`, `ip route` (for netstat -r), `ip -s link` (for netstat -i), `ip maddr` (for netstat -g)'
}
route () {
  net_tools_deprecated_message
  echo 'Use `ip r`'
}

Resque メモ

ただの自分用備忘録

ブランチ


2014/06 現在 gem になっているのは 1.x 系だが、https://github.com/resque/resque のコードは 2.x 系なので、gem 相当のコードを見たい場合は、https://github.com/resque/resque/tree/1-x-stable ブランチを見る必要がある。

テンプレ

module TaskResque
  @queue = ENV['RACK_ENV'].to_sym

  def self.perform(task_id)
    task = Task.find(task_id)
    begin
      # Do something
   rescue Resque::TermException => e
      # Do something when receiving TERM signal.
      # Rescuing this exception prohibits to enqueue to failure queue 
      # Need to start resque worker with env TERM_CHILD=1 to raise this exception
   end
  end

  def self.enqueue(task_id)
    Resque.enqueue(self, task_id)
  end
end

Resque.enqueue に #perform メソッドを持っている module と引数を突っ込むと env TERM_CHILD=1 bundle exec rake resque:work で動かした worker が実行してくれる。

シグナル
  1. QUIT 処理が全部終わってから終了
  2. TERM 即終了。env TERM_CHILD=1 として resque worker を動かすと、Resque::TermException  を raise してくれるようになる。
reque failure queue

(Resque::Failure.count-1).downto(0).each {|i| Resque::Failure.requeue(i) }

clear failure queue

Resque::Failure.clear 

resque-web

bundle exec resque-web -F -L -p $PORT -o $BIND で resque-web を動かすと、GUI から failure queue を requeue したり clear したり、redis の状態をみたりできるようになる。

resque-web を sinatra に組み込む

resque-web は sinatra rack アプリなので、sinatra アプリに組み込んでしまうこともできる。config.ru で

# config.ru
require 'resque/server'

run Rack::URLMap.new({
  '/' => Web, # the original sinatra app
  '/resque' => Resque::Server,
})

のようにすれば良い。

A Ruby and Fluentd committer working at DeNA. 記事本文および記事中のコード片は引用および特記あるものを除いてすべて修正BSDライセンスとします。 #ruby #fluentd #growthforecast #haikanko #yohoushi #specinfra #serverspec #focuslight
はてぶ人気エントリー