प्रॉम्प्ट और संदर्भ
आप एक बहु-चरणीय (multi-step) ऐप्लिकेशन फ़ॉर्म के ओनर हैं। यूज़र्स टैब बदलते हैं, फ़ोन लॉक करते हैं, वापस नेविगेट करते हैं, या घंटों के लिए पेज छोड़ देते हैं। ब्राउज़र पेज को फ़्रीज़ कर सकता है, इसे bfcache से रीस्टोर कर सकता है, या मेमोरी के दबाव में इसे डिस्कॉर्ड कर सकता है। फ़ॉर्म को नए सर्वर डेटा को ओवरराइट किए बिना ड्राफ़्ट्स को रिकवर करना होगा।
मान लें कि फ़ॉर्म में संवेदनशील लेकिन गैर-विनियमित (non-regulated) डेटा है, यूज़र कई डिवाइसों पर साइन इन कर सकता है, और नेटवर्क एक्सेस रुक-रुक कर आ रहा है। उत्तर में यह स्पष्ट होना चाहिए कि क्या बेस्ट-एफ़र्ट है और क्या आधिकारिक (authoritative) है।
इंटरव्यूअर क्या टेस्ट करता है
- क्या आप visibility, freezing, page discard, और bfcache restoration में अंतर करते हैं।
- क्या आप
unloadयाbeforeunloadको एक विश्वसनीय पर्सिस्टेंस हुक मानने से बचते हैं। - क्या लोकल ड्राफ़्ट्स में ओनरशिप, वर्ज़न, एक्सपायरी और कॉन्फ़्लिक्ट के नियम हैं।
- क्या रिकवरी नए सर्वर डेटा को बिना बताए बदले बिना यूज़र के इरादे को सुरक्षित रखती है।
उत्तर देने से पहले स्पष्टीकरण के लिए प्रश्न
- क्या लोकल ड्राफ़्ट खोना स्वीकार्य है, या प्रॉडक्ट को एक टिकाऊ (durable) रिकवरी गारंटी प्रदान करनी चाहिए? यह तय करता है कि लोकल स्टोरेज एक सुविधा है या केवल एक कैश।
- क्या एक ही ड्राफ़्ट को कई डिवाइसों पर एडिट किया जा सकता है? यदि हाँ, तो सर्वर को एक वर्ज़न या कॉन्फ़्लिक्ट पॉलिसी की आवश्यकता होती है।
- क्या डेटा को लोकली स्टोर करना सुरक्षित है? संवेदनशील फ़ील्ड्स के लिए एन्क्रिप्शन, सेलेक्टिव ओमिशन (छोड़ना), या कोई लोकल पर्सिस्टेंस न होने की आवश्यकता हो सकती है।
- सबमिट कॉन्ट्रैक्ट क्या है? ड्राफ़्ट सेव और फ़ाइनल सबमिट के लिए अलग-अलग आइडम्पोटेंसी (idempotency) और वैलिडेशन नियमों की आवश्यकता होती है।
30-सेकंड उत्तर का फ्रेमवर्क
“मैं सर्वर को आधिकारिक (authoritative) और लोकल ड्राफ़्ट को एक सीमित रिकवरी कैश मानता हूँ। मैं डिबाउंस (debounce) के साथ इनपुट परिवर्तनों पर एक वर्ज़न युक्त ड्राफ़्ट को पर्सिस्ट करता हूँ, और डॉक्यूमेंट के hidden होने पर बेस्ट-एफ़र्ट अपडेट फ़्लश करता हूँ। मैं bfcache रीस्टोरेशन के बाद पुनः वैलिडेट करने के लिए pageshow और फ़्रीज़ के बाद पुराने स्टेट को रीफ़्रेश करने के लिए resume का उपयोग करता हूँ। मैं कभी भी unload पर निर्भर नहीं रहता। प्रत्येक ड्राफ़्ट एक एक्सपायरी, स्कीमा वर्ज़न, बेस सर्वर वर्ज़न और डर्टी फ़ील्ड्स को रिकॉर्ड करता है; कॉन्फ़्लिक्ट्स को चुपचाप ओवरराइट करने के बजाय स्पष्ट रूप से दिखाया या मर्ज किया जाता है।”
स्टेप-बाय-स्टेप डीप डाइव
1. लाइफसाइकिल स्टेट्स को मॉडल करें
visibilitychange पेज को बताता है कि यह hidden हो गया या visible; यह वादा नहीं करता है कि JavaScript चलना जारी रखेगी। ब्राउज़र एक hidden पेज को फ़्रीज़ कर सकता है, और एक डिस्कॉर्ड किया गया पेज कभी भी क्लीनअप कोड नहीं चला सकता है। pagehide और pageshow नेविगेशन और bfcache रीस्टोरेशन का वर्णन करते हैं, जबकि freeze और resume Chromium लाइफसाइकिल ट्रांज़िशन को उजागर करते हैं।
इसलिए यह डिज़ाइन जोखिम से पहले सेव करता है, न कि अंतिम सेकंड के unload के दौरान। visibilitychange से hidden पर, एक छोटा लोकल राइट और एक बेस्ट-एफ़र्ट नेटवर्क फ़्लश शेड्यूल करें। pagehide पर, गैर-ज़रूरी काम बंद करें और एक लोकल चेकपॉइंट रिकॉर्ड करें। pageshow पर, event.persisted की जाँच करें और रीस्टोर किए गए फ़ॉर्म को प्रदर्शित करने से पहले सर्वर वर्ज़न को पुनः वैलिडेट करें।
2. लोकल ड्राफ़्ट को सीमित और वर्ज़न युक्त बनाएं
केवल वे फ़ील्ड स्टोर करें जिन्हें रिकवर करना सुरक्षित हो। एक ड्राफ़्ट रिकॉर्ड में शामिल हो सकता है:
draft_id, user_id, form_schema, base_server_version,
changed_at, expires_at, dirty_fields, valuesस्ट्रक्चर्ड डेटा के लिए IndexedDB और लुकअप के लिए एक छोटे लोकल इंडेक्स का उपयोग करें। राइट्स को डिबाउंस करें ताकि टाइपिंग से प्रत्येक कीस्ट्रोक पर राइट न बने, पेलोड आकार को सीमित करें, और एक्सपायर हो चुके ड्राफ़्ट्स को हटा दें। एक स्कीमा वर्ज़न ऐप को असंगत रिकॉर्ड को वर्तमान डेटा के रूप में पार्स करने के बजाय उसे माइग्रेट या डिस्कॉर्ड करने की अनुमति देता है।
लोकल रिकॉर्ड कोई आधिकारिक स्रोत नहीं है। यह एक यूज़र-डिवाइस कैश है जो गायब हो सकता है, पुराना हो सकता है, डुप्लिकेट हो सकता है, या ब्राउज़र द्वारा हटाया जा सकता है।
3. पेज के hidden होने पर सुरक्षित रूप से फ़्लश करें
जब डॉक्यूमेंट hidden हो जाता है, तो पहले लोकल चेकपॉइंट को कमिट करें, फिर यदि प्रॉडक्ट बैकग्राउंड सेविंग का समर्थन करता है तो एक छोटा ऑथेंटिकेटेड अनुरोध करने का प्रयास करें। अनुरोध ड्राफ़्ट आईडी और उस सर्वर वर्ज़न को ले जाता है जिस पर यह आधारित था। सर्वर इसे केवल तभी स्वीकार करता है जब वर्ज़न मेल खाता है, फिर नया वर्ज़न लौटाता है।
एक लंबे अनुरोध पर नेविगेशन को ब्लॉक न करें। यदि अनुरोध समाप्त नहीं हो सकता है, तो भी लोकल चेकपॉइंट इस डिवाइस की सुरक्षा करता है। सिंक्रोनस unload अनुरोध से बचें: यह नेविगेशन को नुकसान पहुँचाता है और मोबाइल लाइफसाइकिल पाथ्स पर इसकी कोई गारंटी नहीं होती है।
4. bfcache और freeze के बाद रिकवर करें
जब pageshow, persisted के साथ फ़ायर होता है, तो पेज में एक ऐसा स्नैपशॉट हो सकता है जो विज़ुअली पूरा हो लेकिन लॉजिकली पुराना हो। वर्तमान सर्वर वर्ज़न प्राप्त करें, फ़ॉर्म के बेस वर्ज़न के साथ इसकी तुलना करें, और यदि किसी अन्य डिवाइस ने ड्राफ़्ट को बदल दिया है तो एक कॉन्फ़्लिक्ट विकल्प दिखाएं। जब resume फ़ायर होता है, तो सेशन और डेटा को रीफ़्रेश करें जो पेज के फ़्रीज़ रहने के दौरान एक्सपायर हो सकते हैं।
यदि पेज को डिस्कॉर्ड कर दिया गया था, तो फिर से शुरू करने के लिए कोई इन-मेमोरी स्टेट नहीं होता है। अगले लोड पर, एक अन-एक्सपायर्ड लोकल ड्राफ़्ट देखें, इसके बेस वर्ज़न की सर्वर से तुलना करें, और रीस्टोर, डिस्कॉर्ड या मर्ज का विकल्प दें। मर्ज स्वीकार करने से पहले यूज़र को देखना चाहिए कि कौन से मान नए हैं।
5. फ़ेल्योर पाथ्स और प्राइवेसी का परीक्षण करें
टैब स्विचिंग, मोबाइल ऐप स्विचिंग, बैक/फ़ॉरवर्ड नेविगेशन, bfcache रीस्टोर, ब्राउज़र डिस्कॉर्ड, ऑफ़लाइन एडिट्स, कोटा समाप्त होना, स्कीमा माइग्रेशन, एक्सपायर्ड ड्राफ़्ट्स और दो-डिवाइस कॉन्फ़्लिक्ट्स का परीक्षण करें। पुष्टि करें कि नया सर्वर वर्ज़न कभी भी चुपचाप ओवरराइट न हो।
लॉग्स से संवेदनशील फ़ील्ड्स को हटा दें (redact करें)। अकाउंट हटाने पर ड्राफ़्ट्स साफ़ करें, एक छोटी रिटेंशन अवधि का पालन करें, और प्राइवेसी नोटिस में लोकल रिकवरी के बारे में बताएं। रीस्टोर की सफलता, कॉन्फ़्लिक्ट दर, लोकल-राइट फ़ेल्योर और सर्वर-सेव लेटेंसी को मापें; एक "saved" इंडिकेटर को एक ज्ञात चेकपॉइंट का प्रतिनिधित्व करना चाहिए, न कि इस उम्मीद का कि unload हैंडलर चला था।
उच्च गुणवत्ता वाला नमूना उत्तर
मैं सर्वर को वर्ज़न युक्त सोर्स ऑफ़ ट्रुथ बनाऊँगा और रिकवरी के लिए एक सीमित लोकल ड्राफ़्ट रखूँगा। इनपुट परिवर्तनों को एक स्कीमा वर्ज़न, एक्सपायरी, डर्टी-फ़ील्ड सेट और बेस के रूप में उपयोग किए गए सर्वर वर्ज़न के साथ IndexedDB में डिबाउंस किया जाता है। जब visibilitychange hidden की रिपोर्ट करता है, तो मैं चेकपॉइंट लिखता हूँ और एक छोटे ऑथेंटिकेटेड सेव का प्रयास करता हूँ, लेकिन मैं नेविगेशन को ब्लॉक नहीं करता या unload पर भरोसा नहीं करता।
pageshow पर, विशेष रूप से जब इवेंट bfcache से आया हो, तो मैं रीस्टोर किए गए फ़ॉर्म पर भरोसा करने से पहले सर्वर वर्ज़न को फिर से फ़ेच करता हूँ। resume पर, मैं डेटा और सेशन स्टेट को रीफ़्रेश करता हूँ जो फ़्रीज़ रहने के दौरान एक्सपायर हो सकते हैं। एक डिस्कॉर्ड किया गया पेज एक नए लोड से शुरू होता है और अन-एक्सपायर्ड लोकल ड्राफ़्ट की पेशकश करता है। यदि वर्ज़न भिन्न होते हैं, तो मैं एक कॉन्फ़्लिक्ट UI या फ़ील्ड-लेवल मर्ज दिखाता हूँ; मैं कभी भी नई सर्वर कॉपी को चुपचाप ओवरराइट नहीं करता हूँ। परीक्षण मोबाइल डिस्कॉर्ड, ऑफ़लाइन एडिट्स, कोटा सीमा और दो-डिवाइस कॉन्फ़्लिक्ट्स को कवर करते हैं।
सामान्य गलतियाँ
- त्रुटि: केवल
beforeunloadमें सेव करना → यह क्यों विफल होता है: मोबाइल ब्राउज़र इसे फ़ायर किए बिना किसी पेज को समाप्त कर सकते हैं और यह bfcache को ब्लॉक कर सकता है → समाधान: इनपुट और hidden ट्रांज़िशन पर चेकपॉइंट बनाएं। - त्रुटि: दृश्यमान (visible) bfcache पेज को ताज़ा मानना → यह क्यों विफल होता है: स्नैपशॉट में पुराना सर्वर स्टेट हो सकता है → समाधान:
pageshowपर पुनः वैलिडेट करें और वर्ज़नों की तुलना करें। - त्रुटि: लोकल स्टोरेज को आधिकारिक मानना → यह क्यों विफल होता है: इसे साफ़ किया जा सकता है, यह पुराना हो सकता है, या अनुपलब्ध हो सकता है → समाधान: सर्वर वर्ज़न का उपयोग करें और लोकल डेटा को रिकवरी कैंडिडेट के रूप में प्रस्तुत करें।
- त्रुटि: किसी नए वर्ज़न पर पूरे फ़ॉर्म को पुनः प्रयास करना → यह क्यों विफल होता है: यह असंबंधित एडिट्स को ओवरराइट करता है → समाधान: आशावादी (optimistic) वर्ज़न के साथ डर्टी फ़ील्ड्स भेजें और कॉन्फ़्लिक्ट्स को हल करें।
- त्रुटि: डिबगिंग के लिए ड्राफ़्ट मानों को लॉग करना → यह क्यों विफल होता है: संवेदनशील सामग्री टेलीमेट्री में लीक हो जाती है → समाधान: केवल आईडी, वर्ज़न, आकार और परिणामों को लॉग करें।
फॉलो-अप प्रश्न और उत्तर
क्या मुझे localStorage या IndexedDB का उपयोग करना चाहिए?
localStorage का उपयोग केवल बहुत छोटे, सिंक्रोनस मेटाडेटा के लिए करें। IndexedDB संरचित (structured) ड्राफ़्ट्स, बड़े पेलोड और एसिंक्रोनस राइट्स के लिए उपयुक्त है। दोनों में से कोई भी टिकाऊपन (durability) या क्रॉस-डिवाइस अधिकार नहीं देता है; दोनों को एक्सपायरी, कोटा हैंडलिंग, स्कीमा वर्ज़निंग और प्राइवेसी नियमों की आवश्यकता होती है।
क्या होगा यदि यूज़र दो डिवाइसों पर ऑफ़लाइन एडिट करता है?
प्रत्येक सेव को एक बेस सर्वर वर्ज़न और डिवाइस या ड्राफ़्ट आईडी दें। कनेक्टिविटी वापस आने पर, केवल मैचिंग वर्ज़न को स्वीकार करें, फिर फ़ील्ड-लेवल मर्ज दिखाएं या यूज़र को चुनने दें। लास्ट-राइट-विन्स (last-write-wins) पॉलिसी केवल तभी स्वीकार्य है जब प्रॉडक्ट स्पष्ट रूप से साइलेंट लॉस को स्वीकार करता है और फ़ील्ड्स स्वतंत्र हैं।
क्या कोई सर्विस वर्कर अंतिम सेव की गारंटी दे सकता है?
नहीं। एक सर्विस वर्कर पुनः प्रयास (retry) डिलीवरी में सुधार कर सकता है, लेकिन इसे रोका जा सकता है, नेटवर्क एक्सेस खो सकता है, या किसी विशेष फ़्लो में अनुपलब्ध हो सकता है। UI को एक कन्फ़र्म किए गए चेकपॉइंट की रिपोर्ट करनी चाहिए, और सर्वर को पुनः प्रयासों को आइडम्पोटेंट बनाना चाहिए। लोकल ड्राफ़्ट उस डिवाइस के लिए फ़ॉलबैक बना रहता है जो अनुरोध पूरा नहीं कर सकता।