निम्नलिखित काल्पनिक परिदृश्य की कल्पना करें:किराये की संपत्ति प्रबंधन प्रणाली में, कर्मचारी ए कुछ अतिरिक्त फोन नंबर जोड़कर, रेंटल एक्स के लिए संपर्क जानकारी संपादित करना शुरू कर देता है। लगभग उसी समय, कर्मचारी बी ने ठीक उसी रेंटल एक्स के लिए संपर्क जानकारी में एक टाइपो नोटिस किया और एक अद्यतन किया। कुछ मिनट बाद, कर्मचारी ए नए फोन नंबरों के साथ रेंटल एक्स की संपर्क जानकारी अपडेट करता है, और ... टाइपो को ठीक करने वाला अपडेट अब चला गया है!
यह निश्चित रूप से बहुत अच्छा नहीं है! और यह एक बहुत ही तुच्छ परिदृश्य है। कल्पना कीजिए कि एक वित्तीय प्रणाली में एक समान संघर्ष हो रहा है!
क्या हम भविष्य में ऐसे परिदृश्यों से बच सकते हैं? सौभाग्य से, उत्तर हाँ है! ऐसी समस्याओं को रोकने के लिए हमें समवर्ती सुरक्षा और लॉकिंग - विशेष रूप से आशावादी लॉकिंग की आवश्यकता है।
आइए रेल आरईएसटी एपीआई में आशावादी लॉकिंग का पता लगाएं।
'खोए हुए अपडेट' और आशावादी लॉकिंग बनाम निराशावादी लॉकिंग
जिस परिदृश्य से हम अभी गुजरे हैं, वह एक प्रकार का 'लॉस्ट अपडेट' है। जब दो समवर्ती लेन-देन एक ही पंक्ति के एक ही कॉलम को अपडेट करते हैं, तो दूसरा लेन-देन पहले वाले के परिवर्तनों को ओवरराइड कर देगा, अनिवार्य रूप से जैसे कि पहला लेन-देन कभी नहीं हुआ।
आमतौर पर, इस समस्या का समाधान निम्न द्वारा किया जा सकता है:
- एक उचित लेनदेन अलगाव स्तर सेट करना, जो डेटाबेस स्तर पर समस्या को संभालता है।
- निराशावादी लॉकिंग — समवर्ती लेनदेन को एक ही पंक्ति को अपडेट करने से रोकना। दूसरा लेन-देन डेटा को पढ़ने से पहले पहले लेनदेन के समाप्त होने की प्रतीक्षा करता है। यहां सबसे बड़ा फायदा यह है कि पुराने डेटा पर काम करना असंभव है। हालांकि, इसका सबसे बड़ा नुकसान यह है कि यह दी गई पंक्ति से डेटा पढ़ने को भी रोकता है।
- आशावादी लॉकिंग - दी गई पंक्ति के संशोधन को रोकता है यदि संशोधन के समय इसकी स्थिति पढ़ने के समय से भिन्न होती है।
हमारी समस्या समवर्ती डेटाबेस लेनदेन (व्यावसायिक लेनदेन की तरह) के बारे में नहीं है - इसलिए पहला समाधान वास्तव में लागू नहीं है। इसका मतलब है कि हमारे पास निराशावादी लॉकिंग और आशावादी लॉकिंग है।
निराशावादी लॉकिंग पहली बार में हमारे काल्पनिक परिदृश्य में खोए हुए अपडेट को होने से रोकेगा। हालांकि, यह उपयोगकर्ताओं के लिए जीवन को कठिन बना देगा यदि यह बहुत लंबे समय तक डेटा तक पहुंच को अवरुद्ध करता है (कल्पना करें कि यह 30 मिनट या उससे अधिक के लिए कुछ फ़ील्ड को पढ़ और संपादित कर रहा है)।
आशावादी लॉकिंग बहुत कम प्रतिबंधात्मक होगा, क्योंकि यह कई उपयोगकर्ताओं को डेटा तक पहुंचने की अनुमति देगा। हालाँकि, यदि कई उपयोगकर्ता डेटा को एक साथ संपादित करना शुरू करते हैं, तो केवल एक ही ऑपरेशन कर सकता है। बाकी को यह बताते हुए एक त्रुटि दिखाई देगी कि वे पुराने डेटा पर काम कर रहे हैं और उन्हें पुनः प्रयास करने की आवश्यकता है। आदर्श नहीं है, लेकिन उचित UX के साथ, यह आवश्यक रूप से उतना दर्दनाक नहीं हो सकता है।
आइए देखें कि हम एक काल्पनिक रेल रेस्ट एपीआई में आशावादी लॉकिंग को कैसे लागू कर सकते हैं।
REST API में आशावादी लॉकिंग
इससे पहले कि हम वास्तविक रेल ऐप में कार्यान्वयन करें, आइए सोचें कि सामान्य आरईएसटी एपीआई के संदर्भ में आशावादी लॉकिंग कैसा दिख सकता है।
जैसा कि ऊपर बताया गया है, हमें किसी वस्तु को पढ़ते समय उसकी मूल स्थिति को ट्रैक करना होगा ताकि अद्यतन के दौरान उसकी बाद की स्थिति से तुलना की जा सके। यदि अंतिम पढ़ने के बाद से राज्य नहीं बदलता है, तो ऑपरेशन की अनुमति है। हालांकि, अगर यह बदल गया है, तो यह विफल हो जाएगा।
REST API के संदर्भ में हमें जो पता लगाने की आवश्यकता है वह है:
- किसी दिए गए संसाधन के डेटा को पढ़ते समय, हम किसी वस्तु की वर्तमान स्थिति को कैसे व्यक्त करते हैं और इसे उपभोक्ता के लिए प्रतिक्रिया में कैसे लौटाते हैं?
- अपडेट करते समय उपभोक्ताओं को किसी संसाधन की मूल स्थिति को एपीआई में कैसे प्रचारित करना चाहिए?
- यदि राज्य बदल गया है और अपडेट संभव नहीं है तो एपीआई उपभोक्ता को क्या लौटाएगा?
अच्छी खबर यह है कि इन सभी सवालों के जवाब HTTP सेमेन्टिक्स के साथ दिए जा सकते हैं।
जहां तक संसाधन की स्थिति पर नज़र रखने की बात है, हम Entity Tags
. का लाभ उठा सकते हैं (या ईटैग)। हम समर्पित ETag
. में संसाधन का फ़िंगरप्रिंट/चेकसम/संस्करण संख्या वापस कर सकते हैं बाद में पैच अनुरोध के साथ भेजने के लिए एपीआई उपभोक्ताओं को हेडर। हम एक If-Match
का उपयोग कर सकते हैं हेडर, एपीआई सर्वर के लिए यह जांचना बहुत आसान बनाता है कि संसाधन बदल गया है या नहीं। यह केवल चेकसम/संस्करण संख्या/जो भी आप ETag के रूप में चुनते हैं उसकी तुलना करने का मामला है।
अनुरोध सफल होगा यदि वर्तमान ETag
और If-Match
मूल्य समान हैं। यदि नहीं, तो एपीआई को शायद ही कभी उपयोग किए जाने वाले 412 Precondition Failed
के साथ जवाब देना चाहिए स्थिति, सबसे उपयुक्त और अभिव्यंजक स्थिति जिसका उपयोग हम इस उद्देश्य के लिए कर सकते हैं।
एक और संभावित परिदृश्य है। हम ईटैग की तुलना केवल तभी कर सकते हैं जब एपीआई उपभोक्ता If-Match
. प्रदान करता है शीर्षलेख। क्या होगा अगर यह नहीं है? आप समवर्ती सुरक्षा को अनदेखा कर सकते हैं और आशावादी लॉकिंग के बारे में भूल सकते हैं, लेकिन यह आदर्श नहीं हो सकता है। एक अन्य समाधान यह होगा कि इसे If-Match
. प्रदान करने की आवश्यकता बना दी जाए शीर्षलेख और प्रतिक्रिया के साथ 428 Precondition Required
स्थिति अगर यह नहीं है।
अब जबकि हमारे पास इस बात का ठोस अवलोकन है कि आरईएसटी एपीआई में आशावादी लॉकिंग कैसे काम कर सकती है, आइए इसे रेल में लागू करें।
रेल में आशावादी लॉकिंग
अच्छी खबर यह है कि रेल आशावादी लॉकिंग आउट-ऑफ-द-बॉक्स प्रदान करता है - हम ActiveRecord::Locking::Optimistic
द्वारा प्रदान की गई सुविधा का उपयोग कर सकते हैं। . जब आप lock_version
जोड़ते हैं कॉलम (या जो कुछ भी आप चाहते हैं, हालांकि लॉकिंग कॉलम को परिभाषित करने के लिए मॉडल स्तर पर अतिरिक्त घोषणाओं की आवश्यकता है), ActiveRecord प्रत्येक परिवर्तन के बाद इसे बढ़ा देगा और जांच करेगा कि वर्तमान में असाइन किया गया संस्करण अपेक्षित है या नहीं। यदि यह पुराना है, तो ActiveRecord::StaleObjectError
अद्यतन/नष्ट करने के प्रयास पर अपवाद उठाया जाएगा।
हमारे एपीआई में आशावादी लॉकिंग को संभालने का सबसे आसान तरीका 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
और यह वह सब कुछ है जो उन सभी कार्यक्षमताओं को लागू करने के लिए आवश्यक है जिनकी हमने पहले चर्चा की थी। हम और चीजों में सुधार कर सकते हैं - उदाहरण के लिए, केवल प्रतिक्रिया कोड के अलावा कुछ अतिरिक्त त्रुटि संदेश प्रदान करके - लेकिन यह इस लेख के दायरे से बाहर होगा।
रैप-अप:रेल एपीआई में आशावादी लॉकिंग का महत्व
आरईएसटी एपीआई डिजाइन करते समय अक्सर समवर्ती सुरक्षा की अनदेखी की जाती है, जिसके गंभीर परिणाम हो सकते हैं।
फिर भी, रेल एपीआई में आशावादी लॉकिंग को लागू करना बहुत सरल है - जैसा कि इस लेख में दिखाया गया है - और संभावित गंभीर मुद्दों से बचने में मदद करेगा।
कोडिंग का मज़ा लें!
पी.एस. यदि आप रूबी मैजिक की पोस्ट प्रेस से छूटते ही पढ़ना चाहते हैं, तो हमारे रूबी मैजिक न्यूजलेटर की सदस्यता लें और एक भी पोस्ट मिस न करें!