アヤオのレベリングキロク

日々のインプットをここでアウトプットします

【Rails】トランザクションについて見直してみる

きっかけ

  • 働いていた時、特にコード規約がなく、いろんな人が触ったコードだったためか、いろんな書き方がされていた
  • 複数のレコードを同時にsaveする時に、使うのは知っている
  • ただベストプラクティスはどうなのかをちゃんと考えたことなかったので見てみる

学んだこと

そもそもエラーハンドリングの心得から勉強してみる

  • こちらの記事がとても勉強になった
  • Ruby on Railsを使って、大事なところを簡潔に書いてくれていてありがたい
  • アンチパターンと、その場合どうすべきかが書かれている

qiita.com

  • 運用のことを考えると、以下の2つの内容はすごく重要だったな。。

特別な理由がない限り、エラー情報を破棄してはいけない。 最悪の場合、エラーの原因を特定できなくなる。

 

エラー処理も必ずテストすること。 「たぶん動くはず」でそのままコードをリリースしてしまうと、最悪の場合「エラー処理のバグによるエラー」が発生してしまい、元のエラー内容が失われてしまう場合がある。(二重障害) こうなるとエラーの原因を調査するのが非常に難しくなる。

Railsトランザクションはどう使うのか?

  • 上記の記事の最後の項目が今回の内容に近い

qiita.com

  • 基本的な書き方はRailsガイドを確認

railsguides.jp

  • これらをみるとわかるように、.transactionをつけた場合は、.saveを通った瞬間DBにcommitされるわけではなく、ブロックを抜けた瞬間にDBにコミットされる
    • つまりコミットタイミングをコントロールしている
  • . lock.with_lockを使うことで、同じレコードを書き込まないようにすることもできる
    • この時はdeadlockが発生しないような処理にする

ふと思い出した他の書き方

  • 昔やってた時に、モデルに対して.transactionをかけただけでなく、以下のような書き方もしてたな〜と
ActiveRecord::Base.transaction do
  # データを作るような処理とsave
end
  • 要は同じ処理で得られる結果は同じ
    • ActiveRecord::Baseは各モデルの親クラスであるため
    • ModelでActiveRecordを継承しているものが対象

techracho.bpsinc.jp

応用編

  • その他、いろんなパターンのトランザクションの使い方は、この記事がわかりやすかった

techracho.bpsinc.jp

  • 中でもなるほど〜って思ったのが、「複数のデータベースコネクションに分散されない」ということ
    • 複数DBを取り扱ったことがなかったので知らなかった
    • 複数DBを跨いでトランザクションをかける時には、.transactionをネストにしないといけないそう

おまけ

  • ついでにコールバックも読んでみた
  • コールバックも密接に関わってくるので、また今度まとめたい

railsguides.jp

まとめ

  • Railsでのトランザクションをどうかけるべきかを改めて見てみた
  • ベストプラクティスが何か?までは答えが出てないが、こうするのが良さそうという基準は自分の中にはできた
    • 基本的にModel.transaction do ... endにすると何のモデルに対してトランザクションがかかるかわかりやすい
    • エラーは握りつぶさず、正常に通知させる