ActiveRecord कॉलबैक आपके मॉडल के जीवन के विभिन्न चरणों के दौरान कोड चलाने का एक आसान तरीका है।
उदाहरण के लिए, मान लें कि आपके पास एक प्रश्नोत्तर साइट है, और आप सभी प्रश्नों को खोजने में सक्षम होना चाहते हैं। हर बार जब आप किसी प्रश्न में परिवर्तन करते हैं, तो आप उसे ElasticSearch जैसी किसी चीज़ में अनुक्रमित करना चाहेंगे। अनुक्रमण में कुछ समय लगता है और यह अत्यावश्यक नहीं है, इसलिए आप इसे साइडकीक के साथ पृष्ठभूमि में करेंगे।
यह 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
ज़रूर, साइडकीक नौकरी के लिए फिर से प्रयास करेगा और यह शायद अगली बार काम करेगा। लेकिन यह अभी भी थोड़ा अजीब है। साइडकीक को वह प्रश्न क्यों नहीं मिल रहा है जिसे आपने अभी सहेजा है?
प्रक्रियाओं के बीच एक दौड़ की स्थिति
रेल कॉल after_save
रिकॉर्ड सहेजने के तुरंत बाद कॉलबैक। लेकिन उस रिकॉर्ड को अन्य डेटाबेस कनेक्शन द्वारा नहीं देखा जा सकता है, जैसे कि एक साइडकीक उपयोग कर रहा है, जब तक कि डेटाबेस लेनदेन नहीं हो जाता प्रतिबद्ध है, जो थोड़ी देर बाद होता है। इसका मतलब यह है कि एक मौका है कि साइडकीक आपके प्रश्न को सहेजने के बाद उसे खोजने का प्रयास करेगा, लेकिन इससे पहले कि आप इसे करें। यह आपका रिकॉर्ड नहीं ढूंढ सकता, और यह फट जाता है।
यह समस्या इतनी आम है कि साइडकीक के पास इसके बारे में अक्सर पूछे जाने वाले प्रश्न प्रविष्टि है। और एक आसान समाधान है।
इसके बजाय after_save
:
class Question < ActiveRecord::Base
after_save :index_for_search
# ...
end
after_commit
. का उपयोग करें :
class Question < ActiveRecord::Base
after_commit :index_for_search
# ...
end
और आपकी नौकरी तब तक कतार में नहीं लगेगी जब तक कि साइडकीक आपका मॉडल नहीं देख लेता।
इसलिए, जब आप किसी पृष्ठभूमि कार्य को कतारबद्ध करते हैं या अपने द्वारा किए गए परिवर्तन के बारे में कोई अन्य प्रक्रिया बताते हैं, तो after_commit
का उपयोग करें . अगर आप ऐसा नहीं करते हैं, तो हो सकता है कि वे आपके द्वारा अभी-अभी छुआ गया रिकॉर्ड न ढूंढ पाएं।
लेकिन एक और समस्या है...
ठीक है, आपने अपने 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}
ओह! क्या परीक्षण को कार्य के लिए कतारबद्ध नहीं करना चाहिए था? वहां अभी क्या हुआ?
डिफ़ॉल्ट रूप से, रेल प्रत्येक टेस्ट केस को अपने डेटाबेस लेनदेन में लपेटता है। यह वास्तव में चीजों को गति दे सकता है। परीक्षण के दौरान आपके द्वारा किए गए सभी परिवर्तनों को पूर्ववत करने के लिए केवल एक डेटाबेस कमांड की आवश्यकता होती है।
लेकिन इसका मतलब यह भी है कि आपका after_commit
कॉलबैक नहीं चलेगा। क्योंकि after_commit
कॉलबैक केवल तभी चलते हैं जब सबसे बाहरी लेन-देन किया गया है।
जब आप save
. को कॉल करते हैं एक परीक्षण मामले के अंदर, यह अभी भी एक लेनदेन (अधिक या कम) करता है, लेकिन यह दूसरा-सबसे-बाहरी है अब लेनदेन। तो आपका after_commit
जब आप उनसे अपेक्षा करते हैं तो कॉलबैक नहीं चलेंगे। और आप परीक्षण नहीं कर सकते कि उनके अंदर क्या होता है।
इस समस्या का एक आसान समाधान भी है। test_after_commit
शामिल करें आपके जेमफाइल में मणि:
group :test do
gem "test_after_commit"
end
और आपका after_commit
आपके सेकेंड-टू-लास्ट . के बाद हुक चलेंगे लेन-देन करता है। वही हुआ जो आप होने की उम्मीद कर रहे थे।
आप सोच रहे होंगे, "यह अजीब है। रेल के साथ आने वाले कॉलबैक का परीक्षण करने के लिए मुझे एक अलग मणि का उपयोग क्यों करना है? क्या यह अपने आप नहीं हो जाना चाहिए?"
तुम सही कह रही हो। यह अजीब है। लेकिन यह लंबे समय तक अजीब नहीं रहेगा।
एक बार रेल 5 जहाज, आपको test_after_commit
. के बारे में चिंता करने की आवश्यकता नहीं होगी . क्योंकि इस समस्या को लगभग एक महीने पहले रेल में ठीक कर दिया गया था।
अपने कोड में, मैं after_commit
. का उपयोग करता हूं बहुत। after_save
. की तुलना में शायद मैं इसका अधिक उपयोग करता हूं ! लेकिन यह अपनी समस्याओं और अजीब किनारे के मामलों के बिना नहीं आया है।
संस्करण दर संस्करण, हालांकि, यह बेहतर होता जा रहा है। और जब आप after_commit
. का उपयोग करते हैं सही जगहों पर, बहुत सारे अजीब, यादृच्छिक अपवाद अब और नहीं होंगे।