無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

トランザクション

transactionを利用して、一連の更新処理をアトミックに実行します。

トランザクションを使う

テーブル定義:

CREATE TABLE kittens (
  id int(11) NOT NULL auto_increment PRIMARY KEY,
  name VARCHAR(255),
  color VARCHAR(255),
  age int(4)
) TYPE = InnoDB ;

サンプル:

# 接続先サーバーの設定は省略
# ロガーの設定/ログを"debug.txt"に出力する。
ActiveRecord::Base.logger = Logger.new("debug.txt")

class Kitten < ActiveRecord::Base
  def to_s
    return name
  end
end

# transaction を利用。
# ブロック実行後に変更がコミットされる。
mii  = Kitten.new
kuro = Kitten.new
Kitten.transaction {
  mii.name = "mii"
  kuro.name = "kuro"
  mii.save
  kuro.save
}
puts Kitten.find_by_name("mii") # =>mii
puts Kitten.find_by_name("kuro") # =>kuro

出力:

mii
kuro

ログを見ると、INSERTがBEGIN/COMMITで囲まれていることがわかります。

ログ(debug.txt):

...
    [4;35;1mSQL (0.000000)  [0m     [0mBEGIN  [0m
    [4;36;1mSQL (0.015000)  [0m     [0;1mINSERT INTO kittens (`name`, `color`, `age`) VALUES('mii', NULL, NULL)  [0m
    [4;35;1mSQL (0.000000)  [0m     [0mINSERT INTO kittens (`name`, `color`, `age`) VALUES('kuro', NULL, NULL)  [0m
    [4;36;1mSQL (0.000000)  [0m     [0;1mCOMMIT  [0m
...

トランザクションをつかわない

transactionを使用しない場合、更新が個別にBEGIN/COMMITされます。

サンプル(トランザクションを利用しない):

# transaction を利用しない。
# saveごとに個別にコミットされる。
mii  = Kitten.new
kuro = Kitten.new
mii.name = "mii"
kuro.name = "kuro"
mii.save
kuro.save

ログ:

...
    [4;35;1mSQL (0.000000)  [0m     [0mBEGIN  [0m
    [4;36;1mSQL (0.016000)  [0m     [0;1mINSERT INTO kittens (`name`, `color`, `age`) VALUES('mii', NULL, NULL)  [0m
    [4;35;1mSQL (0.000000)  [0m     [0mCOMMIT  [0m
    [4;36;1mSQL (0.000000)  [0m     [0;1mBEGIN  [0m
    [4;35;1mSQL (0.000000)  [0m     [0mINSERT INTO kittens (`name`, `color`, `age`) VALUES('kuro', NULL, NULL)  [0m
    [4;36;1mSQL (0.000000)  [0m     [0;1mCOMMIT  [0m
...

ロールバック

transactionブロック内で例外がスローされると、トランザクションロールバックされます。

サンプル(ロールバック):

mii  = Kitten.new
kuro = Kitten.new
Kitten.transaction {
  mii.name = "mii"
  kuro.name = "kuro"
  mii.save
  kuro.save
}

# ブロック内部で例外をスローすると、トランザクションがロールバックされる。
begin
  Kitten.transaction {
    mii.name = "mii.edit"
    kuro.name = "kuro.edit"
    mii.save
    kuro.save
    fail "test"# 例外をスロー
  }
rescue
  p $!
end
puts Kitten.find_by_name("mii") # =>mii
puts Kitten.find_by_name("kuro") # =>kuro

出力:

#<RuntimeError: test>
mii
kuro

ログ:

...
    [4;35;1mSQL (0.160000)  [0m     [0mCOMMIT  [0m
    [4;36;1mSQL (0.010000)  [0m     [0;1mBEGIN  [0m
    [4;35;1mKitten Update (0.020000)  [0m     [0mUPDATE kittens SET `age` = NULL, `color` = NULL, `name` = 'mii.edit' WHERE `id` = 3  [0m
    [4;36;1mKitten Update (0.000000)  [0m     [0;1mUPDATE kittens SET `age` = NULL, `color` = NULL, `name` = 'kuro.edit' WHERE `id` = 4  [0m
    [4;35;1mSQL (0.030000)  [0m     [0mROLLBACK  [0m
...

はまり道

MySQLトランザクションを使うには、テーブルをトランザクションセーフのテーブル(InnoDBBDB)にする必要があります。→ MySQL 4.1 リファレンスマニュアル :: 7 MySQL のテーブル型
最初、デフォルトのMyISAMで試しており、しばらくはまりました。


参考:Module ActiveRecord::Transactions::ClassMethods