आम तौर पर, ग्लोबल वेरिएबल्स खराब होते हैं। लेकिन कभी-कभी, सही जगह पर एक ग्लोबल आपके कोड को बहुत सरल बना सकता है।
रेल्स में, आप एक रिक्वेस्ट के दौरान एक बार डेटा सेट कर सकते हैं, लेकिन अपने ऐप की हर लेयर में इसका उपयोग कर सकते हैं। कौन सा यूजर रिक्वेस्ट कर रहा है? उनके पास क्या परमिशन हैं? किस डेटाबेस कनेक्शन का उपयोग किया जाना चाहिए?
यह जानकारी आपके पूरे कोड में उपलब्ध होनी चाहिए, इसलिए इसे हर जगह पास करना सिर्फ शोर का एक गुच्छा होगा। लेकिन एक रूबी ग्लोबल वेरिएबल का उपयोग करना, या क्लास वेरिएबल सेट करना, अपनी समस्याएं पैदा करेगा। कई थ्रेड्स इसे ओवरराइट कर सकते हैं, और आप एक बड़ी गड़बड़ी के साथ समाप्त होंगे, और शायद अप्राप्य रूप से खराब डेटा।
आपको कुछ ऐसा चाहिए जो ग्लोबल हो, लेकिन सिर्फ आपकी रिक्वेस्ट के लिए।
सादा रूबी तरीका
आमतौर पर, आप इसे Thread.current[] के साथ होते हुए देखेंगे। यह इस तरह दिखता है:
Thread.current[:current_user] = user
यह काफी सरल है। यह एक अच्छा समाधान है। लेकिन इसकी दो बड़ी कमियां हैं:
-
कोई गलती से आपके डेटा को ओवरराइट कर सकता है।
क्या होगा अगर दो लोगों ने एक ही कुंजी चुनी? आप एक-दूसरे के डेटा पर रौंद देंगे। और अगर आप यूजर्स जैसी कोई चीज स्टोर कर रहे हैं, तो यह गंभीर रूप से खराब होगा। आपके अपने ऐप्स में, यह शायद कोई समस्या नहीं होगी। लेकिन अगर आप जेम्स लिख रहे हैं, या आपका रेल्स ऐप बड़ा और गड़बड़ है, तो यह कुछ ऐसा है जिसके बारे में आपको सोचने की ज़रूरत है।
-
यह अच्छी तरह से संरचित नहीं है।
Thread.current[]सिर्फ डेटा का एक बड़ा बैग है। आप इसे आसानी से दस्तावेज़ नहीं कर सकते। यदि आप जानना चाहते हैं कि आप इससे क्या निकाल सकते हैं, तो आपको यह खोजना होगा कि आपने इसमें क्या डाला।बेशक, यदि आप इतना ग्लोबल डेटा डाल रहे हैं कि यह एक समस्या है, तो आपके पास
Thread.current[]की संरचना की कमी के अलावा चिंता करने के लिए और भी बहुत कुछ है। लेकिन यह ध्यान में रखने योग्य एक बिंदु है।
तो, क्या Thread.current[] से बेहतर कोई समाधान है? जैसा कि आप कल्पना कर सकते हैं, रेल्स खुद बहुत सारे रिक्वेस्ट-लोकल डेटा को मैनेज करता है। और गुडीज़ का जो बॉक्स एक्टिवसपोर्ट है, उसके पास फॉलो करने के लिए एक अलग पैटर्न है।
रेल्स तरीका
यदि आप एक्टिवसपोर्ट के माध्यम से खुदाई करते हैं, तो आप ActiveSupport::PerThreadRegistry में आ सकते हैं। आप शायद नाम से बता सकते हैं कि यह वही है जिसकी हम तलाश कर रहे हैं।
ActiveSupport::PerThreadRegistry के साथ, पहले का उदाहरण इस तरह दिखेगा:
class RequestRegistry
extend ActiveSupport::PerThreadRegistry
attr_accessor :current_user, :current_permissions
end
RequestRegistry.current_user = user
RequestRegistry.current_user # => user
यह थोड़ा सा अधिक काम है। लेकिन ActiveSupport::PerThreadRegistry के साथ:
-
आपके पास डॉक्यूमेंटेशन रखने की जगह है। आपकी क्लास को देखने वाला कोई भी व्यक्ति ठीक-ठीक जान जाएगा कि आप किस ग्लोबल डेटा की उम्मीद कर रहे हैं, और वे किस ग्लोबल डेटा का उपयोग कर सकते हैं।
-
आपको एक API मिलता है जो अच्छा दिखता है। वास्तव में, यह ऐसा दिखता है जैसे आप सिर्फ क्लास वेरिएबल सेट कर रहे हैं। यदि आपको थ्रेड्स के बारे में चिंता नहीं करनी होती तो आप शायद यही करते।
-
क्लास पर आपके द्वारा सेट किया गया कोई भी डेटा नेमस्पेस्ड होगा। आपको
RequestRegistry.current_userकेPhoneNumberApiRegistry.current_userसे टकराने के बारे में चिंता करने की ज़रूरत नहीं है, उन्हें पूरी तरह से अलग माना जाता है।
रेल्स ActiveSupport::PerThreadRegistry का आंतरिक रूप से उपयोग करता है, एक्टिवरिकॉर्ड कनेक्शन हैंडलिंग, EXPLAIN क्वेरीज़ स्टोर करने, और ActiveSupport::Notifications जैसी चीजों के लिए। इसलिए यदि आप PerThreadRegistry की शक्ति के अधिक अच्छे उदाहरण ढूंढ रहे हैं, तो रेल्स खुद शुरू करने के लिए एक शानदार जगह है।
जब थ्रेड्स और रिक्वेस्ट मेल नहीं खाते
कुछ रेल्स सर्वर्स के लिए, एक सिंगल थ्रेड कई रिक्वेस्ट्स को संभालता है। इसका मतलब है कि आप जो कुछ भी PerThreadRegistry में डालते हैं वह अगली रिक्वेस्ट के लिए चिपका रहेगा। यह शायद वह नहीं है जो आप चाहते हैं।
आप वह कर सकते हैं जो रेल्स करता है, और उन चीजों को साफ करें जिन्हें आप PerThreadRegistry में डालते हैं जब आप उनके साथ काम कर लेते हैं। आप जो उसमें डालते हैं उस पर निर्भर करते हुए, आप वैसे भी ऐसा कर रहे होंगे।
टिप्पणियों में, मैटबी एक आसान समाधान की ओर इशारा करता है: request_store। यह Thread.current[] की तरह कार्य करता है, लेकिन प्रत्येक रिक्वेस्ट के बाद खुद को साफ कर लेता है।
यह किसी के लिए PerRequestRegistry बनाने का द्वार खुला छोड़ देता है, जो request_store की सुरक्षा को PerThreadRegistry के API के साथ जोड़ता है। अगर किसी ने यह पहले ही कर लिया है, तो मुझे इसके बारे में सुनना अच्छा लगेगा!
इसे आजमाएं
ग्लोबल डेटा खराब हो सकता है। बहुत सारे ग्लोबल जल्दी से अनुरक्षणीय कोड की ओर ले जा सकते हैं।
लेकिन अगर पूरे रिक्वेस्ट के लिए एक केंद्रीय स्थान पर डेटा रखना समझ में आता है, तो Thread.current[] से आगे बढ़ें। ActiveSupport::PerThreadRegistry या request_store को एक शॉट दें। अगर कुछ नहीं, तो यह ग्लोबल डेटा से निपटने को थोड़ा कम जोखिम भरा बना देगा।