কম্পিউটার টিউটোরিয়াল

রেইলস ৫-এ সাধারণ কলব্যাক ফাঁদ এবং তাদের সমাধান

ActiveRecord कॉलबैक्स आपके मॉडल के जीवन के विभिन्न चरणों के दौरान कोड चलाने का एक आसान तरीका है।

उदाहरण के लिए, मान लीजिए कि आपके पास एक Q&A साइट है, और आप सभी प्रश्नों के माध्यम से खोज करने में सक्षम होना चाहते हैं। हर बार जब आप एक प्रश्न में बदलाव करते हैं, तो आप इसे ElasticSearch जैसी किसी चीज़ में इंडेक्स करना चाहेंगे। इंडेक्सिंग में समय लगता है और यह जरूरी नहीं है, इसलिए आप इसे Sidekiq के साथ बैकग्राउंड में करेंगे।

यह after_save कॉलबैक का उपयोग करने का सही समय लगता है! तो आपके मॉडल में, आप कुछ ऐसा लिखेंगे:

app/models/question.rb
class Question < ActiveRecord::Base after_save :index_for_search # ... private def index_for_search QuestionIndexerJob.perform_later(self) end end
app/jobs/question_indexer_job.rb
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 उस प्रश्न को क्यों नहीं ढूंढ सकता जिसे आपने अभी सेव किया?

प्रोसेसेस के बीच एक रेस कंडीशन

रेल्स रिकॉर्ड सेव होने के तुरंत बाद after_save कॉलबैक्स को कॉल करता है। लेकिन उस रिकॉर्ड को अन्य डेटाबेस कनेक्शन द्वारा नहीं देखा जा सकता, जैसे कि Sidekiq उपयोग कर रहा है, जब तक कि डेटाबेस ट्रांजैक्शन कमिट नहीं हो जाता, जो थोड़ी देर बाद होता है। इसका मतलब है कि एक मौका है कि Sidekiq आपके प्रश्न को सेव करने के बाद, लेकिन इससे पहले कि आप इसे कमिट करें, खोजने की कोशिश करेगा। यह आपके रिकॉर्ड को नहीं ढूंढ सकता, और यह फट जाता है।

यह समस्या इतनी आम है कि Sidekiq के पास इसके बारे में एक FAQ एंट्री है। और एक आसान फिक्स है।

after_save के बजाय:

app/models/question.rb
class Question < ActiveRecord::Base after_save :index_for_search # ... end

after_commit का उपयोग करें:

app/models/question.rb
class Question < ActiveRecord::Base after_commit :index_for_search # ... end

और आपका जॉब तब तक कतार में नहीं लगेगा जब तक Sidekiq आपका मॉडल नहीं देख सकता।

तो, जब आप एक बैकग्राउंड जॉब कतार में लगाते हैं या किसी अन्य प्रोसेस को आपके द्वारा किए गए बदलाव के बारे में बताते हैं, तो after_commit का उपयोग करें। यदि आप नहीं करते हैं, तो वे उस रिकॉर्ड को नहीं ढूंढ पाएंगे जिसे आपने अभी छुआ है।

लेकिन एक और समस्या है…

ठीक है, आपने अपने कई after_save हुक को after_commit का उपयोग करने के लिए बदल दिया। सब कुछ काम करने लगता है। इसे सब चेक-इन करने और घर जाने का समय, है ना?

पहले, आप अपने टेस्ट चलाना चाहेंगे:

test/models/question_test.rb
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 कॉलबैक्स तब नहीं चलेंगे जब आप उनसे उम्मीद करते हैं। और आप उनमें जो होता है उसे टेस्ट नहीं कर सकते।

इस समस्या का भी एक आसान फिक्स है। अपने Gemfile में test_after_commit जेम शामिल करें:

Gemfile
group :test do gem "test_after_commit" end

और आपके after_commit हुक आपके दूसरे-से-अंतिम ट्रांजैक्शन कमिट होने के बाद चलेंगे। जो कि आप होने की उम्मीद कर रहे थे।

आप सोच रहे होंगे, "यह अजीब है। मुझे रेल्स के साथ आने वाले एक कॉलबैक को टेस्ट करने के लिए एक पूरी अलग जेम का उपयोग क्यों करना पड़ता है? क्या यह स्वचालित रूप से नहीं होना चाहिए?"

आप सही हैं। यह अजीब है। लेकिन यह लंबे समय तक अजीब नहीं रहेगा।

एक बार रेल्स 5 शिप हो जाने पर, आपको test_after_commit के बारे में चिंता करने की ज़रूरत नहीं होगी। क्योंकि इस समस्या को रेल्स में लगभग एक महीने पहले ठीक किया गया था।

अपने कोड में, मैं after_commit का बहुत उपयोग करता हूँ। मैं शायद इसका उपयोग after_save से अधिक करता हूँ! लेकिन यह अपनी समस्याओं और अजीब एज केस के बिना नहीं आया है।

संस्करण दर संस्करण, हालांकि, यह बेहतर हो रहा है। और जब आप after_commit का उपयोग सही स्थानों पर करते हैं, तो बहुत सारे अजीब, रैंडम एक्सेप्शन बस नहीं होंगे।

  1. ActionCable এবং Turbo দিয়ে Rails-এ লাইভ চ্যাট অ্যাপ বিল্ড করুন: ধাপে ধাপে গাইড

  2. Byebug এবং Pow দিয়ে রেলসে রিমোট ডিবাগিং: একটি স্টেপ-বাই-স্টেপ গাইড

  3. Vue, Vuex, এবং Rails দিয়ে ফুল-স্ট্যাক ডেভেলপমেন্ট মাস্টার করুন: একটি স্কেলেবল পদ্ধতি

  4. ক্রোম ও এজে RESULT_CODE_HUNG সমস্যা সমাধান: দ্রুত ফিক্স গাইড