AD

Database Concurrency और Deadlocks कैसे सुलझाएं: E-Commerce Inventory की प्रैक्टिकल केस स्टडी

Database Concurrency और Deadlocks कैसे सुलझाएं: E-Commerce Inventory की प्रैक्टिकल केस स्टडी

जब किसी ऑनलाइन स्टोर पर फ्लैश सेल शुरू होती है, तो कुछ ही सेकंड्स में हजारों यूजर्स एक ही प्रोडक्ट खरीदने की कोशिश करते हैं। ऐसी स्थिति में अक्सर बैकएंड डेटाबेस या तो क्रैश हो जाता है या फिर "डबल बुकिंग" (Over-selling) जैसी गंभीर गलतियां सामने आती हैं। इसे डेटाबेस इंजीनियरिंग की भाषा में High Concurrency Conflict और Deadlock कहा जाता है।

इस केस स्टडी में हम एक काल्पनिक लेकिन बेहद रियलिस्टिक ई-कॉमर्स प्लेटफॉर्म ShopEasy का उदाहरण लेंगे। हम स्टेप-बाय-स्टेप समझेंगे कि कैसे उनके इन्वेंटरी मैनेजमेंट सिस्टम में आई डेटाबेस बॉटलनेक की समस्या को सॉल्व किया गया और सिस्टम को लाखों रिक्वेस्ट्स के लिए रिलायबल बनाया गया।

केस का विवरण: समस्या कहां थी?

ShopEasy पर जब एक लिमिटेड एडिशन स्मार्टफोन की सेल शुरू हुई, तो इन्वेंटरी में केवल 100 फोन उपलब्ध थे। सेल शुरू होते ही 5,000 से अधिक यूजर्स ने एक ही सेकंड में 'Buy Now' पर क्लिक किया। कुछ मिनटों के भीतर निम्नलिखित समस्याएं सामने आईं:

  • Over-selling: 100 स्टॉक के मुकाबले 114 ऑर्डर्स कन्फर्म हो गए, जिससे 14 ग्राहकों को रिफंड देना पड़ा।
  • Database Deadlocks: कई डेटाबेस ट्रांजैक्शंस एक-दूसरे के लॉक रिलीज होने का इंतजार करते हुए फ्रीज हो गए (Deadlock Error Code 40P01 in PostgreSQL)।
  • High Latency: डेटाबेस CPU यूटिलाइजेशन 98% पहुंच गया और चेकआउट रिस्पॉन्स टाइम 200ms से बढ़कर 8 सेकंड हो गया।

रूट कॉज एनालिसिस (समस्या क्यों आई?)

डेवलपमेंट टीम के इनिशियल कोड में एक बेसिक एंटी-पैटर्न का इस्तेमाल किया गया था:

  1. एप्लिकेशन ने डेटाबेस से चेक किया: SELECT stock FROM products WHERE id = 101;
  2. एप्लिकेशन लॉजिक ने देखा कि stock > 0 है।
  3. एप्लिकेशन ने स्टॉक घटाने की क्वेरी भेजी: UPDATE products SET stock = stock - 1 WHERE id = 101;

इसे Race Condition कहते हैं। जब दो या अधिक थ्रेड्स एक साथ पहला स्टेप रन करते हैं, तो दोनों को स्टॉक उपलब्ध दिखता है और दोनों ही स्टेप 3 रन कर देते हैं। इसके अलावा, डिफ़ॉल्ट READ COMMITTED आइसोलेशन लेवल में पैरेलल ट्रांजैक्शंस एक ही रो (Row) पर अलग-अलग समय पर लॉक एक्वायर करने की कोशिश करते हैं, जिससे डेडलॉक्स पैदा होते हैं।

सॉल्यूशन: स्टेप-बाय-स्टेप इम्प्लीमेंटेशन

इंजीनियरिंग टीम ने डेटाबेस की अखंडता (ACID compliance) बनाए रखते हुए परफॉर्मेंस सुधारने के लिए 4-स्टेप आर्किटेक्चरल अप्रोच लागू की:

स्टेप 1: Atomic Database Updates का उपयोग

सबसे पहले एप्लिकेशन-लेवल चेकिंग को हटाकर डेटाबेस-लेवल एटॉमिक अपडेट लागू किया गया। अलग से SELECT और UPDATE चलाने के बजाय एक सिंगल SQL स्टेटमेंट का इस्तेमाल किया गया:

UPDATE products 
SET stock = stock - 1 
WHERE id = 101 AND stock > 0;

इस क्वेरी का फायदा यह है कि डेटाबेस इंजन रो-लेवल राइट लॉक लगाकर केवल तभी स्टॉक घटाता है जब स्टॉक वास्तव में 0 से अधिक हो। अगर स्टॉक खत्म हो चुका है, तो क्वेरी 0 रो अपडेट करेगी, जिसे बैकएंड आसानी से समझकर 'Out of Stock' का एरर दिखा सकता है।

स्टेप 2: Optimistic Concurrency Control (OCC) जोड़ना

पूरी टेबल या रो को लंबे समय तक लॉक करने से बचने के लिए Optimistic Locking का इस्तेमाल किया गया। इसके लिए टेबल में एक version कॉलम जोड़ा गया:

-- टेबल में वर्जनिंग
UPDATE products 
SET stock = stock - 1, version = version + 1 
WHERE id = 101 AND version = 5;

यदि किसी अन्य यूजर ने मिलीसेकंड के फर्क में वर्जन को 5 से 6 कर दिया, तो अगली क्वेरी तुरंत फेल हो जाएगी और सिस्टम यूजर को बिना डेटाबेस को डेडलॉक में डाले तुरंत रिट्राई (Retry) या आउट ऑफ स्टॉक का मैसेज दे सकेगा।

स्टेप 3: SELECT FOR UPDATE (Pessimistic Locking) कब इस्तेमाल करें?

जिन ऑर्डर्स में मल्टीपल आइटम्स का कॉम्बिनेशन (Cart Checkout) शामिल था, वहां डेडलॉक से बचने के लिए Ordered Pessimistic Locking लागू की गई। इसमें सभी प्रोडक्ट्स को उनकी ID के असेंडिंग ऑर्डर में लॉक किया गया:

SELECT * FROM products 
WHERE id IN (101, 105, 120) 
ORDER BY id ASC 
FOR UPDATE;

आईडी को हमेशा एक ही क्रम में लॉक करने से ट्रांजैक्शंस कभी भी क्रॉस-वेट स्टेट (Deadlock) में नहीं फंसते।

स्टेप 4: Redis In-Memory Cache से डेटाबेस लोड कम करना

डेटाबेस पर हर हिट को रोकने के लिए Redis In-Memory डेटा स्टोर को फ्रंट-लाइन गार्ड बनाया गया:

  • सेल शुरू होने से पहले Redis में की-वैल्यू सेट की गई: SET product_101_stock 100
  • यूजर के क्लिक करते ही Redis का एटॉमिक ऑपरेशन रन किया गया: DECR product_101_stock
  • जब Redis में वैल्यू शून्य से नीचे जाने लगी, तो आगे की रिक्वेस्ट्स को सीधे API गेटवे पर ही रिजेक्ट कर दिया गया। केवल सफल 100 रिक्वेस्ट्स को ही PostgreSQL डेटाबेस में राइट करने के लिए भेजा गया।

रिजल्ट और परफॉर्मेंस सुधार

इस नए आर्किटेक्चर को लोड टेस्टिंग टूल (k6) से 10,000 कंकरेंट यूजर्स के साथ टेस्ट किया गया। नतीजे बेहद शानदार रहे:

  • डेटाबेस डेडलॉक्स: 100% खत्म (0 Deadlock errors)।
  • Over-selling: बिल्कुल शून्य; 100 स्टॉक पर सटीक 100 ऑर्डर प्रोसेस हुए।
  • डेटाबेस CPU यूसेज: 98% से घटकर मात्र 28% पर आ गया।
  • चेकआउट लेटेंसी: 8 सेकंड से घटकर औसतन 45 मिलीसेकंड रह गई।

डेटाबेस इंजीनियर्स के लिए जरूरी सीख

  • एप्लिकेशन कोड के अंदर स्टॉक की वैलिडेशन कभी न करें; हमेशा डेटाबेस के एटॉमिक कंस्ट्रेंट्स या SQL कंडीशन्स पर भरोसा करें।
  • मल्टी-रो ट्रांजैक्शंस में लॉक एक्वायर करते समय रिसोर्स आईडी को हमेशा सॉर्ट (Sort) करें ताकि डेडलॉक की संभावना न रहे।
  • हाई-थ्रूपुट ऑपरेशन्स के लिए रिलेशनल डेटाबेस के आगे Redis जैसे इन-मेमोरी सिस्टम्स का इस्तेमाल बफर की तरह करें।

अक्सर पूछे जाने वाले सवाल (FAQs)

1. Pessimistic Locking और Optimistic Locking में मुख्य अंतर क्या है?

Pessimistic Locking में डेटा पढ़ने के साथ ही रो पर लॉक लगा दिया जाता है ताकि कोई दूसरा उसे बदल न सके। Optimistic Locking में डेटा लॉक नहीं होता, बल्कि अपडेट करते समय चेक किया जाता है कि किसी अन्य प्रोसेस ने डेटा को बदला तो नहीं है। कम टकराव वाले सिस्टम्स के लिए Optimistic लॉकिंग बेहतर स्पीड देती है।

2. डेटाबेस में Deadlock क्या होता है?

डेडलॉक वह स्थिति है जब दो ट्रांजैक्शंस एक-दूसरे द्वारा लॉक किए गए डेटा को एक्सेस करने का इंतजार करते हैं और दोनों में से कोई भी आगे नहीं बढ़ पाता। ऐसी स्थिति में डेटाबेस इंजन किसी एक ट्रांजैक्शन को एरर के साथ रोलबैक (Abort) कर देता है।

3. क्या केवल Redis से पूरी इन्वेंटरी मैनेज की जा सकती है?

नहीं, Redis एक इन-मेमोरी डेटाबेस है जो बहुत तेज है लेकिन इसका इस्तेमाल प्राइमरी सोर्स ऑफ ट्रुथ के बजाय हाई-स्पीड कैशिंग और काउंटर के रूप में किया जाना चाहिए। स्थायी डेटा और फाइनेंशियल रिकॉर्ड्स के लिए ACID-कंप्लायंट रिलेशनल डेटाबेस (जैसे PostgreSQL या MySQL) जरूरी है।

Post a Comment

0 Comments