प्रॉम्प्ट और संदर्भ
यह प्रश्न परीक्षण करता है कि क्या कोई बैकएंड इंजीनियर पेजिनेशन को एक स्टेबल रीड प्रोटोकॉल के रूप में मानता है। अनुरोधों के बीच ऑर्डर, टिप्पणियाँ और लॉग इन्सर्ट, डिलीट या अपडेट किए जा सकते हैं। सादा OFFSET बदलती स्थितियों पर निर्भर करता है और गहरे पेजों पर कई पंक्तियों को स्कैन करता है। ऑर्डरिंग, कर्सर एन्कोडिंग, फ़िल्टर बाइंडिंग, कंसिस्टेंसी सीमाओं, इंडेक्स और फॉरवर्ड/बैकवर्ड सेमेंटिक्स को कवर करें।
इंटरव्यूअर क्या जांचता है
मजबूत उत्तर यह स्पष्ट करते हैं कि क्या उत्पाद को जंप, काउंट या रियल-टाइम परिणामों की आवश्यकता है, और फिर कीसेट/कर्सर या ऑफ़सेट का चयन करते हैं। एक कर्सर ऑर्डरिंग और फ़िल्टर को बांधता है; इसकी सॉर्ट की अद्वितीय (unique), स्थिर (stable) और इंडेक्स की गई होती है। सर्वर इसे साइन करता है और इसकी समाप्ति (expiry) निर्धारित करता है। उत्तर यह समझाता है कि नई पंक्तियाँ अगले पेज पर डुप्लिकेट क्यों नहीं करती हैं, डिलीट परिणामों को कैसे प्रभावित करते हैं, और next_cursor और लिस्ट की समाप्ति स्थिति कैसे लौटाई जाती है।
स्पष्ट करने हेतु प्रश्न
- सॉर्ट क्रम क्या है? क्या सॉर्ट फ़ील्ड बदल सकता है, और अद्वितीय टाई-ब्रेकर क्या है?
- क्या हमें पिछले पेज के नेविगेशन, मनमाने पेज जंप, सटीक काउंट, या केवल फॉरवर्ड इनफिनिट स्क्रॉल की आवश्यकता है?
- क्या रीड्स को फ्रोज़न स्नैपशॉट का उपयोग करना चाहिए या अंततः कंसिस्टेंट (eventually consistent) लाइव लिस्ट की अनुमति देनी चाहिए?
- क्या फ़िल्टर, टेनेंट स्कोप, अनुमतियाँ और सॉर्ट क्रम को कर्सर में बांधा जाना चाहिए? यह कब तक मान्य है?
- डिलीट, सॉफ्ट डिलीट, अनुमति परिवर्तन और क्रॉस-शार्ड रीड्स को कैसे संभाला जाता है?
30-सेकंड का उत्तर ढांचा
“मैं डिसेंडिंग कीसेट क्वेरीज़ के लिए (created_at, id) जैसी एक स्थिर कंपोजिट की का उपयोग करूँगा। कर्सर एक हस्ताक्षरित अपारदर्शी टोकन है जिसमें अंतिम सॉर्ट मान, फ़िल्टर हैश, दिशा और संस्करण शामिल होते हैं। सर्वर इसे मान्य करता है और मिलान वाले कंपोजिट इंडेक्स के विरुद्ध WHERE (created_at,id) < (:time,:id) चलाता है। नई पंक्तियाँ पहले से पढ़े गए विंडो में डाले जाने के बजाय रिफ्रेश करने पर दिखाई देती हैं; डिलीट किसी पेज को छोटा कर सकते हैं लेकिन डुप्लिकेट नहीं बनाते हैं। यदि पूर्ण कंसिस्टेंसी की आवश्यकता है, तो मैं एक स्नैपशॉट सीमा जोड़ूँगा और इसकी लागत समझाऊँगा।”
चरण-दर-चरण विस्तृत उत्तर
चरण 1: लिस्ट सेमेंटिक्स को परिभाषित करें
तय करें कि यह ऑडिट इतिहास है, लाइव फ़ीड है या एडमिन टेबल है। इतिहास को आमतौर पर एक स्थिर सीमा की आवश्यकता होती है; लाइव फ़ीड केवल रिफ्रेश के बाद नई पंक्तियाँ दिखा सकती है। एक ही समय में रियल-टाइम अपडेट, मनमाने जंप, सटीक काउंट और कम लागत का वादा न करें।
चरण 2: एक स्थिर सॉर्ट की चुनें
टाइमस्टैम्प टाई हो सकते हैं या संपादित किए जा सकते हैं, इसलिए टाई-ब्रेकर के रूप में एक अद्वितीय id जोड़ें। डिस्प्ले फ़ील्ड्स से बचें। यदि अपडेट रिकॉर्ड्स को स्थानांतरित कर सकते हैं, तो एक अपरिवर्तनीय (immutable) निर्माण अनुक्रम का उपयोग करें या स्पष्ट रूप से दस्तावेज़ करें कि पंक्तियाँ पेजों के बीच स्थानांतरित हो सकती हैं।
चरण 3: एक अपारदर्शी कर्सर डिज़ाइन करें
सॉर्ट मान, दिशा, फ़िल्टर हैश, API संस्करण और समाप्ति शामिल करें। इसे साइन करें या सर्वर-साइड स्टोर करें; Base64 केवल एन्कोडिंग है, सुरक्षा नहीं। फ़िल्टर बदलने पर चुपचाप असंबंधित पेज लौटाने के बजाय कर्सर को अस्वीकार करें।
चरण 4: कीसेट क्वेरी लिखें
डिसेंडिंग (created_at,id) के लिए, अगला पेज उसी कंपोजिट इंडेक्स के साथ created_at < t OR (created_at = t AND id < id0) का उपयोग करता है। क्वेरी को पैरामीटरयुक्त करें, limit को सीमित करें, और कभी भी कर्सर मानों को सीधे SQL में संयोजित (concatenate) न करें।
चरण 5: राइट्स और डिलीट को संभालें
पेज एक के बाद इन्सर्ट की गई पंक्तियाँ पेज दो में दिखाई नहीं देनी चाहिए; वे रिफ्रेश के बाद दिखाई देती हैं। हटाई गई पंक्ति पेज को छोटा कर सकती है, जो कि एक स्वीकार्य घोषित सेमेंटिक है। यदि चूक (omissions) अस्वीकार्य हैं, तो स्नैपशॉट सीमा या वर्ज़न रीड का उपयोग करें।
चरण 6: अनुमतियों और फ़िल्टर को बाइंड करें
कर्सर का फ़िल्टर हैश, टेनेंट और ऑथराइजेशन स्कोप अनुरोध से मेल खाना चाहिए। जब अनुमतियाँ सख्त हो जाएँ तो परिणामों की पुनर्गणना करें; पुराना कर्सर एक्सेस कंट्रोल को बायपास नहीं करना चाहिए। क्रॉस-शार्ड कोऑर्डिनेटर स्थानीय कर्सर को मर्ज कर सकते हैं, लेकिन उत्तर में प्रवर्धन (amplification) लागत का उल्लेख होना चाहिए।
चरण 7: प्रतिक्रिया और त्रुटियों को परिभाषित करें
items, next_cursor, has_more, और वैकल्पिक रूप से एक स्नैपशॉट id लौटाएँ। समाप्त या अमान्य कर्सर और बदले हुए फ़िल्टर स्टेबल व्यावसायिक त्रुटियों का उपयोग करते हैं; क्लाइंट कर्सर को साफ़ करता है और पेज एक से पुनरारंभ करता है। SQL या आंतरिक डेटाबेस विफलताओं को उजागर न करें।
चरण 8: समवर्ती परीक्षणों के साथ सत्यापन करें
अनुरोधों के बीच इन्सर्ट, डिलीट, समान टाइमस्टैम्प, फ़िल्टर परिवर्तन, छेड़छाड़ किए गए कर्सर और गहरे पेजों का परीक्षण करें। एक सत्र के लिए कोई डुप्लिकेट न होने, इंडेक्स सीक्स (index seeks) और ऐसी लेटेंसी की पुष्टि करें जो पेज संख्या के साथ रैखिक रूप से न बढ़े। डुप्लिकेट दर, स्किप दर, p95 लेटेंसी और कर्सर त्रुटियों को ट्रैक करें।
क्वेरी स्यूडोकोड
SELECT id, created_at, total
FROM orders
WHERE tenant_id = :tenant
AND (created_at, id) < (:cursor_time, :cursor_id)
ORDER BY created_at DESC, id DESC
LIMIT :page_size;ट्रेड-ऑफ और सीमाएं
| आवश्यकता | विकल्प | लागत |
|---|---|---|
| बड़ा इनफिनिट स्क्रॉल | कीसेट कर्सर | कोई मनमाना पेज जंप नहीं |
| छोटी एडमिन टेबल | ऑफ़सेट | गहरे पेज धीमे और राइट्स के तहत अस्थिर |
| पूर्ण कंसिस्टेंसी | स्नैपशॉट सीमा | स्नैपशॉट स्टोरेज और क्लीनअप |
| सटीक काउंट | अलग या एसिंक्रोनस काउंट | अतिरिक्त कार्य और संभावित पुरानापन (staleness) |
कर्सर पेजिनेशन स्थिति की स्थिरता और क्वेरी दक्षता को हल करता है; यह क्रॉस-पेज व्यावसायिक डिडुप्लिकेशन, अनुमति परिवर्तन, या पंक्तियों को स्थानांतरित करने वाले अपडेट को स्वचालित रूप से हल नहीं करता है। खोज परिणामों को एक क्वेरी संस्करण की भी आवश्यकता होती है; एग्रीगेट्स को पॉइंट-इन-टाइम रीड की आवश्यकता हो सकती है।
रोलआउट योजना और साक्ष्य
एक उच्च-रीड वाली लिस्ट चुनें और वर्तमान डुप्लिकेट, स्किप, डीप-पेज लेटेंसी और काउंट लागत को मापें। एक कवरिंग इंडेक्स जोड़ें, वर्ज़न्ड कर्सर जारी करें, और समवर्ती-राइट परीक्षण और मेट्रिक्स जोड़ें। Django REST framework कर्सर पेजिनेशन को एक अपारदर्शी कर्सर के रूप में प्रलेखित करता है; Hello Interview और TechInterview सामग्री बदलते डेटासेट पर कर्सर/कीसेट स्थिरता पर जोर देती हैं।
पायलट निकास मानदंड
समवर्ती इन्सर्ट/डिलीट परीक्षण घोषित डुप्लिकेट और चूक सेमेंटिक्स को पूरा करते हैं; डीप-पेज p95 स्थिर है; छेड़छाड़ और फ़िल्टर परिवर्तनों को अस्वीकार कर दिया जाता है; क्लाइंट सुरक्षित रूप से पेज एक पर पुनर्प्राप्त होते हैं; और ऑथराइजेशन समीक्षा पुष्टि करती है कि टोकन डेटा लीक नहीं करते हैं।
यह कैसे साबित करें कि लाभ वास्तविक है
समान डेटा आकार और राइट दर पर, ऑफ़सेट और कर्सर p95/p99 लेटेंसी, स्कैन की गई पंक्तियों, डुप्लिकेट दर, चूक दर और डेटाबेस CPU की तुलना करें। कैश प्रभावों को अलग करें ताकि एकल कोल्ड क्वेरी परिणाम तय न करे।
सामान्य गलतियाँ और फॉलो-अप
Base64 को सुरक्षित कर्सर मानना
Base64 केवल एन्कोडिंग है। क्लाइंट id या टेनेंट को बदल सकते हैं। टोकन को साइन करें या स्टोर करें और फ़िल्टर, वर्ज़न और समाप्ति को बाइंड करें।
केवल टाइमस्टैम्प द्वारा सॉर्ट करना
कई पंक्तियाँ एक ही मिलीसेकंड साझा कर सकती हैं, जिससे सीमा अस्पष्ट हो जाती है। एक अद्वितीय टाई-ब्रेकर और मिलान कंपोजिट इंडेक्स जोड़ें।
क्या कोई कर्सर किसी भी पेज पर जंप कर सकता है?
मानक कर्सर मनमाने जंप के लिए डिज़ाइन नहीं किए गए हैं। स्पष्ट कंसिस्टेंसी और लागत के साथ बाउंडेड ऑफ़सेट, प्रीकंप्यूटेड एंकर, या सर्च-इंजन पेजिंग प्रदान करें।
क्या अपडेट किसी पंक्ति को डुप्लिकेट कर सकते हैं?
यदि सॉर्ट फ़ील्ड बदलता है, तो एक पंक्ति पेजों के बीच स्थानांतरित हो सकती है। एक अपरिवर्तनीय निर्माण अनुक्रम या स्नैपशॉट/वर्ज़न सेमेंटिक्स का उपयोग करें और व्यवहार को दस्तावेज़ करें।
आप पिछले पेज का बटन कैसे लागू करते हैं?
पिछले कर्सर का स्टैक रखें या रिवर्स क्वेरी चलाएं और सर्वर पर परिणाम को उलट दें। क्लाइंट को कर्सर के आंतरिक विवरण का अनुमान नहीं लगाना चाहिए।
क्या सटीक कुल काउंट लौटाया जाना चाहिए?
नहीं। इनफिनिट स्क्रॉल को आमतौर पर has_more की आवश्यकता होती है; सटीक काउंट एसिंक्रोनस या अलग हो सकते हैं ताकि प्रत्येक पेज टेबल को स्कैन न करे।