Rails ActiveRecordのコールバックとdebug.gem

Railsのコールバックのことを学び直した

こんにちは、まいむです!

少し前に FBCのチーム開発でメールが配信されたりされなかったりする問題が一時期発生していました。自分の開発担当箇所にも影響があるかを調べた際にRailsのコールバックについて以前より理解が深まったため、まとめておきたいと思います。

私がチーム開発で担当したissueにコールバック内に実装されている通知処理を別のgemに置き換えるという内容のものがありました。

私の場合はサイト内通知とメール共に届いていたのですが、似たような置き換え処理でメールが飛ばない場合があり、なぜメールが飛んだり飛ばなかったりするのかが気になっていました。

そこでまず、そもそもコールバックについて自分の言葉で説明ができない状態だったため、RailsガイドやパーフェクトRuby on Railsの該当箇所を読んで整理しました。

Rails ActiveRecordのコールバック

コールバックは、オブジェクトが作成・更新・削除される際の状態が切り替わる「前」または「後」にロジックをトリガに特定の瞬間に呼び出されるメソッドのことです。

オブジェクトのライフサイクルの中で利用が可能なコールバックはさまざまな種類があるのですが、メールが飛ばない場合がある処理が実装されていたコールバックは after_save が利用されていました。

実際のコードとは違いますがイメージ的にはこんな感じです。

class Notification < ApplicationRecord
  after_save do
    NotificationJob.perform_later(id)
  end
end

class NotificationJob < ApplicationJob
  queue_as :default

  def perform(id)
    notification = Notification.find(id)
    # TO DO
  end
end

これの何が問題かというと、非同期処理をする際に after_saveを利用してジョブをキューに入れると、トランザクションの処理に時間がかかった場合など、処理中に非同期処理が実行されてしまうことがあります。 その場合、COMMITが完了していないためINSERT されたレコードは見つけることができません。

ここまででなんとな〜くメールが飛んだり飛ばなかったりする原因が見えてきました。

ただ、INSERT されたレコードが見つけられないという点がよく分からずでした。

debug.gemがとても便利だった

チーム開発に取り組まれている他の受講生の方がこのメールの問題を調査されていて、調査時にVSCodeでdebug.gemを使ってとても便利だったことを日報に書かれているのを見つけ、ペアプロをお願いしてdebug.gemの使い方を教えていただきました。

設定方法は Railsの練習帳 に詳しく書かれています。

VSCodeVSCode rdbg Ruby Debugger拡張をインストールすると、デバッグコンソールでrailsl serverを実行した際と同様にlocalhostを立ち上げた状態でdebug.gemが利用できます。

デバッグコンソールを開いた状態でlocalhost側で通知を飛ばす操作を実行します。

操作の結果、エラーがあるとデバッグコンソール上でデバッガーが止まってくれ、エラーの確認が可能です。

以下はペアプロの際に教えていただいたこととパーフェクトRuby on Railsの説明を参考に整理した内容です。

  • ジョブをキューに入れる際、Active Recordのオブジェクトは GlobalID ライブラリ にシリアライズの処理がされ、Active Jobを実行する時にActive Record のオブジェクトがデシリアライズされます。
  • この時、トランザクションがCOMMITされていない場合はエラー「ActiveRecord::RecordNotFound」が発生します。

debug.gemを使って上記エラーの発生を確認することができ、INSERT されたレコードが見つけられないという点がようやく理解できました。

また、これがメールが飛んだり飛ばなかったりする原因ということも分かりました。

上記問題への対処は複数ありそうですが、コールバックを利用する場合は after_commit を使えば、レコードが見つけられない問題は回避できそうです。

まとめ

コールバックはrailsで保存/更新/削除などが関係する処理を実装する際に便利に使えそうと漠然と捉えていましたが、今回のようにトランザクションのCOMMIT前後で問題が発生したり、意図しない結果が発生する場合があるということを学びました。

自分がissueで対応したようにコールバックを使わずとも実装が可能な場合もあるため、今後もコールバックを利用するかはサービスの方針やテストの実装状況などに合わせて複合的に判断したいなと思いました。

もし、今回のブログの内容で誤りがありましたらコメントやTwitterでお知らせいただけると幸いです。

FBCのチーム開発で、Railsに関する理解が日々深まっているため、学んだ内容を今後もまとめていきたいと思います。