Posted at

rubyのバッチやデーモンに便利なDBラッパー

More than 5 years have passed since last update.


背景

Socket.IOを使ったスマホ向けSPA(Single Page Application)のサイトSmartFXを運用しています。

指値、逆指値等の予約注文を実装するにあたって、DBまわりの処理をいかに効率よく処理するかがポイントとなりました。

なぜなら、為替レートは1秒間に複数回更新されることがあり、また通貨ペアも複数あります。

その更新の度に該当する予約注文があるか検索し、該当レコードがあれば注文を約定する必要があります。

そのため直にSQLを叩く必要があるのですが、Webアプリ自体はRailsで書かれているので、その設定ファイルを活用しつつDBに対して直接クエリを発行できるようなDbwrapperというGemを作りました。

また、いくつかの便利メソッドも追加しています。

sqlite3、mysql2、postgresqlに対応。


install

Gemfileに下記を追加します。


gemfile

gem 'dbwrapper'

#sqlite3を使わない場合は下記をコメントアウト
gem 'sqlite3'
#postgresqlを使わない場合は下記をコメントアウト
gem 'pg'
gem 'pg_typecast'
#mysql2を使わない場合は下記をコメントアウト
gem 'mysql2'
gem 'mysql2-cs-bind'


使い方

#sqlite3

db=Dbwrapper::DB.new(database: "test.sqlite3",adapter: "sqlite3")
#mysql2
db=Dbwrapper::DB.new(database: "test",adapter: "mysql2",username: "root",password: "xxxx",host: "xxxx")
#postgresql
db=Dbwrapper::DB.new(database: "test",adapter: "postgresql",username: "root",password: "xxxx",host: "xxxx")
#もしくは単にRailsのdatabase.ymlを読み込ませる
db=Dbwrapper::DB.new(YAML::load(File.open('database.yml'))["development"])

#dbのドライバーインスタンスは下記で直接参照可能
db.client

#create tableはDBによって使える型が全然違うので意識が必要 ここはdatetime型を使っているのでmysql2のみ
#create tableの実行
db.query("create table users (id integer,name text,created_at datetime)")

#insertの実行
db.query('insert users (id,name,created_at) value(?,?,?)',1,"smith","2014-2014-06-26 00:00:00")

#DB種類を意識せず簡単にlast_idを取れる
db.last_id #=> 1

#selectはHashのArrayが返ってくる
db.query("select * from users") #=> [{"id"=>1, "name"=>"smith", "created_at"=>2014-06-26 00:00:00 +0900}]

#update
db.query("update users set name=? where id = ?","hay",1)

#DB種類を意識せず簡単にaffected_rowsが取れる
db.affected_rows #=> 1

#dbの返却値も直接参照可能
db.result

#便利メソッド table名とhashのArrayを渡すことでmultiple insertが可能。
#sqlite3の場合はmultiple insert非対応なので単にレコード数回insertが実行されます。
db.xinsert("users",[{id: 2,name: "hello",created_at: Time.now},{id: 3,name: "good",created_at: Time.now}])

#便利メソッド テーブル名とパスを指定することでpathの配下にテーブル名 + .csvの名前でバックアップが作成されます。
db.backup_table("users","/var/backup")

#truncate もしsqlite3だった場合は単にdeleteが呼ばれます。
db.truncate("users")

#便利メソッド テーブル名とパスを指定することでpathの配下のテーブル名 + .csvのファイルのデータを読み込み、テーブルをリストアします
db.restore_table("users","/var/backup")

#DBのclose postgresqlの場合はfinishが呼ばれる
db.close


まとめ

バッチやデーモンでは大量のinsertを行ったり、複数のレコードを同時にupdateかけたり、複雑なSQLを書きたいことがよくあるので、その際に便利に使えます。

バッチの実行前と実行後で関連するtableのbackupを実行することで問題があった場合の検証にも使えます。