ActiveRecordのようにTokyoTyrantを使う、MiyazakiResistanceを作りました

76de73a1dae79a86bb99a813bd8e8e0a?s=30&d=http%3a%2f%2fkaeruspoon%.net2f2014%2fimages%2fno imageTsukasa OISHI

TokyoTyrantをActiveRecordのように気軽に使える、MiyazakiResistanceを作りました。Cabinet(内閣)、Tyrant(傀儡)という意味を見て、地方から傀儡政権に抵抗するというイメージが勝手にわいてきたのでした。
MiyazakiResistanceは、TokyoTyrantのテーブルデータベースを対象にしています。データスキーマのないTokyoTyrantに、あえてスキーマの制約を与えることで管理をしやすくしています。TokyoTyrantでは異なる種類のデータをひとつのデータベースに置くことはあまりないと想定してのことです(キー値がかぶるし)。
MiyazakiResistanceは、レプリケーションにも対応しています。マスターと複数のスレーブ構成でも、デュアルマスタ構成でも大丈夫です。デュアルマスタはアクティブ-スタンバイで運用することを想定していて、アクティブ側がタイムアウトしたときはスタンバイ側にフェイルオーバーします。
まだ開発中なのでバグはあちらこちらに残っているのですが、適宜解消していく予定です。
github上で公開しています。 http://github.com/tsukasaoishi/miyazakiresistance/tree/master

1.インストール(gemcutterで公開しています)

sudo gem install miyazakiresistance

2.モデルクラスの定義

require 'rubygems'
require 'miyzakiresistance'
class Example < MiyazakiResistance::Base
  set_server "localhost", 1975, :write
  set_timeout 60
  set_column :name, :string
  set_column :age, :string
  set_column :created_at, :datetime
end

MiyazakiResistance::Baseを継承して定義します。

set_server でTokyoTyrantのサーバーを指定します。第一引数がホスト名、第二引数がポート番号です。第三引数はオプションで指定しなければ:readonlyと解釈されます。:writeは読み書き両用のサーバーです。
マスターと複数のスレーブで構成するときは、

  set_server "master", 1975, :write
  set_server "slave1", 1975
  set_server "slave2", 1975

と宣言します。読みのときにどのサーバーが選択されるかはランダムに決定されます。
デュアルマスタ構成のときは、

  set_server "active", 1975, :write
  set_server "standby", 1975, :standby

:standbyで指定されたサーバには、読み込みのみアクセスします。さらにフェイルオーバーの対象に指定されます。

set_timeout でタイムアウト値を宣言します。単位は秒です。タイムアウト値を超えると、対象のサーバーはコネクションプールから排除されます。対象のサーバーがマスタの場合、デュアルマスタでスタンバイが存在しているときはフェイルオーバーします。スタンバイが存在しなければ例外を吐きます。

set_columnでデータ構造を宣言します。キー値はself.idとして規約で定義されているので宣言の必要はありません。第一引数にカラム名、第二引数にデータの型を指定します。データの型は、:string(文字列)、:integer(数値)、:date(日付)、:datetime(日時時刻)の4種類です。:integerは実際には実数でも問題ありません。:dateはDateクラス、:datetimeはTimeクラスに対応しています。

実際のCRUD操作はActiveRecordとほぼ同様です。conditionsの書き方だけが少し違っていて、条件同士は必ずAND結合されるので、条件の間にAND, ORなどを書く必要はありません。

work = Example.new(:name => "tsukasa")
work.age = 34
work.save
Example.count
Example.find(:all, :conditions => ["name = ? age = ?", "tsukasa", 34], :order => "created_at DESC", :limit => 10)

TokyoTyrantのオプティマイザは最初に型が一致したカラムのインデックスを使用します。

Example.find(:all, :conditions => ["created_at >= ? created_at <= ?", yesterday, today])

だと、最初の created_at > ? にはインデックスが適用されますが、二つ目の条件 created_at

Example.find(:all, :conditions => ["created_at between ?", [yesterday, today]])

とすればいいでしょう。