इस श्रृंखला में हमारे पिछले आलेख ने स्थापित किया कि यदि आपको बैकग्राउंड जॉब्स को प्रोसेस करने के लिए एक सिस्टम की आवश्यकता है तो Solid Queue एक उत्कृष्ट विकल्प है। यह बाहरी निर्भरताओं को कम करता है — Redis की कोई आवश्यकता नहीं! — सभी जॉब्स को आपके डेटाबेस में संग्रहीत करके। इसके बावजूद, यह अविश्वसनीय रूप से प्रदर्शनकारी है।
लेकिन सिर्फ प्रदर्शनकारी होना प्रोडक्शन-रेडी बैकग्राउंड जॉब सिस्टम के लिए पर्याप्त नहीं है। Rails डेवलपर्स वर्षों से बहुत उम्मीद करने लगे हैं। हम सिर्फ बैकग्राउंड में चलाने के लिए जॉब्स को एनक्यू नहीं करना चाहते। हम जॉब्स को शेड्यूल करना चाहते हैं, उन्हें एक आवर्ती शेड्यूल पर चलाना चाहते हैं, और हम यह भी सीमित करना चाह सकते हैं कि कितने जॉब्स एक साथ चल सकते हैं। हमें और सुविधाएं चाहिए!
आश्चर्यजनक रूप से, Solid Queue उन सभी सुविधाओं को बॉक्स से बाहर प्रदान करता है। आइए Solid Queue में गहराई से गोता लगाएं और सीखें कि यह कैसे संभव है!
Solid Queue के साथ Ruby on Rails के लिए जॉब्स शेड्यूल करना
सबसे पहले, एक छोटे से पुनर्कथन का समय। Solid Queue आपके डेटाबेस का उपयोग करता है — और केवल आपके डेटाबेस का — जॉब डेटा संग्रहीत करने के लिए। यह जो कुछ भी करता है वह एक डेटाबेस टेबल या किसी अन्य द्वारा समर्थित है। जॉब्स शेड्यूल करना — यानी, जॉब्स को भविष्य में किसी विशिष्ट बिंदु पर चलाने के लिए नामित करना — कोई अलग नहीं है। कोई भी शेड्यूल्ड जॉब solid_queue_scheduled_executions में संग्रहीत होता है।
यह टेबल solid_queue_ready_executions टेबल के लगभग समान है। एकमात्र अंतर scheduled_at कॉलम का जोड़ है, जो हमें बताता है कि एक शेड्यूल्ड जॉब को कब निष्पादित किया जाना चाहिए। आइए पुष्टि करें कि जब हम एक जॉब शेड्यूल करते हैं तो क्या होता है।
वहां कोई आश्चर्य नहीं। Solid Queue solid_queue_scheduled_executions टेबल में एक नई पंक्ति जोड़ता है, जिसमें वह डेटा होता जिसकी हम अपेक्षा करते हैं। लेकिन हम इस तरह के रिकॉर्ड के मौजूद होने से वास्तव में सही समय पर जॉब चलाने तक कैसे जाते हैं?
हमें एक ऐसी प्रक्रिया की आवश्यकता है जो solid_queue_scheduled_executions टेबल को लगातार पोल करती रहे। उस प्रक्रिया को डिस्पैचर कहा जाता है, और यह शेड्यूल्ड जॉब्स को समय पर निष्पादित करने के लिए जिम्मेदार है। यह तब शुरू होता है जब Solid Queue शुरू होता है — किसी अतिरिक्त कॉन्फिगरेशन की आवश्यकता नहीं होती। हालांकि, यदि आवश्यक हो तो आप Solid Queue को एक विशिष्ट कॉन्फिगरेशन के साथ चलाकर केवल डिस्पैचर प्रक्रिया शुरू कर सकते हैं।
यदि आप सोच रहे थे कि डिस्पैचर प्रक्रिया की निगरानी कैसे की जाती है, तो वह उपयुक्त नाम वाले Supervisor की जिम्मेदारी है। यह Solid Queue के भीतर चल रही किसी भी प्रक्रिया को ट्रैक करता है, जिसमें वर्कर प्रक्रियाएं और डिस्पैचर्स शामिल हैं।
तो, डिस्पैचर वास्तव में कैसे काम करता है? यह एक poll मेथड को परिभाषित करता है जिसे शेड्यूल्ड जॉब्स के लिए लगातार जांचने के लिए एक लूप के भीतर बुलाया जाता है। पोलिंग कोड कई क्लासेस और मॉड्यूल्स में फैला हुआ है, लेकिन भारी रूप से सरलीकृत रूप में, यह इस तरह दिखता है:
'रेडी' शेड्यूल्ड एक्जीक्यूशन को पुनः प्राप्त करने के लिए क्वेरी सीधी है।
तो, अतीत में scheduled_at वाला कोई भी शेड्यूल्ड जॉब डिस्पैच होने के लिए तैयार है। जैसा कि हमने इस श्रृंखला के भाग एक में कवर किया था, जब Solid Queue एक जॉब डिस्पैच करता है, तो यह एक ReadyExecution रिकॉर्ड बनाता है और संबंधित ScheduledExecution रिकॉर्ड को नष्ट कर देता है। ReadyExecution रिकॉर्ड को तब नियमित वर्कर प्रक्रियाओं द्वारा उठाया जाता है, और संबंधित जॉब चलता है।
अब तक, अच्छा है। शेड्यूल्ड जॉब्स वास्तव में इतने जटिल नहीं हैं! आइए कुछ अधिक जटिल देखें: आवर्ती कार्य।
आवर्ती कार्य
आवर्ती कार्य बैकग्राउंड जॉब प्रोसेसर्स के लिए एक अक्सर अनुरोधित सुविधा है। सीधे शब्दों में कहें, वे बैकग्राउंड जॉब्स हैं जो एक आवर्ती शेड्यूल पर चलना चाहिए। वे Cron जॉब्स के समान हैं जिसमें आप एक शेड्यूल परिभाषित करते हैं (जैसे हर पांच मिनट, हर दिन दोपहर में, और इसी तरह) जब काम होना चाहिए।
Solid Queue में, आप config/recurring.yml फाइल का उपयोग करके अपने आवर्ती जॉब्स को कॉन्फिगर करते हैं। उदाहरण के लिए, यदि हम हर दिन दोपहर में CleanupData जॉब चलाना चाहते थे, तो हम इसे इस तरह करेंगे।
Solid Queue Fugit का उपयोग शेड्यूल एक्सप्रेशन को पार्स करने के लिए करता है, यही कारण है कि 'हर दिन दोपहर' जैसे मानव-पठनीय शेड्यूल की अनुमति है। शेड्यूल्ड कार्यों का उपयोग करते समय, आप चलाने के लिए जॉब की क्लास और किसी भी जॉब आर्गुमेंट को परिभाषित करते हैं। उत्कृष्ट SolidQueue आवर्ती कार्य ReadMe अधिक विवरण प्रदान करता है। हम यह जानने के लिए यहां हैं कि यह कैसे काम करता है, तो आइए हुड के नीचे देखें।
आवर्ती कार्यों को RecurringTask मॉडल द्वारा दर्शाया जाता है, जो एक संबंधित solid_queue_recurring_tasks टेबल द्वारा समर्थित है। उसमें कॉलम कॉन्फिगरेशन फाइल में उपलब्ध फील्ड्स से मेल खाते हैं।
जब आप SolidQueue शुरू करते हैं, तो आपके आवर्ती कार्यों की कॉन्फिगरेशन फाइल के अनुसार आवर्ती कार्य रिकॉर्ड बनाए जाते हैं। सही समय पर जॉब्स बनाने के लिए, हमें एक बार फिर एक नई प्रक्रिया की आवश्यकता होती है — इस बार जिसे शेड्यूलर कहा जाता है। शेड्यूलर डिस्पैचर का एक सहोदर है, जिसे हम पहले से जानते हैं। यह लगभग उसी तरह काम करता है: Solid Queue शुरू होने पर एक नई प्रक्रिया शुरू की जाती है, और यह प्रक्रिया एक अंतहीन लूप चलाती है। शेड्यूलर और डिस्पैचर के बीच का अंतर उस लूप के भीतर क्या होता है। जहां डिस्पैचर solid_queue_scheduled_executions टेबल को क्वेरी करता है, वहीं शेड्यूलर solid_queue_recurring_tasks को क्वेरी करता है — और सही समय पर जॉब्स शेड्यूल करता है। तो, शेड्यूलर को कैसे पता चलता है कि सही समय क्या है, और सही जॉब्स को कब शेड्यूल करना है?
उस प्रश्न का उत्तर देने के लिए, हमें कार्यान्वयन की बारीकी से जांच करनी होगी। शेड्यूलर क्लास एक नया RecurringSchedule ऑब्जेक्ट बनाता है जो एक schedule मेथड को परिभाषित करता है। उस मेथड को प्रत्येक शेड्यूल्ड कार्य के लिए बार-बार बुलाया जाता है। यहां एक सरलीकृत संस्करण है:
आइए इस कोड को सुलझाएं। Solid Queue Concurrent::ScheduledTask (concurrent-ruby लाइब्रेरी से) का उपयोग एक नया थ्रेड उत्पन्न करने के लिए करता है। उस थ्रेड को आवर्ती कार्य के शेड्यूल द्वारा निर्दिष्ट समय पर चलाने के लिए शेड्यूल किया जाता है। जब वह थ्रेड चलता है, तो यह पहले अगले आवर्ती कार्य को शेड्यूल करने के लिए एक और थ्रेड को रिकर्सिवली उत्पन्न करता है। फिर, यह 'वर्तमान' शेड्यूल्ड जॉब को एनक्यू करता है।
आइए चीजों को समझने के लिए एक सरल आवर्ती कार्य का उदाहरण देखें।
यदि हम Solid Queue को 8:30 पर शुरू करते हैं, तो शेड्यूल मेथड के भीतर वेरिएबल्स को निम्नलिखित मान दिए जाते हैं। शब्दशः नहीं, ध्यान दें। हम यहां बहुत सरल कर रहे हैं।
तो, हमारा बैकग्राउंड थ्रेड अब से तीस मिनट बाद चलने के लिए शेड्यूल किया गया है, जो कि 9:00 है। जब वह समय आता है, तो बैकग्राउंड थ्रेड निष्पादित होता है। यह thread_task.enqueue(at: 9:00) चलाता है — इसलिए CleanupData का एक इंस्टेंस निष्पादन के लिए क्यूड होता है। यह thread_schedule.schedule के माध्यम से खुद को रिकर्सिवली भी बुलाता है। क्योंकि अब 9:00 है, इस आह्वान के लिए वेरिएबल्स बदल गए हैं।
तो, बैकग्राउंड थ्रेड को फिर से 10:00 पर चलाने के लिए शेड्यूल किया गया है, और चक्र जारी रहता है। आप सोच रहे होंगे कि क्या होता है यदि शेड्यूलिंग थ्रेड मारा जाता है, उदाहरण के लिए एक पुनः तैनाती या सिस्टम क्रैश के दौरान। क्या यह आपके शेड्यूल को परेशान करेगा? सौभाग्य से, उत्तर नहीं है। Cron शेड्यूल स्थिर हैं। 'हर घंटे' जैसा एक एक्सप्रेशन हमेशा 10:00, 11:00, 12:00, और इसी तरह हल होता है, भले ही Solid Queue कब शुरू हो। शेड्यूलिंग थ्रेड में किसी भी रुकावट से वह नहीं बदलता।
यहां कुछ अन्य कार्यान्वयन विवरण दिए गए हैं जिनके बारे में पता होना चाहिए। पहला, आवर्ती कार्य के अगले घटना को निष्पादित करने से पहले शेड्यूल करने का यह पैटर्न GoodJob से प्रेरित है। दूसरा, RecurringTask.enqueue एक नया Job और ReadyExecution रिकॉर्ड नहीं बनाता जैसा कि आप उम्मीद कर सकते हैं। इसके बजाय, यह एक और रिकॉर्ड बनाता है, अर्थात् RecurringExecution।
यह रिकॉर्ड पूरी तरह से आवर्ती जॉब्स को कई बार निष्पादित होने से रोकने के लिए है। इसमें task_key और run_at पर एक इंडेक्स है जिसमें अद्वितीय प्रतिबंध हैं जो उस उद्देश्य की पूर्ति करते हैं। एक RecurringTask केवल तभी क्यूड होता है जब उसी समय और उसी जॉब के लिए कोई पूर्व RecurringExecution नहीं होता।
सतर्क पाठक ध्यान देंगे कि यह कोड स्निपेट Solid Queue में एक सीमा की ओर इशारा करता है। अर्थात्, यदि आप अपने cron-स्टाइल कार्यों को चलाने के लिए बैकएंड के रूप में Solid Queue का उपयोग नहीं कर रहे हैं — हां, आप ऐसा कर सकते हैं — Solid Queue यह गारंटी नहीं दे सकता कि आवर्ती जॉब्स केवल एक बार एनक्यूड हैं। यदि आप खुद को ऐसी स्थिति में पाते हैं, तो आपको इसके बारे में पता होना चाहिए।
आप यह भी सोच रहे होंगे कि क्या होता है यदि शेड्यूलर प्रक्रिया मर जाती है या मारी जाती है — उदाहरण के लिए, एक तैनाती के दौरान। चूंकि पुनरावृत्तियों को एक थ्रेड द्वारा प्रबंधित किया जाता है, क्या थ्रेड को मारना शेड्यूल को तोड़ देगा? सौभाग्य से, उस प्रश्न का उत्तर भी नहीं है।
कनकरेंसी कंट्रोल्स
आइए Solid Queue की एक अंतिम सुविधा देखें, अर्थात् कनकरेंसी कंट्रोल्स। कभी-कभी, आप सीमित करना चाहते हैं कि एक निश्चित प्रकार के कितने जॉब्स एक साथ चल सकते हैं। आप Solid Queue के साथ limits_concurrency का उपयोग करके ऐसा कर सकते हैं।
यहां, हम SolidQueue को बता रहे हैं कि प्रत्येक उपयोगकर्ता के लिए MyJob का अधिकतम एक इंस्टेंस चलाएं। आइए कॉन्फिगरेशन को अधिक विस्तार से देखें।
to: आप जितने जॉब्स को एक साथ चलाना चाहते हैं उनकी अधिकतम संख्या।key: एक आवश्यक आर्गुमेंट यह नामित करने के लिए कि किन जॉब्स को एक साथ सीमित किया जाना चाहिए। हमारे उदाहरण में, एक ही यूजर आईडी वाले जॉब्स एक साथ एक ही निष्पादन तक सीमित हैं। आपkeyके रूप में किसी भी जॉब आर्गुमेंट का उपयोग कर सकते हैं, लेकिन स्ट्रिंग्स या सिंबल जैसे कॉन्स्टेंट्स भी अनुमत हैं।duration: वह अधिकतम समय जिसके लिए Solid Queue एक जॉब के एनक्यूड होने के बाद कनकरेंसी की गारंटी दे सकता है। यदि आपके जॉब्स उससे अधिक समय तक चलते हैं, तो कनकरेंसी कंट्रोल लागू नहीं होंगे और जॉब्स ओवरलैप हो सकते हैं। हम बाद में सीखेंगे कि क्यों!group: आप इस विकल्प का उपयोग विभिन्न जॉब क्लासेस में कनकरेंसी को सीमित करने के लिए कर सकते हैं।
यदि आप अधिक सीखना चाहते हैं, तो मैं आपको कनकरेंसी कंट्रोल डॉक्यूमेंटेशन का संदर्भ देता हूं। कनकरेंसी कंट्रोल्स आसानी से Solid Queue की सबसे परिष्कृत सुविधा हैं। यदि शेड्यूल्ड कार्यों ने पहले से ही आपका सिर नहीं घुमाया, तो यह सीखना कि यह सुविधा कैसे काम करती है, निश्चित रूप से घुमा देगी।
आइए बुनियादी बातों से शुरू करें। Solid Queue की अन्य सुविधाओं की तरह, कनकरेंसी कंट्रोल्स विभिन्न मॉडल्स और उनके संबंधित डेटाबेस टेबल्स द्वारा समर्थित हैं। दो जिनके बारे में आपको विशेष रूप से पता होना चाहिए वे हैं Semaphore और BlockedExecution।
आइए पहले Semaphore को देखें। जैसा कि नाम से पता चलता है, यह काउंटिंग सेमाफोर पैटर्न का एक कार्यान्वयन है। जब भी Solid Queue limits_concurrency के साथ एक जॉब एनक्यू करता है, तो यह पहले एक कनकरेंसी की के आधार पर सेमाफोर लॉक प्राप्त करने का प्रयास करता है। यह कनकरेंसी की limits_concurrency को पास किए गए आर्गुमेंट्स पर आधारित है, अर्थात् जॉब क्लास, की, और ग्रुप नाम — यदि कोई प्रदान किया गया था। यदि सेमाफोर उपलब्ध है, तो जॉब एनक्यूड होता है। यदि नहीं, तो एक BlockedExecution रिकॉर्ड बनाया जाता है।
सेमाफोर में एक value होता है जो कई समवर्ती जॉब्स का समर्थन करता है। आप इसे सेमाफोर की शेष क्षमता के रूप में सोच सकते हैं। सेमाफोर प्राप्त करने का अर्थ है उस मान को कम करना, और इसे जारी करने का अर्थ है उसे बढ़ाना। एक सेमाफोर को अनुपलब्ध माना जाता है एक बार जब उसका मान शून्य हो जाता है। आइए एक सरल जॉब के लिए लॉकिंग तंत्र कैसे काम करता है इसका उदाहरण देखें।
आइए देखें कि क्या होता है यदि हम इस जॉब को लगातार कई बार एनक्यू करने का प्रयास करते हैं।
MyJobका पहला इंस्टेंस एनक्यूड होता है। अभी तक कोई सेमाफोर नहीं है, इसलिए एक बनाया जाता है। इसका प्रारंभिक मानlimit - 1है। क्योंकि आपकी सीमा तीन है, सेमाफोर का प्रारंभिक मान दो है।MyJobका दूसरा इंस्टेंस एनक्यूड होता है। Solid Queue उस जॉब के लिए लॉक प्राप्त करने का प्रयास करता है। जॉब को एनक्यूड किया जा सकता है क्योंकि मान दो है, जो शून्य से अधिक है। सेमाफोर का मान घटकर एक हो जाता है।- हमारे जॉब का तीसरा इंस्टेंस एनक्यूड होता है। हम वही प्रक्रिया दोहराते हैं। सेमाफोर का मान अब शून्य है।
MyJobका चौथा इंस्टेंस एनक्यूड होता है। सेमाफोर प्राप्त करना विफल होता है क्योंकि इसका मान अब शून्य है। जॉब के लिए एकBlockedExecutionरिकॉर्ड बनाया जाता है।- हमारे जॉब का पहला इंस्टेंस समाप्त होता है। जब यह समाप्त होता है, तो यह सेमाफोर को जारी करता है, इसलिए सेमाफोर का मान एक बार फिर एक हो जाता है।
- समाप्त होने पर, पहला जॉब इंस्टेंस किसी भी ब्लॉक्ड जॉब को जारी करने के लिए एक मेथड भी बुलाता है।
MyJobका चौथा इंस्टेंस जारी होता है और फिर से लॉक प्राप्त करने का प्रयास करता है। सेमाफोर का मान एक है, इसलिए लॉक प्राप्त किया जा सकता है और ब्लॉक्ड जॉब क्यूड होता है। सेमाफोर का मान अब शून्य है।
जॉब समाप्त होने पर सेमाफोर को जारी करने का कोड सीधा है।
एक और विवरण है जिसे हमने अभी तक नहीं छुआ है। सेमाफोर की समाप्ति तिथि क्यों होती है, और limits_concurrency का उपयोग करते समय हमें अवधि निर्धारित करने की आवश्यकता क्यों होती है?
विचार करें कि क्या होता है जब एक जॉब अपने सेमाफोर को जारी किए बिना क्रैश हो जाता है — उदाहरण के लिए, जब उस जॉब को प्रोसेस करने वाला वर्कर मर जाता है। जब तक हम सेमाफोर को साफ करने के लिए कुछ तंत्र नहीं जोड़ते, उस जॉब द्वारा रखा गया लॉक हमेशा के लिए रखा जाएगा। सबसे खराब स्थिति में, यह हमेशा के लिए अन्य जॉब्स को प्रोसेस होने से रोक देगा।
सेमाफोर की एक समाप्ति होती है जो जॉब परिभाषा में दी गई अवधि से मेल खाती है ताकि उस स्थिति से बचा जा सके। यदि एक सेमाफोर समाप्त हो जाता है — जो तब होता है जब कोई जॉब एनक्यूड नहीं होते — सेमाफोर नष्ट हो जाएगा। हम पहले से ही उस प्रक्रिया को जानते हैं जो उसके लिए जिम्मेदार है — यह हमारा दोस्त, डिस्पैचर है। यह ConcurrencyMaintenance क्लास को इंस्टेंटिएट करता है, जो दो चीजें करता है:
- पहला, यह किसी भी समाप्त सेमाफोर को हटा देता है।
- दूसरा, यह जांचेगा कि क्या कोई ब्लॉक्ड जॉब हैं और उन्हें जारी करेगा।
जॉब्स को एक-एक करके जारी किया जाता है, इसलिए कनकरेंसी सीमाएं अभी भी लागू रहेंगी। हालांकि, विचार करें कि क्या होता है यदि आपका जॉब दी गई अवधि से अधिक समय तक चलता है। उस स्थिति में, सेमाफोर को साफ कर दिया जाएगा, हालांकि जॉब अभी भी चलेगा। यदि फिर कोई अन्य जॉब एनक्यूड होता है, तो वे जॉब्स ओवरलैप होंगे।
AppSignal के साथ Rails के लिए Solid Queue की निगरानी
जैसा कि हमने स्थापित किया है, Solid Queue बहुत कुछ कर सकता है। हालांकि, इन सभी चलते हुए हिस्सों के साथ, निगरानी महत्वपूर्ण हो जाती है। सौभाग्य से, AppSignal Solid Queue के लिए बिल्ट-इन सपोर्ट प्रदान करता है, जॉब निष्पादन समय, थ्रूपुट, और विफलता दरों के लिए रेडी-मेड डैशबोर्ड के साथ। बस अपने Rails एप्लिकेशन में AppSignal इंस्टॉल करें, और आप जाने के लिए तैयार हैं।
AppSignal स्वचालित रूप से आपके Solid Queue के उपयोग का पता लगाएगा और एक एक्टिव जॉब डैशबोर्ड बनाएगा जिसमें महत्वपूर्ण मेट्रिक्स के लिए ग्राफ होंगे, जैसे त्रुटि दर और थ्रूपुट।

यदि आप कभी भी ऐसे जॉब्स देखते हैं जो दुर्व्यवहार करते हैं — या तो क्योंकि वे धीमे चलते हैं या उनमें बहुत अधिक त्रुटियां हैं — उन्हें प्रभावी ढंग से हल करने के लिए एक स्थिति और असाइनी असाइन करें।

जाहिर है, आपको यह पता लगाने के लिए पूरे दिन डैशबोर्ड देखने की आवश्यकता नहीं होनी चाहिए कि क्या कोई समस्या है। AppSignal Alerts आपकी पीठ थपथपाता है। बस जॉब मेट्रिक्स के लिए एक नया अलर्ट बनाएं, जैसे विफलता दर और जॉब अवधि, और आप पूरी तरह तैयार हैं।

Solid Queue आपके एप्लिकेशन में परेशानी के बिना शक्तिशाली जॉब प्रोसेसिंग जोड़ने के लिए अद्भुत है। AppSignal निगरानी के मामले में भी ऐसा ही करता है!
समापन
हमने Solid Queue की उन्नत सुविधाओं की खोज में बहुत सारी जमीन कवर की है। शेड्यूल्ड जॉब्स से लेकर जटिल निर्भरता श्रृंखलाओं तक, प्रत्येक सुविधा उस ठोस नींव पर निर्माण करती है जिस पर हमने भाग एक में चर्चा की थी। जैसा कि हमने देखा, एक जॉब प्रोसेसिंग बैकएंड बनाना आसान नहीं है। लेकिन Solid Queue के सोर्स कोड और इसके कामकाज में गहराई से गोता लगाकर, हमने इसमें शामिल चुनौतियों के लिए एक समझ और कुछ सराहना प्राप्त की है।
किसी भी मामले में, Solid Queue अपने उत्कृष्ट डेटाबेस डिजाइन और प्रक्रिया समन्वय के कारण Rails इकोसिस्टम के लिए एक अद्भुत अतिरिक्त है। यह उन उपकरणों को प्रदान करता है जिनकी आपको आवश्यकता है जबकि अपने मूल वादे को बनाए रखता है: बाहरी निर्भरताओं के बिना सरलता और विश्वसनीयता।
हैप्पी कोडिंग!