समस्या और लागू होने वाले परिदृश्य
रिसोर्स invoice_items/{id} में एक विवरण (description) और एक राशि (amount) शामिल है। Alice और Bob दोनों वर्ज़न 7 पढ़ते हैं। Alice पहले सेव करती है और वर्ज़न 8 बनाती है। इसके बाद Bob एक संपादन सबमिट करता है जो अभी भी वर्ज़न 7 पर आधारित है। एक बिना शर्त (unconditional) UPDATE Bob को Alice के डेटा को ओवरराइट करने देता है जबकि दोनों कॉलर्स को सफलता (success) प्राप्त होती है। यह वही लॉस्ट अपडेट (lost update) है जिसे इस प्रश्न में रोका जाना चाहिए।
मान लें कि एक HTTP JSON API है, एक रिलेशनल डेटाबेस है जो कंडीशनल अपडेट्स का समर्थन करता है, और रिसोर्स IDs हैं जिन्हें कभी दोबारा इस्तेमाल नहीं किया जाता है। प्रत्येक रिस्पॉन्स का एक कैनोनिकल JSON रिप्रजेंटेशन होता है। प्रत्येक बिजनेस-फील्ड परिवर्तन रिसोर्स वर्ज़न को आगे बढ़ाता है और एक नया स्ट्रॉन्ग ETag उत्पन्न करता है। कॉन्फ्लिक्ट्स के असामान्य होने की उम्मीद है, इसलिए यह डिज़ाइन किसी व्यक्ति द्वारा संपादन करते समय डेटाबेस लॉक रखने के बजाय ऑप्टिमिस्टिक कॉन्करेंसी (optimistic concurrency) का उपयोग करता है। PUT, PATCH और DELETE स्कोप में हैं; रियल-टाइम कोलाबोरेटिव एडिटिंग एल्गोरिदम स्कोप में नहीं हैं।
मुख्य योग्यता बैकएंड डिज़ाइन है: क्लाइंट द्वारा पढ़े गए वर्ज़न, एक HTTP प्रीकंडीशन, और एक एटॉमिक डेटाबेस राइट को एक टेस्टेबल कॉन्ट्रैक्ट में जोड़ना। उदाहरण PostgreSQL-शैली SQL का उपयोग करते हैं; किसी अन्य डेटाबेस को एक समतुल्य कंडीशनल अपडेट और प्रभावित-पंक्ति (affected-row) चेक की आवश्यकता होती है। एक कंट्रोलर में वर्ज़न की तुलना करना और फिर एक अनकंडीशनल अपडेट जारी करना अभी भी चेक और राइट के बीच एक रेस कंडीशन (race condition) छोड़ देता है।
इंटरव्यूअर क्या मूल्यांकन कर रहा है
पहला संकेत यह है कि क्या उम्मीदवार लॉस्ट-अपडेट इंटरलीविंग को दिखा सकता है और समझा सकता है कि अंतिम लेखक की जीत (last writer wins) हमेशा सही क्यों नहीं होती है। एक मजबूत उत्तर प्रत्येक संपादन योग्य रीड पर एक वर्ज़न विटनेस (witness) लौटाता है और राइटर से यह साबित करने की मांग करता है कि यह वर्तमान वर्ज़न पर आधारित है।
दूसरा संकेत सटीक HTTP सिमेंटिक्स है। एक GET एक स्ट्रॉन्ग ETag लौटाता है और एक मॉडिफाइंग रिक्वेस्ट इसे If-Match में भेजती है। If-Match स्ट्रॉन्ग तुलना का उपयोग करता है, इसलिए एक पुराना (stale) या कमज़ोर (weak) टैग पास नहीं हो सकता। एक अनुपलब्ध आवश्यक प्रीकंडीशन 428 Precondition Required उत्पन्न कर सकती है; दिया गया टैग जो मेल नहीं खाता है, वह 412
Precondition Failed उत्पन्न करता है। HTTP प्रीकंडीशन द्वारा व्यक्त नहीं किए गए व्यावसायिक कॉन्फ्लिक्ट के लिए 409 Conflict को सुरक्षित रखें।
तीसरा संकेत डेटाबेस चेक और राइट को एटॉमिक बनाना है। id और version दोनों का मिलान करें, बिजनेस फ़ील्ड्स को अपडेट करें, और एक ही स्टेटमेंट में वर्ज़न को इनक्रीमेंट करें। शून्य प्रभावित पंक्तियाँ वह बिंदु हैं जहाँ रिसोर्स या तो समाप्त हो चुका है या पुराना है। वर्ज़न को पढ़ना और फिर अनकंडीशनल राइट करना एक TOCTOU रेस है।
अंत में, कॉन्फ्लिक्ट रिकवरी और सीमाओं पर ध्यान दें। ऑप्टिमिस्टिक कॉन्करेंसी आइडेम्पोटेंसी की (idempotency key) की जगह नहीं लेती है, और एक पंक्ति का वर्ज़न क्रॉस-पंक्ति प्रेडिकेट की रक्षा नहीं करता है। यदि कोई हॉट रिसोर्स लगातार 412 लौटाता है, तो डिज़ाइन को क्लाइंट्स से हमेशा के लिए तुरंत पुनः प्रयास कराने के बजाय अपनी कंटेंशन रणनीति को बदलना चाहिए।
उत्तर देने से पहले स्पष्ट करने योग्य प्रश्न
- क्या कॉन्फ्लिक्ट डोमेन पूरा रिसोर्स है या केवल एक फ़ील्ड? पूरे-रिसोर्स का वर्ज़न साबित करना सबसे आसान है, लेकिन
अलग-अलग फ़ील्ड्स के संपादन में भी कॉन्फ्लिक्ट होगा। लगातार होने वाले झूठे कॉन्फ्लिक्ट्स (false conflicts) छोटे एग्रीगेट्स, स्पष्ट ऑपरेशन APIs, या फ़ील्ड-लेवल मर्ज नियमों को उचित ठहरा सकते हैं।
- किन म्यूटेशन मेथड्स के लिए वर्ज़न की आवश्यकता होती है? इस प्रश्न के लिए
PUT,PATCHऔर
DELETE पर If-Match की आवश्यकता है। इम्पोर्ट्स, बैकग्राउंड जॉब्स और एडमिनिस्ट्रेशन स्क्रिप्ट्स को एक असुरक्षित राइट पाथ बनाए रखने के बजाय उसी प्रोटोकॉल का पालन करना चाहिए।
- कॉन्फ्लिक्ट का समाधान कौन करता है? मानवीय संपादनों के लिए, मूल, प्रस्तावित और वर्तमान मान दिखाएं और यूज़र को
चुनने दें। कोई मशीन केवल तभी पुनर्गणना और पुनः प्रयास कर सकती है जब उसका मर्ज फ़ंक्शन व्यावसायिक इनवेरिएंट को बनाए रखता हो।
- कॉन्फ्लिक्ट रेट और लेटेंसी बजट क्या हैं? दुर्लभ टकराव एक ऑप्टिमिस्टिक डिज़ाइन के अनुकूल होते हैं। हॉट इन्वेंटरी या
नीलामी रिकॉर्ड एक एटॉमिक बिजनेस अपडेट, एक छोटे पेसिमिस्टिक ट्रांजेक्शन, या एक सिंगल-राइटर कतार के पक्ष में हो सकते हैं।
- क्या कोई क्लाइंट टाइमआउट के बाद वही रिक्वेस्ट दोबारा भेज सकता है? वर्ज़न चेक विभिन्न इरादों (intents) की रेस को संभालते हैं।
आइडेम्पोटेंसी कीज़ एक इरादे के बार-बार ट्रांसपोर्ट को संभालती हैं। डिज़ाइन को दोनों की आवश्यकता हो सकती है।
- क्या वर्ज़न एक मल्टी-रो नियम को कवर करता है? एक रो वर्ज़न केवल उस रिसोर्स की सुरक्षा करता है। "एक
डॉक्टर को कॉल पर रहना चाहिए" जैसे नियम के लिए Serializable, एक कॉमन लॉक रो, या एक डेटाबेस कंस्ट्रेंट की आवश्यकता होती है।
30-सेकंड उत्तर फ्रेमवर्क
"मैं वर्तमान रिसोर्स को एक स्ट्रॉन्ग ETag के साथ लौटाऊँगा, जैसे कि वर्ज़न 7 के लिए \"invoice-item-42-v7\"। सेव करते समय क्लाइंट उस मान को If-Match में भेजता है। यदि आवश्यक हेडर अनुपस्थित है तो सर्वर 428 लौटाता है और यदि टैग पुराना है तो 412 लौटाता है। वास्तविक सुरक्षा एक एटॉमिक डेटाबेस स्टेटमेंट है: UPDATE ... WHERE id = ? AND version
= 7 बिजनेस फ़ील्ड्स को बदलता है और वर्ज़न को एक साथ इनक्रीमेंट करता है। शून्य पंक्तियों का अर्थ है पुराना या हटाया गया; कभी भी पहले चेक न करें और फिर अनकंडीशनल अपडेट न करें। एक सफल रिस्पॉन्स वर्ज़न 8 का ETag ले जाता है। 412 पर, क्लाइंट डेटा दोबारा प्राप्त करता है और यूज़र को मर्ज करने देता है। एक आइडेम्पोटेंसी की टाइमआउट के बाद पुनः प्रसारण को अलग से कवर करती है। मैं दो क्लाइंट्स का परीक्षण करूँगा जो दोनों वर्ज़न 7 पढ़ते हैं और साबित करूँगा कि ठीक एक राइट ही सफल होता है।"
चरण-दर-चरण विस्तृत विश्लेषण
गलत इतिहास के साथ शुरुआत करें:
Alice: GET item 42 -> amount=10000, version=7
Bob: GET item 42 -> amount=10000, version=7
Alice: PUT amount=10500 -> unconditional UPDATE -> success
Bob: PUT amount=9800 -> unconditional UPDATE -> success
Final: amount=9800; Alice's accepted update is lostअनुशंसित रीड कॉन्ट्रैक्ट एक स्ट्रॉन्ग ETag लौटाता है। यह एक अपारदर्शी मान (opaque value) है जिसे क्लाइंट को बिल्कुल वैसा ही लौटाना चाहिए। इसमें संवेदनशील जानकारी नहीं होनी चाहिए, और यह कभी भी प्रमाणीकरण (authentication) या प्राधिकरण (authorization) की जगह नहीं लेता है:
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "invoice-item-42-v7"
{"id":42,"description":"Consulting","amountCents":10000,"version":7}Alice उस टैग को If-Match में रखती है:
PUT /invoice-items/42 HTTP/1.1
Content-Type: application/json
If-Match: "invoice-item-42-v7"
{"description":"Consulting","amountCents":10500}सर्वर प्रीकंडीशन का मूल्यांकन करने से पहले अपना सामान्य प्रमाणीकरण, प्राधिकरण और रिक्वेस्ट वैलिडेशन करता है। यदि API के लिए प्रत्येक म्यूटेशन का कंडीशनल होना आवश्यक है, तो If-Match की अनुपस्थिति 428 लौटाती है और क्लाइंट को टैग के साथ दोबारा प्राप्त करने और पुनः सबमिट करने के लिए कहती है। यदि दिया गया मान वर्तमान स्ट्रॉन्ग ETag नहीं है, तो सर्वर रिसोर्स को संशोधित नहीं करता है और 412 लौटाता है। स्ट्रॉन्ग तुलना के लिए दोनों एंटिटी टैग्स का नॉन-वीक होना आवश्यक है और उनके अपारदर्शी टैग्स का वर्ण-दर-वर्ण मेल खाना आवश्यक है। इसलिए W/"invoice-item-42-v7" पास नहीं हो सकता।
HTTP चेक के बाद, डेटाबेस को उसी अपेक्षित वर्ज़न को एटॉमिक रूप से लागू करना चाहिए:
UPDATE invoice_items
SET description = $1,
amount_cents = $2,
version = version + 1
WHERE id = $3
AND version = $4
RETURNING id, description, amount_cents, version;$4 मान्य ETag से डिकोड किया गया अपेक्षित वर्ज़न 7 है। एक पंक्ति लौटाए जाने का अर्थ सफलता है, इसलिए रिस्पॉन्स में वर्ज़न 8 और उसका नया ETag शामिल होता है। शून्य पंक्तियों पर, प्राधिकरण सीमा के भीतर वर्तमान रिकॉर्ड पढ़ें: यदि यह अनुपस्थित है तो 404 लौटाएं, या यदि यह अभी भी मौजूद है तो 412 लौटाएं। नो-ID-रीयूज़ धारणा डिलीट और री-क्रिएट को मूल रिसोर्स के रूप में प्रदर्शित होने से रोकती है। वर्गीकरण की परवाह किए बिना, शून्य-पंक्ति पाथ कभी राइट नहीं करता है।
SELECT version न चलाएं, इसकी तुलना न करें, और इसके बाद ऐसा UPDATE न लगाएं जिसमें वर्ज़न प्रेडिकेट न हो। उन स्टेटमेंट्स के बीच एक अन्य ट्रांजेक्शन कमिट हो सकता है, जिससे वह रिक्वेस्ट जिसने अभी-अभी अपना चेक पास किया है, नई स्थिति को ओवरराइट कर सकती है। भले ही कंट्रोलर ने ETag की तुलना की हो, WHERE version = $4 अंतिम शुद्धता सीमा (correctness boundary) है। एक ORM कॉन्करेंसी टोकन को समतुल्य कंडीशनल अपडेट उत्पन्न करना चाहिए और शून्य प्रभावित पंक्तियों को एक कॉन्करेंसी कॉन्फ्लिक्ट में बदलना चाहिए।
प्रत्येक स्टेटस को उसके कारण के साथ संरेखित करें:
428 Precondition Required: क्लाइंट ने इस API द्वारा आवश्यक शर्त को छोड़ दिया।412 Precondition Failed: क्लाइंट नेIf-Matchभेजा, लेकिन वर्तमान रिप्रजेंटेशन इसे संतुष्ट नहीं करता है।404 Not Found: प्राधिकरण नीति के अधीन, रिसोर्स अब मौजूद नहीं है।409 Conflict: वर्ज़न प्रीकंडीशन पास हो गई, लेकिन एक अन्य बिजनेस-स्टेट कॉन्फ्लिक्ट लागू होता है, जैसे कि एक सेटल किया गया
इनवॉइस जो अपरिवर्तनीय (immutable) है।
412 के बाद, एक इंटरैक्टिव क्लाइंट एक नया GET निष्पादित करता है और तीन मान रखता है: यूज़र ने मूल रूप से क्या पढ़ा, यूज़र ने क्या प्रस्तावित किया, और सर्वर अब क्या संग्रहीत करता है। एक प्रस्तावित परिवर्तन स्वचालित रूप से केवल उस फ़ील्ड पर लागू किया जा सकता है जिसका वर्तमान मान अभी भी मूल के बराबर है; अन्य फ़ील्ड्स को एक स्पष्ट कॉन्फ्लिक्ट निर्णय की आवश्यकता होती है। एक एकल रिसोर्स वर्ज़न असंबद्ध (disjoint) संपादनों को भी रूढ़िवादी रूप से अस्वीकार करता है। यदि यह एक मापा गया बॉटलनेक बन जाता है, तो रिसोर्स को विभाजित करें या चुपचाप लास्ट राइटर विन्स पर वापस जाने के बजाय POST /invoice-items/42/adjust-amount जैसा एक इंटेंट-आधारित ऑपरेशन प्रदर्शित करें।
एक आइडेम्पोटेंसी की एक अलग विफलता का समाधान करती है। मान लीजिए कि Alice का वर्ज़न 7 अपडेट वर्ज़न 8 के रूप में कमिट हो गया लेकिन सफलता का रिस्पॉन्स खो गया। एक शाब्दिक पुनः प्रयास में अब एक पुराना If-Match है। एक स्थिर आइडेम्पोटेंसी की के साथ, सर्वर संग्रहीत पहला परिणाम लौटा सकता है। वर्ज़न विटनेस वर्ज़न 7 पर आधारित दो अलग-अलग संपादनों का पता लगाता है; आइडेम्पोटेंसी रिकॉर्ड पहचानता है कि एक संपादन दो बार प्रसारित किया गया था। वे एक दूसरे के विकल्प नहीं हैं। जब सर्वर विश्वसनीय रूप से पूर्व निष्पादन को साबित नहीं कर सकता है, तो समान वर्तमान डेटा से सफलता का अनुमान लगाने की तुलना में 412 लौटाना अधिक सुरक्षित है।
ऑप्टिमिस्टिक कॉन्करेंसी कम कंटेंशन मानती है। यदि किसी रिसोर्स में लगातार उच्च कॉन्फ्लिक्ट दर है, तो बार-बार पढ़ना और मैनुअल मर्ज क्षमता को बर्बाद करते हैं और यूज़र अनुभव को खराब करते हैं। इन्वेंटरी में कमी सीधे एक-पंक्ति इनवेरिएंट को लागू करने के लिए UPDATE ... SET stock = stock - $1 WHERE stock >= $1 का उपयोग कर सकती है। एक छोटा रीड-डिसाइड-राइट ऑपरेशन एक पंक्ति लॉक का उपयोग कर सकता है। एक एग्रीगेट जिसे क्रम में कमांड्स को प्रोसेस करना चाहिए, उसे एकल राइटर के लिए विभाजित (partitioned) किया जा सकता है। मापी गई कॉन्फ्लिक्ट दर, क्या ऑपरेशन्स कम्यूट करते हैं, प्रतीक्षा बजट, और इनवेरिएंट के दायरे के आधार पर चयन करें।
सत्यापन के लिए API को क्रम में दो बार कॉल करने के बजाय एक इंटरलीविंग लागू करनी चाहिए। दोनों क्लाइंट्स को वर्ज़न 7 पढ़ने दें, एक बैरियर के माध्यम से विभिन्न राशियाँ जारी करें, और ठीक एक सफलता, एक 412, अंतिम वर्ज़न 8, और विजेता की सामग्री को सत्यापित करें। अनुपस्थित If-Match द्वारा 428 लौटाने, कमज़ोर टैग के विफल होने, एक नए ETag के साथ एक सफल रिस्पॉन्स, पुराने टैग के पुराने बने रहने, डिलीट के साथ अपडेट की रेस, और खोए हुए रिस्पॉन्स के बाद उसी आइडेम्पोटेंसी की के साथ रीप्ले का भी परीक्षण करें। प्रोडक्शन में, मिसिंग-प्रीकंडीशन दर, 412 दर, स्वचालित पुनः प्रयास, और एंडपॉइंट द्वारा अंतिम परित्याग (abandonment) की निगरानी करें। अचानक वृद्धि आमतौर पर एक हॉटस्पॉट या ऐसे क्लाइंट की पहचान करती है जो रीफ्रेश नहीं हो रहा है।
उच्च गुणवत्ता वाला नमूना उत्तर
"मैं कॉन्फ्लिक्ट डोमेन को एक इनवॉइस लाइन आइटम पर सेट करूँगा। दोनों यूज़र्स के पास वर्ज़न 7 है, इसलिए बाद के सेव को पहले वाले को बिना शर्त ओवरराइट नहीं करना चाहिए। प्रत्येक संपादन योग्य रीड एक स्ट्रॉन्ग ETag लौटाता है, और प्रत्येक PUT, PATCH, या DELETE को इसे If-Match में इको करना चाहिए। गायब हेडर को 428 मिलता है; पुराने टैग को बिना राइट किए 412 मिलता है।
HTTP लेयर पर तुलना करना पर्याप्त नहीं है। मैं प्रति-रिसोर्स वर्ज़न स्टोर करूँगा और पर्सिस्टेंस लेयर से UPDATE invoice_items SET ..., version = version + 1 WHERE id = ? AND version = ? RETURNING ... को एटॉमिक रूप से निष्पादित करवाऊँगा। Alice के वर्ज़न 7 के साथ सफल होने के बाद, पंक्ति वर्ज़न 8 बन जाती है। Bob का वही प्रेडिकेट शून्य पंक्तियों को प्रभावित करता है, इसलिए यह Alice को ओवरराइट नहीं कर सकता। एक सफलता नया ETag लौटाती है। शून्य पंक्तियों के बाद, एक प्राधिकरण-सचेत रीड 404 को 412 से अलग करता है, लेकिन कोई भी शाखा अनकंडीशनल अपडेट पर वापस नहीं आती है।
412 पर, क्लाइंट डेटा दोबारा प्राप्त करता है और मूल, प्रस्तावित और वर्तमान मानों की तुलना करता है ताकि यूज़र एक वास्तविक कॉन्फ्लिक्ट को हल कर सके। यदि असंबंधित फ़ील्ड्स बार-बार टकराते हैं, तो मैं रिसोर्स को विभाजित करूँगा या इंटेंट-आधारित एटॉमिक ऑपरेशन्स डिज़ाइन करूँगा। एक टाइमआउट पुनः प्रयास अलग से एक आइडेम्पोटेंसी की का उपयोग करता है: यह एक ही इरादे के डुप्लिकेट ट्रांसपोर्ट को पहचानता है, जबकि वर्ज़न उसी पुरानी स्थिति पर आधारित विभिन्न इरादों को पहचानता है।
मैं दो कनेक्शन्स और एक बैरियर का उपयोग करूँगा ताकि दोनों सबमिट करने से पहले वर्ज़न 7 पढ़ें। ठीक एक को सफल होना चाहिए, दूसरे को 412 प्राप्त होना चाहिए, और अंतिम वर्ज़न 8 होना चाहिए। मैं अनुपस्थित If-Match, कमज़ोर टैग्स, डिलीट रेस, और खोए हुए रिस्पॉन्स रीप्ले का भी परीक्षण करूँगा। यदि प्रोडक्शन में 412 दर उच्च बनी रहती है, तो मैं अनिश्चित काल तक पुनः प्रयासों को बढ़ाने के बजाय एक कंडीशनल बिजनेस अपडेट, एक छोटे लॉक, या एक सिंगल राइटर का मूल्यांकन करूँगा।"
सामान्य गलतियाँ
- अपडेट करने से पहले एप्लिकेशन कोड में वर्ज़न की जाँच करना → चेक और राइट के बीच एक और कमिट हो सकता है → वर्ज़न को उसी
UPDATEप्रेडिकेट में रखें और प्रभावित पंक्तियों का निरीक्षण करें। If-Matchअनुपस्थित होने पर भी जारी रखना → सुरक्षित क्लाइंट्स लीगेसी क्लाइंट्स के साथ सह-अस्तित्व में रहते हैं जो अभी भी डेटा को
नष्ट कर सकते हैं → कंडीशनल म्यूटेशन को API कॉन्ट्रैक्ट का हिस्सा बनाएं और अनुपस्थित होने पर 428 लौटाएं।
If-Matchके लिए एक कमज़ोर ETag स्वीकार करना → मानक के लिए स्ट्रॉन्ग तुलना की आवश्यकता होती है, इसलिए एक कमज़ोर टैग मेल नहीं खा सकता
→ संपादन योग्य रिप्रजेंटेशन्स के लिए सिमेंटिक रूप से मान्य स्ट्रॉन्ग ETag उत्पन्न करें।
- प्रत्येक कॉन्फ्लिक्ट के लिए 409 लौटाना → क्लाइंट्स HTTP प्रीकंडीशन विफलता और बिजनेस
स्टेट कॉन्फ्लिक्ट के बीच अंतर नहीं कर सकते हैं → विफल वर्ज़न प्रीकंडीशन के लिए 412 लौटाएं और स्वतंत्र बिजनेस कॉन्फ्लिक्ट के लिए 409 सुरक्षित रखें।
- 412 के बाद स्वचालित रूप से वही अनुरोध दोबारा भेजना → पुराना टैग पुराना ही रहता है, जबकि इसे चुपचाप बदलने से
दूसरा संपादन ओवरराइट हो सकता है → डेटा दोबारा प्राप्त करें और पुनर्गणना करें या यूज़र से मर्ज करने के लिए कहें।
- यह मान लेना कि आइडेम्पोटेंसी कीज़ समवर्ती ओवरराइट्स को रोकती हैं → दो अलग-अलग संपादन अलग-अलग कीज़ का उपयोग करते हैं लेकिन फिर भी
एक ही पुराने बेस को साझा कर सकते हैं → डुप्लिकेट ट्रांसपोर्ट के लिए आइडेम्पोटेंसी और समवर्ती इरादे के लिए वर्ज़न शर्तों का क्रमशः उपयोग करें।
- यह दावा करना कि प्रत्येक पंक्ति पर एक वर्ज़न हर इनवेरिएंट की रक्षा करता है → क्रॉस-रो राइट स्क्यू कभी भी एक पंक्ति के वर्ज़न पर
टकराता नहीं है → इनवेरिएंट के दायरे के लिए Serializable, एक सामान्य कंटेंशन पॉइंट, या एक कंस्ट्रेंट चुनें।
- बिना किसी सीमा के तुरंत एक हॉटस्पॉट का पुनः प्रयास करना → विफल अनुरोध फिर से टकराते हैं और लोड को बढ़ाते हैं → **कॉन्फ्लिक्ट्स को मापें
और एक एटॉमिक ऑपरेशन, एक छोटे लॉक, या एक सिंगल राइटर पर विचार करें।**
फॉलो-अप प्रश्न और उत्तर
फॉलो-अप 1: क्या विभिन्न फ़ील्ड्स के लिए दो PATCH अनुरोधों में कॉन्फ्लिक्ट होना चाहिए?
पूरे-रिसोर्स का वर्ज़न किसी भी फ़ील्ड परिवर्तन पर आगे बढ़ता है, इसलिए अलग-अलग पैच में कॉन्फ्लिक्ट होता है। यह सुरक्षित और आसानी से समझाया जाने वाला डिफ़ॉल्ट है। यदि साक्ष्य कई झूठे कॉन्फ्लिक्ट्स दिखाते हैं, तो स्वतंत्र रूप से बदलते जीवनचक्रों को सबरिसोर्सेस में विभाजित करें या फ़ील्ड बेसलाइन भेजें और थ्री-वे मर्ज परिभाषित करें। फ़ील्ड-लेवल टोकन झूठे कॉन्फ्लिक्ट्स को कम करते हैं लेकिन टोकन स्थिति को बढ़ाते हैं और संयुक्त इनवेरिएंट्स और ऑडिटिंग को जटिल बनाते हैं; उन्हें केवल 412 रिस्पॉन्स को दबाने के लिए न जोड़ें।
फॉलो-अप 2: क्या होता है जब कोई क्लाइंट टाइमआउट के बाद पुनः प्रयास करता है और पुराना ETag पुराना (stale) होता है?
एक स्थिर आइडेम्पोटेंसी की भेजें और की, रिक्वेस्ट डाइजेस्ट, अपेक्षित वर्ज़न, और पहले परिणाम को एक साथ स्टोर करें। उसी की और रिक्वेस्ट के साथ रीप्ले पहला परिणाम लौटाता है; एक अलग रिक्वेस्ट के साथ उसी की को अस्वीकार कर दिया जाता है। एक विश्वसनीय आइडेम्पोटेंसी रिकॉर्ड के बिना, वर्तमान फ़ील्ड्स जो केवल समान दिखते हैं, यह साबित नहीं करते हैं कि पहला अनुरोध सफल हुआ था। 412 लौटाएं और क्लाइंट को वर्तमान स्थिति का निरीक्षण करने दें।
फॉलो-अप 3: क्या updated_at वर्ज़न टोकन हो सकता है?
यह केवल तभी सुरक्षित है जब डेटाबेस इसे उत्पन्न करता है, प्रत्येक प्रासंगिक म्यूटेशन इसे बदलता है, इसकी सटीकता लगातार राइट्स को अलग करती है, और प्रत्येक नोड इसे समान सिमेंटिक्स देता है। ट्रंकेटेड टाइमस्टैम्प सटीकता या बाईपास करने वाला राइट पाथ दो वर्ज़न्स को समान मान दे सकता है। एक प्रति-रिसोर्स पूर्णांक (integer) या एक डेटाबेस-मूल रो वर्ज़न आमतौर पर कम अस्पष्ट समानता जाँच प्रदान करता है और इसे अभी भी एक अपारदर्शी ETag के रूप में एन्कोड किया जा सकता है।
फॉलो-अप 4: उच्च कंटेंशन के तहत डिज़ाइन कैसे बदलना चाहिए?
पहले 412 रिस्पॉन्स को रिसोर्स और ऑपरेशन द्वारा समूहित करें ताकि एक हॉट की, एक अत्यधिक व्यापक कॉन्फ्लिक्ट डोमेन, और लंबे समय से ऑफ़लाइन क्लाइंट्स को अलग किया जा सके। कम्यूटेटिव इनक्रीमेंट या डिक्रीमेंट इरादों को एटॉमिक डेटाबेस एक्सप्रेशन्स में बदलें। छोटे गैर-कम्यूटेटिव निर्णयों के लिए रो लॉक का उपयोग करें, या रिसोर्स की द्वारा सख्ती से ऑर्डर किए गए कमांड्स को एक राइटर पर रूट करें। प्रत्येक विकल्प कम अस्वीकृत राइट्स के बदले प्रतीक्षा, थ्रूपुट, या आर्किटेक्चर जटिलता का ट्रेड-ऑफ करता है, इसलिए मापे गए कॉन्फ्लिक्ट और लेटेंसी डेटा को इस परिवर्तन को ट्रिगर करना चाहिए।
फॉलो-अप 5: एक पंक्ति का वर्ज़न राइट स्क्यू (write skew) को क्यों नहीं रोकता है?
राइट स्क्यू में, दो ट्रांजेक्शन एक ही क्रॉस-रो प्रेडिकेट को पढ़ते हैं और विभिन्न रिकॉर्ड्स को अपडेट करते हैं, इसलिए दोनों रो-वर्ज़न शर्तें पास हो सकती हैं। प्रत्येक टोकन केवल यह साबित करता है कि अपडेट की जा रही पंक्ति नहीं बदली है; यह साझा प्रेडिकेट के बारे में कुछ नहीं कहता है। इनवेरिएंट को एक सामान्य काउंटर रो पर प्रोजेक्ट करें, एक सामान्य गार्ड को लॉक करें, एक उपयुक्त डेटाबेस कंस्ट्रेंट जोड़ें, या गैर-क्रमबद्ध (non-serializable) निर्भरता का पता लगाने के लिए Serializable का उपयोग करें।