ねこものがたり

いちにちいっぽ

transactionの使い方

transactionとは

An input message to a computer system dealt with as a single unit of work. ( transaction | Definition of transaction in English by Oxford Dictionaries )


商取引、売買、執行、取扱、議事録などの意味を持つ英単語。ITの分野では、取引記録などの意味の他に、ソフトウェアの処理方式の一つで、互いに関連・依存する複数の処理をまとめ、一体不可分の処理単位として扱うことを指す場合が多い。( トランザクションとは - IT用語辞典 )


トランザクションは、複数のユーザーが同時にデータベースを操作する状況において、データベースに対する複数の操作(選択、更新、削除など)を実行している途中で仮にエラーが発生したとしても、データの不整合が起きないことを保証するリレーショナルデータベースシステム(RDBMS)のメカニズム( トランザクションでデータの不整合を防ぐ:SQL実践講座(25) - @IT)

RDBMSの機能の1つだそうです。

でも具体的にはDBの種類で周辺機能が大きく違うらしい(参考: 改訂第3版 すらすらと手が動くようになる SQL書き方ドリル:書籍案内|技術評論社)。考え方を押さえて、使うDBとか言語とかフレームワークに合わせてやりたいことが書ければ良さそう!

使い所:エラーが発生したとしても、データの不整合が起きないことを保証する

「transaction rails」などとググったり本で探したりするとすぐ出てくるのが"送金処理"の例。

Aさんの口座テーブルとBさんの口座テーブルがある。 AさんからBさんに1万円送金する場合、
* Aさんの口座テーブルから1万円出金がある
* Bさんの口座テーブルに1万円入金がある
という流れになる。

この時に「Aさんの口座テーブルから1万円出金」と「Bさんの口座テーブルに1万円入金」は両方成り立ってないといけない。 仮に「Aさんの口座から1万円出金したけどBさんの口座に届けることが出なかった」というようなトラブルがあったと場合、そのまま途中で処理が止まって「消えた1万円事件」が起きたらやばい。その時はシステムにはよしなに動いていただいてAさんのもとに1万円戻ってきてほしい。

「失敗したらロールバックする(=もとに戻す)」というのがtransaction。1万円は消えたりしない。(送金できないとかありえないけど、とりあえずお金消えなくてなくてよかった!)

APIでは次のコードが例示されている。

ActiveRecord::Base.transaction do
  david.withdrawal(100)
  mary.deposit(100)
end

ポイント:失敗のときは例外を発生させる

transactionでロールバックするかどうかのトリガーは例外が発生するかどうか。 だから何かが失敗した時には例外が発生するように書かないといけない。

例えばUserクラスのインスタンスsaveしたい場合

例外が発生しない書き方:save→失敗したときの戻り値はfalse

User.save(name: "neko")とか。

saveソースコードはこれ。

def save(*args, &block)
  create_or_update(*args, &block)
rescue ActiveRecord::RecordInvalid
  false
end

(ActiveRecord::Persistence)

例外発生する書き方 :save!→失敗時にはraiseが実行される

User.save!(name: "neko")など。

def save!(*args, &block)
  create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
end

( ActiveRecord::Persistence )

このようにsavesave!では失敗のときの処理と戻り値が異なっていてfalseが返ってくるときにはtransactionが効かないのでsave!とかupdate!などを使って書く必要がある。! の有無でぜんぜん違う・・・!

参考記事

ActiveRecord::Transactions::ClassMethods

Rails tips: ActiveRecordのトランザクションの概要(翻訳)

RailsのTransactionの使い方 - Qiita

Railsアプリケーションにおけるエラー処理(例外設計)の考え方 - Qiita