Computer >> कंप्यूटर ट्यूटोरियल >  >> प्रोग्रामिंग >> Ruby

अपने डेटा की रक्षा करें: Rails REST APIs में ऑप्टिमिस्टिक लॉकिंग

निम्न काल्पनिक परिदृश्य की कल्पना करें: एक रेंटल प्रॉपर्टी मैनेजमेंट सिस्टम में, कर्मचारी A रेंटल X के लिए संपर्क जानकारी संपादित करना शुरू करता है, कुछ अतिरिक्त फोन नंबर जोड़ता है। लगभग उसी समय, कर्मचारी B ठीक उसी रेंटल X की संपर्क जानकारी में एक टाइपो देखता है और एक अपडेट करता है। कुछ मिनट बाद, कर्मचारी A रेंटल X की संपर्क जानकारी को नए फोन नंबरों के साथ अपडेट करता है, और ... टाइपो ठीक करने वाला अपडेट अब चला गया है!

यह निश्चित रूप से अच्छा नहीं है! और यह एक बहुत ही तुच्छ परिदृश्य है। वित्तीय प्रणाली में इसी तरह के संघर्ष की कल्पना करें!

क्या हम भविष्य में ऐसे परिदृश्यों से बच सकते हैं? सौभाग्य से, जवाब हां है! हमें ऐसे मुद्दों को रोकने के लिए कंकरेंसी प्रोटेक्शन और लॉकिंग — विशेष रूप से, ऑप्टिमिस्टिक लॉकिंग — की आवश्यकता है।

आइए Rails REST APIs में ऑप्टिमिस्टिक लॉकिंग का पता लगाएं।

'लॉस्ट अपडेट्स' और ऑप्टिमिस्टिक लॉकिंग बनाम पेसिमिस्टिक लॉकिंग

हमने अभी जिस परिदृश्य को देखा है वह एक प्रकार का 'लॉस्ट अपडेट' है। जब दो समवर्ती ट्रांजैक्शन्स एक ही पंक्ति के एक ही कॉलम को अपडेट करते हैं, तो दूसरा पहले के परिवर्तनों को ओवरराइड कर देगा, अनिवार्य रूप से जैसे कि पहला ट्रांजैक्शन कभी हुआ ही नहीं।

आमतौर पर, इस समस्या को निम्न द्वारा संबोधित किया जा सकता है:

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

हमारी समस्या समवर्ती डेटाबेस ट्रांजैक्शन्स के बारे में नहीं है (व्यावसायिक ट्रांजैक्शन्स की तरह अधिक) — इसलिए पहला समाधान वास्तव में लागू नहीं होता। इसका मतलब है कि हम पेसिमिस्टिक लॉकिंग और ऑप्टिमिस्टिक लॉकिंग के साथ बचे हैं।

पेसिमिस्टिक लॉकिंग हमारे काल्पनिक परिदृश्य में लॉस्ट अपडेट को पहले स्थान पर होने से रोकेगा। हालांकि, यह उपयोगकर्ताओं के लिए जीवन कठिन भी बना देगा यदि यह बहुत लंबे समय तक डेटा तक पहुंच को ब्लॉक करता है (कल्पना करें कि कुछ फील्ड्स को पढ़ने और संपादित करने में 30 मिनट या अधिक समय लगता है)।

ऑप्टिमिस्टिक लॉकिंग बहुत कम प्रतिबंधात्मक होगा, क्योंकि यह कई उपयोगकर्ताओं को डेटा तक पहुंचने की अनुमति देगा। हालांकि, यदि कई उपयोगकर्ता डेटा को समवर्ती रूप से संपादित करना शुरू करते हैं, तो केवल एक ही ऑपरेशन कर सकता है। बाकी को एक एरर दिखाई देगी जिसमें कहा गया है कि उन्होंने स्टेल डेटा पर काम किया है और उन्हें रीट्राई करने की आवश्यकता है। आदर्श नहीं, लेकिन उचित UX के साथ, यह जरूरी नहीं कि इतना दर्दनाक हो।

आइए देखें कि हम काल्पनिक Rails REST API में ऑप्टिमिस्टिक लॉकिंग को कैसे लागू कर सकते हैं।

REST APIs में ऑप्टिमिस्टिक लॉकिंग

इससे पहले कि हम वास्तविक Rails ऐप में कार्यान्वयन पर पहुंचें, आइए सोचें कि ऑप्टिमिस्टिक लॉकिंग सामान्य REST APIs के संदर्भ में कैसा दिख सकता है।

जैसा कि ऊपर वर्णित है, हमें किसी ऑब्जेक्ट की मूल स्थिति को पढ़ते समय ट्रैक करने की आवश्यकता है ताकि बाद में अपडेट के दौरान इसकी बाद की स्थिति के साथ तुलना की जा सके। यदि अंतिम पढ़ने के बाद स्थिति नहीं बदलती है, तो ऑपरेशन की अनुमति है। यदि यह बदल गई है, तो यह विफल हो जाएगा।

REST APIs के संदर्भ में हमें यह पता लगाने की आवश्यकता है:

  • किसी दिए गए संसाधन का डेटा पढ़ते समय, हम ऑब्जेक्ट की वर्तमान स्थिति को कैसे व्यक्त करते हैं और उपभोक्ता के लिए रिस्पॉन्स में इसे वापस करते हैं?
  • उपभोक्ताओं को API पर अपडेट करते समय संसाधन की मूल स्थिति को कैसे प्रसारित करना चाहिए?
  • यदि स्थिति बदल गई है और अपडेट संभव नहीं है तो API उपभोक्ता को क्या वापस करना चाहिए?

अच्छी खबर यह है कि इन सभी सवालों का जवाब HTTP सिमेंटिक्स के साथ दिया और संभाला जा सकता है।

जहां तक संसाधन की स्थिति को ट्रैक करने की बात है, हम Entity Tags (या ETags) का लाभ उठा सकते हैं। हम संसाधन के फिंगरप्रिंट/चेकसम/संस्करण संख्या को समर्पित ETag हेडर में API उपभोक्ताओं को वापस कर सकते हैं ताकि बाद में PATCH रिक्वेस्ट के साथ भेजा जा सके। हम If-Match हेडर का उपयोग कर सकते हैं, जिससे API सर्वर के लिए यह जांचना काफी सीधा हो जाता है कि संसाधन बदल गया है या नहीं। यह केवल चेकसम/संस्करण संख्या/जो भी आप ETag के रूप में चुनते हैं, की तुलना का मामला है।

यदि वर्तमान ETag और If-Match मान समान हैं तो रिक्वेस्ट सफल होगी। यदि नहीं, तो API को शायद ही कभी उपयोग किए जाने वाले 412 Precondition Failed स्टेटस के साथ प्रतिक्रिया देनी चाहिए, जो इस उद्देश्य के लिए हम उपयोग कर सकने वाला सबसे उपयुक्त और अभिव्यंजक स्टेटस है।

एक अन्य संभावित परिदृश्य है। हम ETags की तुलना तभी कर सकते हैं जब API उपभोक्ता If-Match हेडर प्रदान करता है। क्या होगा अगर यह नहीं करता? आप कंकरेंसी प्रोटेक्शन को अनदेखा कर सकते हैं और ऑप्टिमिस्टिक लॉकिंग को भूल सकते हैं, लेकिन यह आदर्श नहीं हो सकता। एक अन्य समाधान If-Match हेडर प्रदान करना अनिवार्य बना देगा और यदि यह नहीं है तो 428 Precondition Required स्टेटस के साथ प्रतिक्रिया देगा।

अब जब हमारे पास REST APIs में ऑप्टिमिस्टिक लॉकिंग कैसे काम कर सकती है, इसका एक ठोस अवलोकन है, तो आइए इसे Rails में लागू करें।

Rails में ऑप्टिमिस्टिक लॉकिंग

अच्छी खबर यह है कि Rails ActiveRecord::Locking::Optimistic द्वारा प्रदान की गई सुविधा का उपयोग करके ऑप्टिमिस्टिक लॉकिंग को आउट-ऑफ-द-बॉक्स प्रदान करता है। जब आप lock_version कॉलम जोड़ते हैं (या जो भी आप चाहते हैं, हालांकि इसके लिए मॉडल स्तर पर अतिरिक्त घोषणाओं की आवश्यकता होती है लॉकिंग कॉलम को परिभाषित करने के लिए), ActiveRecord प्रत्येक परिवर्तन के बाद इसे बढ़ाएगा और जांच करेगा कि वर्तमान में असाइन किया गया संस्करण अपेक्षित है या नहीं। यदि यह स्टेल है, तो अपडेट/डिस्ट्रॉय प्रयास पर ActiveRecord::StaleObjectError एक्सेप्शन उठाया जाएगा।

हमारे API में ऑप्टिमिस्टिक लॉकिंग को संभालने का सबसे आसान तरीका lock_version से मान को ETag के रूप में उपयोग करना है। आइए इसे हमारे काल्पनिक RentalsController में पहला कदम बनाएं:

class RentalsController after_action :assign_etag, only: [:show] def show @rental = Rental.find(params[:id]) respond_with @rental end private def assign_etag response.headers["ETag"] = @rental.lock_version end end

यह, निश्चित रूप से, कंट्रोलर का एक बहुत सरल संस्करण है क्योंकि हम केवल वही चीजों में रुचि रखते हैं जो ऑप्टिमिस्टिक लॉकिंग के लिए आवश्यक हैं, न कि प्रमाणीकरण, प्राधिकरण, या अन्य अवधारणाएं। यह उपभोक्ता को उचित ETag उजागर करने के लिए पर्याप्त है। आइए अब If-Match हेडर का ध्यान रखें जो उपभोक्ता प्रदान कर सकते हैं:

class RentalsController after_action :assign_etag, only: [:show, :update] def show @rental = Rental.find(params[:id]) respond_with @rental end def update @rental = Rental.find(params[:id]) @rental.update(rental_params) respond_with @rental end private def assign_etag response.headers["ETag"] = @rental.lock_version end def rental_params params .require(:rental) .permit(:some, :permitted, :attributes).merge(lock_version: lock_version_from_if_match_header) end def lock_version_from_if_match_header request.headers["If-Match"].to_i end end

और यह वास्तव में ऑप्टिमिस्टिक लॉकिंग के न्यूनतम संस्करण को काम करने के लिए पर्याप्त है! हालांकि, स्पष्ट रूप से, हम नहीं चाहते कि कोई संघर्ष होने पर 500 रिस्पॉन्स वापस करें। हम किसी भी अपडेट के लिए If-Match को आवश्यक भी बना देंगे:

class RentalsController before_action :ensure_if_match_header_provided, only: [:update] after_action :assign_etag, only: [:show, :update] rescue_from ActiveRecord::StaleObjectError do head 412 end def show @rental = Rental.find(params[:id]) respond_with @rental end def update @rental = Rental.find(params[:id]) @rental.update(rental_params) respond_with @rental end private def ensure_if_match_header_provided request.headers["If-Match"].present? or head 428 and return end def assign_etag response.headers["ETag"] = @rental.lock_version end def rental_params params .require(:rental) .permit(:some, :permitted, :attributes) .merge(lock_version: lock_version_from_if_match_header) end def lock_version_from_if_match_header request.headers["If-Match"].to_i end end

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

समापन: Rails APIs में ऑप्टिमिस्टिक लॉकिंग का महत्व

REST APIs डिजाइन करते समय कंकरेंसी प्रोटेक्शन को अक्सर अनदेखा किया जाता है, जिससे गंभीर परिणाम हो सकते हैं।

फिर भी, जैसा कि इस लेख में दिखाया गया है, Rails APIs में ऑप्टिमिस्टिक लॉकिंग लागू करना काफी सीधा है — और संभावित रूप से महत्वपूर्ण मुद्दों से बचने में मदद करेगा।

हैप्पी कोडिंग!

P.S. यदि आप Ruby Magic पोस्ट्स को प्रेस से निकलते ही पढ़ना चाहते हैं, तो हमारे Ruby Magic न्यूज़लेटर को सब्सक्राइब करें और कभी भी एक भी पोस्ट मिस न करें!


  1. Rails प्रोजेक्ट्स के लिए एक कुशल डॉक्यूमेंटेशन वर्कफ़्लो डिज़ाइन करना Rails प्रोजेक्ट्स के लिए एक कुशल डॉक्यूमेंटेशन वर्कफ़्लो डिज़ाइन करना

    आधुनिक डॉक्यूमेंटेशन वर्कफ़्लोज़ सॉफ्टवेयर डेवलपमेंट वर्कफ़्लो के साथ तेजी से जुड़ रहे हैं। आप GitHub या Jira में डॉक्यूमेंटेशन इश्यूज़ ट्रैक कर सकते हैं, या आप कोड कमेंट्स या Markdown फाइलों में डॉक्स लिख सकते हैं। आपकी टीम के डेवलपर्स टेक्निकल राइटर्स के साथ सीधे काम कर सकते हैं, या वे स्वतंत्र रू

  1. अपने रेल्स ऐप्स को सुरक्षित करें: इंजेक्शन अटैक्स को समझना और रोकना अपने रेल्स ऐप्स को सुरक्षित करें: इंजेक्शन अटैक्स को समझना और रोकना

    यदि आप यूजर डेटा से डील करते हैं, तो आपको यह सुनिश्चित करना होगा कि वह सुरक्षित है। हालांकि, यदि आप सुरक्षा में नए हैं, तो यह ट्रिकी, बोरिंग और जटिल लग सकता है। यह लेख उस श्रृंखला का पहला भाग है जो आपको सामान्य प्रकार की सुरक्षा कमजोरियों और रेल्स डेवलपमेंट पर उनके प्रभाव के बारे में सिखाएगा। हम OWA

  1. Angular को Rails 5 के साथ इंटीग्रेट करना: एक सहज फुल-स्टैक दृष्टिकोण Angular को Rails 5 के साथ इंटीग्रेट करना: एक सहज फुल-स्टैक दृष्टिकोण

    आपने पहले कहानी सुनी है। आपके पास पहले से ही एक एप्लिकेशन है जो आपके विकेंद्रीकृत और पूरी तरह से काम करने वाले बैक-एंड APIs और किसी सामान्य टूलसेट के साथ बने फ्रंट-एंड पर चल रहा है। अब, आप Angular पर आगे बढ़ना चाहते हैं। या, शायद आप बस अपने रेल्स प्रोजेक्ट्स के साथ Angular को इंटीग्रेट करने का एक तर