sequelのafter_commit

  • このエントリーをはてなブックマークに追加

DBのライブラリとしてsequelを良く使います。使い勝手が好みです。

Modelを利用しないでトランザクションを利用する時は次のような処理を書きます。

db = Sequel.connect(...)
db.transaction do
db[:xxx].insert(...)
db[:xxx].where(...).update(...)
end # commit

何を寝ぼけていたのか、こんな処理を書いてしまっていました。
commit前に更新通知の送信をしているので、受信側はcommit前にメッセージを受け取り更新前の情報を参照してしまいます。

db.transaction do
cond = db[:xxx].where(...).select(...).first
if cond
db[:xxx].insert(...)
else
db[:xxx].where().update(...)
mq.publish(...) # 更新通知をMessageQueueに送信する
end
end # ここでcommit

もちろんtransactionブロックを抜けたところにロジックを書いても良いですが、それだと上記のように複雑な条件文の中にあると、一律実行する事はできずにフラグを用意する必要があります。

そこでafter_commitもしくはafter_rollbackを使います。

db.transaction do
cond = db[:xxx].where(...).select(...).first
if cond
db[:xxx].insert(...)
else
db[:xxx].where().update(...)
db.after_commit do
mq.publish(...) # 更新通知をMessageQueueに送信する
end
end
end

注意点は、after_commitで変数を参照している場合、ブロックの最後の値になるという事です。threadのようにその時の変数の値を渡すことができません。以下の例ではafter_commitのブロック中ではx=10ではなくx=11になります。

db.transaction do 
x = 10
db.after_commit { puts x } # x = 11
x = 11
end