संभावना है, यदि आप रूबी कोड लिख रहे हैं, तो आप पृष्ठभूमि प्रसंस्करण को संभालने के लिए साइडकीक का उपयोग कर रहे हैं। अगर आप ActiveJob
. से आ रहे हैं या कुछ अन्य पृष्ठभूमि, देखते रहें, कवर की गई कुछ युक्तियों को वहां भी लागू किया जा सकता है।
लोग विभिन्न मामलों के लिए पृष्ठभूमि नौकरियों (साइडकीक) का उपयोग करते हैं। कुछ क्रंचनंबर, कुछ प्रेषण उपयोगकर्ताओं के लिए स्वागत ईमेल, और कुछ शेड्यूल डेटासिंकिंग। आपका मामला जो भी हो, आप अंततः डुप्लिकेट नौकरियों से बचने के लिए एक आवश्यकता में भाग ले सकते हैं। डुप्लिकेट नौकरियों से, मैं दो नौकरियों की कल्पना करता हूं जो एक ही काम करते हैं। आइए उस पर थोड़ा ध्यान दें।
डी-डुप्लीकेट नौकरियां क्यों?
एक ऐसे परिदृश्य की कल्पना करें जहां आपकी नौकरी निम्न की तरह दिखे:
class BookSalesWorker
include Sidekiq::Worker
def perform(book_id)
crunch_some_numbers(book_id)
upload_to_s3
end
...
end
BookSalesWorker
हमेशा एक ही काम करता है — book_id
. पर आधारित पुस्तक के लिए DB से पूछताछ करता है और कुछ नंबरों की गणना करने के लिए नवीनतम बिक्री डेटा प्राप्त करता है। फिर, यह उन्हें एक संग्रहण सेवा में अपलोड करता है। ध्यान रखें कि जब भी आपकी वेबसाइट पर कोई पुस्तक बेची जाती है, तो आपके पास यह कार्य पंक्तिबद्ध हो जाएगा।
अब, अगर आपको एक बार में 100 बिक्री मिल जाए तो क्या होगा? आपके पास इनमें से 100 कार्य ठीक वही काम कर रहे होंगे। शायद आप इसके साथ ठीक हैं। आपको S3 लिखने की इतनी परवाह नहीं है, और आपकी कतारें इतनी भीड़भाड़ वाली नहीं हैं, इसलिए आप लोड को संभाल सकते हैं। लेकिन, "क्या यह स्केल करता है?"™️
खैर, निश्चित रूप से नहीं। यदि आप अधिक पुस्तकों के लिए अधिक बिक्री प्राप्त करना शुरू करते हैं, तो आपकी कतार जल्दी से अनावश्यक काम के साथ ढेर हो जाएगी। अगर आपके पास 100 नौकरियां हैं जो एक ही किताब के लिए समान काम करती हैं, और आपके पास समानांतर में बिकने वाली 10 किताबें हैं, तो अब आप अपनी कतार में 1000 नौकरियां गहरे हैं, जहां वास्तव में, आपके पास प्रत्येक पुस्तक के लिए केवल 10 नौकरियां हो सकती हैं।
अब, आइए कुछ विकल्पों पर गौर करें कि कैसे आप डुप्लिकेट नौकरियों को अपनी कतारों में जमा होने से रोक सकते हैं।
<एच2>1. DIY तरीकायदि आप बाहरी निर्भरता और जटिल तर्क के प्रशंसक नहीं हैं, तो आप आगे बढ़ सकते हैं और अपने कोडबेस में कुछ कस्टम समाधान जोड़ सकते हैं। मैंने अपने उदाहरणों को पहली बार आज़माने के लिए नमूना रेपो बनाया। उदाहरण के लिए प्रत्येक दृष्टिकोण में एक लिंक होगा।
1.1 एक फ़्लैग दृष्टिकोण
आप एक ध्वज जोड़ सकते हैं जो यह तय करता है कि किसी कार्य को कतार में लगाना है या नहीं। कोई sales_enqueued_at
जोड़ सकता है उनकी पुस्तक तालिका में और उसे बनाए रखें। उदाहरण के लिए:
module BookSalesService
def schedule_with_one_flag(book)
# Check if the job was enqueued more than 10 minutes ago
if book.sales_enqueued_at < 10.minutes.ago
book.update(sales_enqueued_at: Time.current)
BookSalesWorker.perform_async(book.id)
end
end
end
इसका मतलब है कि कोई भी नया कार्य तब तक नहीं किया जाएगा जब तक कि अंतिम कार्य की कतार लगने के 10 मिनट नहीं हो जाते। 10 मिनट बीत जाने के बाद, हम फिर sales_enqueued_at
. को अपडेट करते हैं और एक नया काम शुरू करें।
एक और चीज जो आप कर सकते हैं वह है एक ध्वज सेट करना जो एक बूलियन है, उदा.crunching_sales
. आपने crunching_sales
. सेट किया है पहली नौकरी से पहले सच करने के लिए कतारबद्ध। फिर, काम पूरा होने के बाद, आप इसे गलत पर सेट करते हैं। अन्य सभी कार्य जो शेड्यूल करने का प्रयास करते हैं उन्हें crunching_sales
. तक अस्वीकार कर दिया जाएगा झूठा है।
आप इस दृष्टिकोण को रेपो द्वारा बनाए गए उदाहरण में आजमा सकते हैं।
1.2 दो फ़्लैग दृष्टिकोण
यदि किसी कार्य को 10 मिनट के लिए कतार में लगने से "लॉक करना" बहुत डरावना लगता है, लेकिन आप अभी भी अपने कोड में अतिरिक्त फ़्लैग के साथ ठीक हैं, तो अगला सुझाव आपको रुचिकर लग सकता है।
आप मौजूदा sales_enqueued_at
. में एक और फ़्लैग जोड़ सकते हैं — sales_calculated_at
तब हमारा कोड कुछ इस तरह दिखेगा:
module BookSalesService
def schedule_with_two_flags(book)
# Check if sales are being calculated right now
if book.sales_enqueued_at <= book.sales_calculated_at
book.update(sales_enqueued_at: Time.current)
BookSalesWorker.perform_async(book.id)
end
end
end
class BookSalesWorker
include Sidekiq::Worker
def perform(book_id)
crunch_some_numbers(book_id)
upload_to_s3
# New adition
book.update(sales_calculated_at: Time.current)
end
...
end
इसे आज़माने के लिए, उदाहरण रेपो में दिए गए निर्देशों को देखें।
अब हम उस समय के एक हिस्से को नियंत्रित करते हैं जब कोई कार्य कतारबद्ध और समाप्त हो जाता है। समय के उस हिस्से में, कोई नौकरी नहीं लग सकती है। जब कार्य चल रहा हो, sales_enqueued_at
sales_calculated_at
. से बड़ा होगा . जब जॉब खत्म हो जाए, तो sales_calculated_at
sales_enqueued_at
. से बड़ा (हाल ही में) होगा और एक नई नौकरी की कतार लगेगी।
दो फ़्लैग का उपयोग करना दिलचस्प हो सकता है, इसलिए आप पिछली बार दिखा सकते हैं कि वे बिक्री नंबर UI में अपडेट हुए थे। फिर जो उपयोगकर्ता उन्हें पढ़ते हैं, उन्हें अंदाजा हो सकता है कि डेटा कितना हाल का है। एक जीत की स्थिति।
सम अप फ़्लैग करें
जरूरत के समय में इस तरह के समाधान बनाना लुभावना हो सकता है, लेकिन मेरे लिए, वे थोड़े अनाड़ी लगते हैं, और वे कुछ ओवरहेड जोड़ते हैं। यदि आपका उपयोग मामला सरल है, तो मैं इसका उपयोग करने की सलाह दूंगा, लेकिन जैसे ही यह जटिल या पर्याप्त साबित होता है, मैं आपसे अन्य विकल्पों को आज़माने का आग्रह करूंगा।
ध्वज दृष्टिकोण के साथ एक बड़ा नुकसान यह है कि आप उन सभी नौकरियों को खो देंगे, जिन्होंने उन 10 मिनटों के दौरान कतारबद्ध करने का प्रयास किया था। एक बड़ा समर्थक यह है कि आप निर्भरता में नहीं ला रहे हैं, और यह कतार में नौकरी की संख्या को बहुत जल्दी कम कर देगा।
1.3 कतार को पार करना
एक और तरीका जो आप अपना सकते हैं, वह है एक ही जॉब को कतार में लगने से रोकने के लिए एक कस्टम लॉकिंग मैकेनिज्म बनाना। हम रुचि रखने वाले साइडकीक कतार की जांच करेंगे और देखेंगे कि क्या नौकरी (कार्यकर्ता) पहले से ही है। कोड कुछ इस तरह दिखेगा:
module BookSalesService
def schedule_unique_across_queue(book)
queue = Sidekiq::Queue.new('default')
queue.each do |job|
return if job.klass == BookSalesWorker.to_s &&
job.args == [book.id]
end
BookSalesWorker.perform_async(book.id)
end
end
class BookSalesWorker
include Sidekiq::Worker
def perform(book_id)
crunch_some_numbers(book_id)
upload_to_s3
end
...
end
ऊपर के उदाहरण में हम जाँच कर रहे हैं कि क्या 'default'
क्यू में BookSalesWorker
. के वर्ग नाम के साथ एक कार्य है . हम यह भी जांच रहे हैं कि क्या नौकरी के तर्क बुक आईडी से मेल खाते हैं। अगर BookSalesWorker
एक ही बुक आईडी के साथ जॉब कतार में है, हम जल्दी लौटेंगे और दूसरा शेड्यूल नहीं करेंगे।
ध्यान दें कि यदि आप बहुत तेज़ी से कार्य शेड्यूल करते हैं, तो उनमें से कुछ शेड्यूल हो सकते हैं क्योंकि कतार खाली है। इसके साथ स्थानीय रूप से परीक्षण करते समय मेरे साथ ठीक यही हुआ:
100.times { BookSalesService.schedule_unique_across_queue(book) }
आप इसे उदाहरण रेपो में आज़मा सकते हैं।
इस दृष्टिकोण के बारे में अच्छी बात यह है कि आप किसी मौजूदा नौकरी की तलाश के लिए सभी कतारों को पार कर सकते हैं यदि आपको इसकी आवश्यकता है। नुकसान यह है कि यदि आपकी कतार खाली है और आप उनमें से बहुतों को एक साथ शेड्यूल करते हैं, तो भी आपके पास डुप्लिकेट कार्य हो सकते हैं। साथ ही, आप संभावित रूप से एक शेड्यूल करने से पहले कतार में सभी नौकरियों के माध्यम से आगे बढ़ रहे हैं, इसलिए यह आकार के आधार पर महंगा हो सकता है आपकी कतार।
2. Sidekiq Enterprise में अपग्रेड करना
यदि आपके या आपके संगठन के पास कुछ पैसा पड़ा है, तो आप साइडकीक के एंटरप्राइज संस्करण में अपग्रेड कर सकते हैं। यह $ 179 प्रति माह से शुरू होता है, और इसमें एक शानदार विशेषता है जो आपको डुप्लिकेट नौकरियों से बचने में मदद करती है। दुर्भाग्य से, मेरे पास SidekiqEnterprise नहीं है, लेकिन मेरा मानना है कि उनका दस्तावेज़ीकरण पर्याप्त है। आप निम्न कोड के साथ आसानी से अद्वितीय (गैर-डुप्लिकेट) कार्य प्राप्त कर सकते हैं:
class BookSalesWorker
include Sidekiq::Worker
sidekiq_options unique_for: 10.minutes
def perform(book_id)
crunch_some_numbers(book_id)
upload_to_s3
end
...
end
और बस। आपके पास एक समान कार्य कार्यान्वयन है जिसे हमने 'एक ध्वज दृष्टिकोण' अनुभाग में वर्णित किया है। कार्य 10 मिनट के लिए अद्वितीय होगा, जिसका अर्थ है कि समान तर्क के साथ कोई अन्य कार्य उस समयावधि में निर्धारित नहीं किया जा सकता है।
बहुत अच्छा वन-लाइनर, हुह? ठीक है, अगर आपके पास एंटरप्राइज़ साइडकीक है और आपको इस सुविधा के बारे में पता चला है, तो मुझे वास्तव में खुशी है कि मैंने मदद की। हम में से अधिकांश लोग इसका उपयोग नहीं करने जा रहे हैं, तो चलिए अगले समाधान पर चलते हैं।
3. बचाव के लिए साइडकीक-अद्वितीय-जॉब्स
हां, मुझे पता है कि हम एक रत्न का जिक्र करने वाले हैं। और हाँ, इसमें कुछ लुआ फाइलें हैं जो कुछ लोगों को बंद कर सकती हैं। लेकिन मेरे साथ रहो, यह वास्तव में एक प्यारा सौदा है जो आपको मिल रहा है। साइडकीक-अद्वितीय-जॉबजेम बहुत सारे लॉकिंग और अन्य कॉन्फ़िगरेशन विकल्पों के साथ आता है - शायद आपकी आवश्यकता से अधिक।
जल्दी से शुरू करने के लिए, sidekiq-unique-jobs
put डालें अपने Gemfile में मणि, bundle
करें और दिखाए गए अनुसार अपने कार्यकर्ता को कॉन्फ़िगर करें:
class UniqueBookSalesWorker
include Sidekiq::Worker
sidekiq_options lock: :until_executed,
on_conflict: :reject
def perform(book_id)
book = Book.find(book_id)
logger.info "I am a Sidekiq Book Sales worker - I started"
sleep 2
logger.info "I am a Sidekiq Book Sales worker - I finished"
book.update(sales_calculated_at: Time.current)
book.update(crunching_sales: false)
end
end
बहुत सारे विकल्प हैं, लेकिन मैंने इसे सरल बनाने और इसका उपयोग करने का निर्णय लिया:
sidekiq_options lock: :until_executed, on_conflict: :reject
lock: :until_executed
पहले UniqueBookSalesWorker
को लॉक कर देगा कार्य निष्पादित होने तक। on_conflict: :reject
. के साथ , हम कह रहे हैं कि हम अन्य सभी नौकरियों को चाहते हैं जो मृत कतार में खारिज होने के लिए निष्पादित करने का प्रयास करें। यहां हमने जो हासिल किया वह वैसा ही है जैसा हमने ऊपर दिए गए विषयों में अपने DIY उदाहरणों में किया था।
उन DIY उदाहरणों में थोड़ा सुधार यह है कि हमारे पास एक प्रकार का लोगो है जो हुआ। यह कैसा दिखता है, इसे समझने के लिए, आइए निम्नलिखित प्रयास करें:
5.times { UniqueBookSalesWorker.perform_async(Book.last.id) }
केवल एक कार्य पूरी तरह से निष्पादित होगा, और अन्य चार कार्यों को मृत कतार में भेज दिया जाएगा, जहां आप उन्हें पुनः प्रयास कर सकते हैं। यह तरीका हमारे उन उदाहरणों से अलग है जहां डुप्लीकेट नौकरियों को अभी-अभी नज़रअंदाज़ किया गया था।
जब लॉकिंग और संघर्ष समाधान की बात आती है, तो चुनने के लिए बहुत सारे विकल्प होते हैं। मेरा सुझाव है कि आप अपने विशिष्ट उपयोग के मामले के लिए रत्न के दस्तावेज़ देखें।
महान अंतर्दृष्टि
इस रत्न के बारे में सबसे अच्छी बात यह है कि आप ताले और इतिहास देख सकते हैं कि आपकी कतार में क्या गिरावट आई है। आपको बस अपने config/routes.rb
में निम्नलिखित पंक्तियों को जोड़ना है। :
# config/routes.rb
require 'sidekiq_unique_jobs/web'
Rails.application.routes.draw do
mount Sidekiq::Web, at: '/sidekiq'
end
इसमें मूल साइडकीक क्लाइंट शामिल होगा, लेकिन यह आपको दो और पेज भी देगा - एक जॉब लॉक के लिए और दूसरा चेंजलॉग के लिए। यह इस तरह दिखता है:
ध्यान दें कि हमारे पास दो नए पृष्ठ कैसे हैं, "लॉक" और "चेंजलॉग"। बहुत बढ़िया फीचर।
आप यह सब उदाहरण प्रोजेक्ट में आजमा सकते हैं जहां मणि स्थापित है और जाने के लिए तैयार है।
लुआ क्यों?
सबसे पहले, मैं मणि का लेखक नहीं हूं, इसलिए मैं यहां चीजें मान रहा हूं। पहली बार जब मैंने मणि देखी, तो मैंने सोचा:रूबी मणि के अंदर लुआ का उपयोग क्यों करें? यह पहली बार में अजीब लग सकता है, लेकिन रेडिस लुआ स्क्रिप्ट चलाने का समर्थन करता है। मुझे लगता है कि मणि के लेखक के मन में यह बात थी और वह लुआ में अधिक फुर्तीला तर्क करना चाहता था।
यदि आप मणि के रेपो में लुआ फाइलों को देखते हैं, तो वे जटिल नहीं हैं। सभी Lua लिपियों को बाद में SidekiqUniqueJobs::Script::Caller
में रूबी कोड से कॉल किया जाता है। यहाँ। कृपया स्रोत कोड पर एक नज़र डालें, यह पढ़ना और यह पता लगाना दिलचस्प है कि चीजें कैसे काम करती हैं।
वैकल्पिक रत्न
यदि आप ActiveJob
. का उपयोग करते हैं व्यापक रूप से, आप active-job-uniqueness
को आजमा सकते हैं मणि यहीं है। विचार समान है, लेकिन कस्टम लुआ स्क्रिप्ट के बजाय, यह रेडिस में आइटम को लॉक करने के लिए [रेडलॉक] का उपयोग करता है।
इस रत्न का उपयोग करके एक अद्वितीय कार्य करने के लिए, आप इस तरह की नौकरी की कल्पना कर सकते हैं:
class BookSalesJob < ActiveJob::Base
unique :until_executed
def perform
...
end
end
सिंटैक्स कम क्रियात्मक है लेकिन sidekiq-unique-jobs
. के समान है रत्न यदि आप ActiveJob
. पर अत्यधिक भरोसा करते हैं तो यह आपके मामले का समाधान कर सकता है ।
अंतिम विचार
मुझे आशा है कि आपने अपने ऐप में डुप्लिकेट नौकरियों से निपटने के तरीके के बारे में कुछ ज्ञान प्राप्त किया है। मुझे निश्चित रूप से विभिन्न समाधानों के साथ शोध करने और खेलने में मज़ा आया। अगर आपको वह नहीं मिला जिसकी आप तलाश कर रहे थे, तो मुझे आशा है कि कुछ उदाहरणों ने आपको अपना खुद का कुछ बनाने के लिए प्रेरित किया।
यहां सभी कोड स्निपेट के साथ प्रोजेक्ट का उदाहरण दिया गया है।
मैं आपको अगले एक में देखूंगा, चीयर्स।
पी.एस. यदि आप रूबी मैजिक की पोस्ट प्रेस से बाहर होते ही पढ़ना चाहते हैं, तो हमारे रूबी मैजिक न्यूजलेटर की सदस्यता लें और एक भी पोस्ट मिस न करें!