いくつかのコールバックの落とし穴(そしてRails 5の修正)
ActiveRecordコールバックは、モデルのライフサイクルのさまざまな段階でコードを実行する簡単な方法です。
たとえば、Q&Aサイトがあり、すべての質問を検索できるようにしたいとします。質問に変更を加えるたびに、ElasticSearchのようなものでインデックスを作成する必要があります。インデックス作成には時間がかかり、緊急ではないため、Sidekiqを使用してバックグラウンドでインデックス作成を行います。
これは、after_save
を使用するのに最適な時期のようです。 コールバック! したがって、モデルでは、次のように記述します。
class Question < ActiveRecord::Base
after_save :index_for_search
# ...
private
def index_for_search
QuestionIndexerJob.perform_later(self)
end
end
class QuestionIndexerJob < ActiveJob::Base
queue_as :default
def perform(question)
# ... index the question ...
end
end
これはうまくいきます!または、少なくとも、そうです。 さらに多くのジョブをキューに入れて、これらのエラーが表示されるまで:
2015-03-10T05:29:02.881Z 52530 TID-oupf889w4 WARN: Error while trying to deserialize arguments: Couldn't find Question with 'id'=3
確かに、Sidekiqはジョブを再試行し、次回はおそらく機能します。しかし、それでも少し奇妙です。 保存したばかりの質問がSidekiqで見つからないのはなぜですか?
Railsはafter_save
を呼び出します レコードが保存された直後のコールバック。 ただし、そのレコードは、データベースがトランザクションするまで、Sidekiqが使用しているような他のデータベース接続では表示できません。 コミットされますが、これは少し後で発生します。 これは、Sidekiqが質問を保存した後、コミットする前に、質問を見つけようとする可能性があることを意味します。記録が見つからず、爆発します。
この問題は非常に一般的であるため、Sidekiqにはそれに関するFAQエントリがあります。そして、簡単な修正があります。
after_save
の代わりに :
class Question < ActiveRecord::Base
after_save :index_for_search
# ...
end
after_commit
を使用する :
class Question < ActiveRecord::Base
after_commit :index_for_search
# ...
end
そして、Sidekiqがあなたのモデルを見ることができるまで、あなたの仕事はキューに入れられません。
したがって、バックグラウンドジョブをキューに入れたり、行った変更について別のプロセスに通知したりする場合は、after_commit
を使用してください。 。そうしないと、触れたばかりのレコードが見つからない可能性があります。
しかし、もう1つ問題があります…
OK、after_save
の束を切り替えました after_commit
を使用するためのフック 代わりは。すべてがうまくいくようです。すべてをチェックインして家に帰る時間ですよね?
まず、テストを実行する必要があります:
require 'test_helper'
class QuestionTest < ActiveSupport::TestCase
test "A saved question is queued for indexing" do
assert_enqueued_with(job: QuestionIndexerJob) do
Question.create(title: "Is it legal to kill a zombie?")
end
end
end
1) Failure:
QuestionTest#test_A_saved_question_is_queued_for_indexing [/Users/jweiss/Source/testapps/after_commit/test/models/question_test.rb:7]:
No enqueued job found with {:job=>QuestionIndexerJob}
おっと!テストはジョブをキューに入れるべきではありませんか? そこで何が起こったのですか?
デフォルトでは、Railsは各テストケースを独自のデータベーストランザクションでラップします。これは本当に物事をスピードアップすることができます。テスト中に行ったすべての変更を元に戻すには、1つのデータベースコマンドだけで済みます。
ただし、これはafter_commit
も意味します コールバックは実行されません。 after_commit
のため コールバックは、最も外側の場合にのみ実行されます トランザクションがコミットされました。
save
を呼び出すとき テストケース内では、(多かれ少なかれ)トランザクションをコミットしますが、それは2番目に外側です。 今トランザクション。つまり、after_commit
コールバックは、期待どおりに実行されません。そして、それらの内部で何が起こっているかをテストすることはできません。
この問題も簡単に修正できます。 test_after_commit
を含めます Gemfile内のgem:
group :test do
gem "test_after_commit"
end
そしてあなたのafter_commit
フックは最後から2番目の後に実行されます トランザクションがコミットされます。 これがあなたが期待していたことです。
「それは変だ。 Railsに付属するコールバックをテストするために、まったく別のgemを使用する必要があるのはなぜですか?自動的に発生するのではないでしょうか?」
あなたが正しい。それは変だね。しかし、それは長い間奇妙なままではありません。
Rails 5が出荷されたら、test_after_commit
について心配する必要はありません。 。この問題は約1か月前にRailsで修正されたためです。
私自身のコードでは、after_commit
を使用しています 多くの。私はおそらくafter_save
を使用するよりも多く使用しています !しかし、問題と奇妙なエッジケースがないわけではありません。
バージョンごとですが、改善されています。 また、after_commit
を使用する場合 適切な場所では、多くの奇妙でランダムな例外が発生しなくなります。
-
Vue、Vuex、Railsを使用したフルスタックアプリケーションの構築
スケーラビリティを念頭に置いてフルスタックアプリケーションを構築することは、特に、完全なタイプスクリプトをサポートする最新バージョンのVueおよびVuexを使用して構築する場合、威圧的になる可能性があります。この記事では、不健康な家畜への治療の処方を管理するCRUDアプリケーションを探索することで、APIリクエストとデータベースの相互作用を処理するVuex4.0を使用した状態管理からスケーラブルなフルスタックアプリケーションを構築するために知っておく必要のあるすべてを読者に教えます。バックエンドはRailsで構築され、フロントエンドによる統合のために基本的なCRUDAPIを公開します。 ほと
-
Chrome と Edge で RESULT_CODE_HUNG を修正
複数のブラウザーがインターネット ドメインの拠点を占めていますが、Google Chrome と Microsoft Edge はそのリストの中で際立っています。 Chrome は世界中の何百万人ものユーザーにとって頼りになる選択肢ですが、私の数人の Windows ユーザーには Edge が好まれています。しかし、これらの優れたブラウザーにもいくつかの欠陥があります。ユーザーは、インターネット サーフィン中にいくつかのよくあるエラーに気を取られることがよくあります。そのようなよくあるエラーの 1 つに Aw Snap! RESULT_CODE_HUNG . Chrome、Edge、Brave