Request Deduplication एक तंत्र है जो समान समानांतर अनुरोधों को एक में जोड़ता है, ताकि डेटा स्रोत को दर्जनों के बजाय केवल एक कॉल प्राप्त हो। मोबाइल एप्लिकेशन में, डिडुप्लिकेशन विशेष रूप से महत्वपूर्ण है: कई स्क्रीन एक साथ एक ही उपयोगकर्ता प्रोफ़ाइल या उत्पाद सूची का अनुरोध कर सकती हैं। Square Engineering (2024) के अनुसार, डिडुप्लिकेशन के कार्यान्वयन ने सर्वर लॉजिक को बदले बिना उनके API लोड को 30% कम कर दिया।
मुख्य बातें
Request Deduplication एक तकनीक है जो एक ही समय विंडो में एक ही डेटा स्रोत पर कई समान अनुरोधों को निष्पादित होने से रोकती है। 10 समान HTTP अनुरोध भेजने के बजाय, सिस्टम एक भेजता है, जबकि अन्य 9 उसके परिणाम की प्रतीक्षा करते हैं।
डुप्लिकेट अनुरोधों की समस्या विशेष रूप से अवस्था-आधारित आर्किटेक्चर (MVVM, MVI, Redux) वाले मोबाइल एप्लिकेशन में गंभीर है। जब कई प्रेक्षक थोड़े समय के भीतर समान डेटा की सदस्यता लेते हैं, तो प्रत्येक अपना स्वयं का अनुरोध शुरू करता है, जिससे अतिरिक्त भार पैदा होता है। Uber Engineering (2024) के अनुसार, Uber मोबाइल क्लाइंट में सभी अनुरोधों का 18% तक डुप्लिकेट होते हैं, और क्लाइंट-साइड डिडुप्लिकेशन ने उनकी संख्या को 4 गुना कम कर दिया।
डिडुप्लिकेशन कैशिंग के समान नहीं है। कैश निष्पादन के बाद अनुरोध परिणाम संग्रहीत करता है। डिडुप्लिकेशन उनके निष्पादन से पहले और दौरान अतिरिक्त अनुरोधों को रोकता है। अनुरोध पूरा होने के बाद, कैशिंग प्रभावी हो जाती है।
class DeduplicatorT(
private val source: suspend () -> T
) {
private val inFlight = ConcurrentHashMap<String, Deferred<T>>()
suspend fun get(key: String): T = inFlight.getOrPut(key) {
async {
source().also { inFlight.remove(key) }
}
}.await()
}
यह Kotlin क्लास गारंटी देता है कि प्रति कुंजी केवल एक कोरूटीन चलता है। समान कुंजी वाले सभी समवर्ती कॉल एक ही Deferred की प्रतीक्षा करते हैं। पूरा होने के बाद, कुंजी हटा दी जाती है, और अगला अनुरोध सामान्य रूप से निष्पादित होता है।
सर्वर लोड कम करना पहला और सबसे स्पष्ट कारण है। प्रत्येक डुप्लिकेट अनुरोध सर्वर संसाधनों का उपभोग करता है: CPU, मेमोरी, डेटाबेस कनेक्शन। लाखों उपकरणों के पैमाने पर, 10–15% डुप्लिकेट अनुरोध भी महत्वपूर्ण भार पैदा करते हैं, जिसके लिए अतिरिक्त सर्वर की आवश्यकता होती है।
बैटरी और डेटा खपत कम करना — मोबाइल डिवाइस पर प्रत्येक HTTP अनुरोध रेडियो मॉड्यूल ऊर्जा की खपत करता है। Google I/O (2025) के अनुसार, एक असफल या डुप्लिकेट अनुरोध एक नेटवर्क सत्र की 15% तक ऊर्जा खर्च कर सकता है। डिडुप्लिकेशन रेडियो मॉड्यूल सक्रियणों की संख्या कम करता है, जिससे डिवाइस की बैटरी लाइफ बढ़ती है।
डेटा विरोध से बचना — यदि दो डुप्लिकेट अनुरोध स्थानीय स्टोरेज में डेटा लिखते हैं, तो रेस कंडीशन हो सकती है: दूसरा अनुरोध पहले के परिणाम को पुराने डेटा से ओवरराइट कर सकता है। डिडुप्लिकेशन गारंटी देता है कि स्थानीय स्टोरेज में लिखना केवल एक बार होता है, जिससे रेस समाप्त हो जाती हैं।
बेहतर UX — उपयोगकर्ता समान डेटा के लिए कई लोडिंग संकेतक नहीं देखता है। UI स्थिति (लोड हो रहा है / सफलता / त्रुटि) कई प्रतिस्पर्धी अनुरोधों के बजाय सत्य के एकल स्रोत द्वारा प्रबंधित की जाती है।
Memoization किसी फ़ंक्शन के परिणाम को उसके निष्पादन के दौरान कैश करना है। यदि कोई फ़ंक्शन पहले से ही समान तर्कों के साथ चल रहा है, तो नया कॉल दूसरी प्रक्रिया शुरू नहीं करता बल्कि पहले का परिणाम प्राप्त करता है। यह इन-प्रोसेस परिदृश्यों के लिए डिडुप्लिकेशन का सबसे सरल रूप है।
मोबाइल एप्लिकेशन में एक विशिष्ट कार्यान्वयन Deferred या Promise के लिए कुंजियों का HashMap है। कुंजी आमतौर पर अनुरोध URL स्ट्रिंग या पैरामीटर का संयोजन होती है। प्रविष्टि का जीवनकाल पहले अनुरोध से लेकर प्रतिक्रिया पूरी होने तक होता है। Dropbox Engineering (2024) के अनुसार, Dropbox मोबाइल क्लाइंट में मेमोइज़ेशन ने डुप्लिकेट API अनुरोधों को 40% कम कर दिया।
त्रुटिपूर्ण डिडुप्लिकेशन — एक खतरनाक गलती: यदि त्रुटि के बाद कुंजी नहीं हटाई जाती है, तो सभी बाद के अनुरोध हमेशा के लिए वही त्रुटि लौटाएंगे। सही कार्यान्वयन को Error और Failure को संभालना चाहिए, कैश साफ़ करना चाहिए और पुनः प्रयास की अनुमति देनी चाहिए।
class MemoizedLoaderT(
private val loader: suspend () -> T
) {
private var cachedResult: Result<T>? = null
suspend fun get(): T = cachedResult ?.getOrThrow() ?: run {
loader().let {
Result.success(it)
}.also { cachedResult = it }
}.await()
}
MemoizedLoader सही त्रुटि प्रबंधन के लिए Result<T> का उपयोग करता है: सफलता पर — कैश करता है, त्रुटि पर — पुनः प्रयास की अनुमति देता है। यह दृष्टिकोण सुनिश्चित करता है कि अस्थायी नेटवर्क विफलता बाद के अनुरोधों को अवरुद्ध नहीं करती है।
Request Merging एक तकनीक है जिसमें एक ही स्रोत के कई अलग-अलग अनुरोधों को एक समूह में एकत्र किया जाता है और एक बैच अनुरोध के रूप में भेजा जाता है। डिडुप्लिकेशन के विपरीत, यहाँ अनुरोध समान नहीं हैं — वे पैरामीटर में भिन्न हैं लेकिन एक ही संसाधन को संबोधित करते हैं।
एक विशिष्ट परिदृश्य: 5 एप्लिकेशन स्क्रीन विभिन्न उपयोगकर्ताओं की प्रोफ़ाइल का अनुरोध करती हैं। /api/users/1, /api/users/2 आदि पर 5 अलग-अलग अनुरोध भेजने के बजाय, सिस्टम 20 मि.से. प्रतीक्षा करता है, सभी ID एकत्र करता है, और एक अनुरोध /api/users?ids=1,2,3,4,5 भेजता है। विंडो टाइमआउट मुख्य पैरामीटर है: बहुत लंबी विंडो UX को खराब करती है, बहुत छोटी — पर्याप्त अनुरोध एकत्र नहीं कर पाती।
Netflix Engineering (2023) के अनुसार, GraphQL एग्रीगेटर BFF (बैकएंड फॉर फ्रंटएंड) में, अनुरोध मर्जिंग ने परतों के बीच HTTP कॉल की संख्या को 65% और अतिरिक्त RTT को समाप्त करके औसत प्रतिक्रिया समय को 120 मि.से. कम कर दिया। एसिंक्रोनस विंडो (debounce) कोरूटीन या RxJava के माध्यम से मानक कार्यान्वयन है।
class BatchMergerT {
private val pending = ConcurrentLinkedQueue<Pair<String, CompletableDeferred<T>>>()
suspend fun get(id: String): T = suspendCoroutine { cont ->
pending.add(Pair(id, cont))
scheduleFlush()
}
}
यह मिक्सिन प्रत्येक अनुरोध को निलंबित करने के लिए suspendCoroutine और समूह एकत्र करने के लिए 30 मि.से. विंडो का उपयोग करता है। टाइमर समाप्त होने के बाद, सभी एकत्रित ID एक बैच अनुरोध में भेजी जाती हैं, और प्रत्येक कोरूटीन को अपना परिणाम मिलता है।
DataLoader एक लाइब्रेरी है (मूल रूप से JavaScript/GraphQL के लिए) जो सर्वर साइड पर बैचिंग और मेमोइज़ेशन लागू करती है। यह एक इवेंट लूप टिक के भीतर एक ही डेटा स्रोत के सभी अनुरोधों को समूहित करती है और उन्हें एक कॉल के साथ निष्पादित करती है। DataLoader का व्यापक रूप से GraphQL के साथ उपयोग किया जाता है लेकिन इसे किसी भी REST एप्लिकेशन में लागू किया जा सकता है।
यह कैसे काम करता है: एक माइक्रोटास्क के भीतर सभी loader.load(id) कॉल को ID की एक सरणी में एकत्र किया जाता है और बैच फ़ंक्शन को पास किया जाता है। परिणाम प्राप्त करने के बाद, प्रत्येक ID को अपना सरणी तत्व मिलता है। DataLoader में कैशिंग केवल एक HTTP अनुरोध के भीतर काम करती है — अगले अनुरोध पर कैश साफ़ हो जाती है, जो डेटा की ताज़गी सुनिश्चित करती है।
Meta Engineering (2024) के अनुसार, Facebook के GraphQL लेयर में DataLoader लागू करने से N+1 समस्या समाप्त हो गई, जिससे प्रति सामान्य पृष्ठ डेटाबेस क्वेरी 200 से घटकर 10 हो गई। बैच शेड्यूलिंग — DataLoader का मुख्य नवाचार — समूहीकरण को अनुकूलित करने के लिए process.nextTick (Node.js) या DispatchQueue.main (iOS) का उपयोग करता है।
Memoization एकल प्रक्रिया (मोबाइल ऐप, माइक्रोसर्विस) के लिए इष्टतम है। लागू करने में सरल और समान समानांतर कॉल के लिए प्रभावी। नुकसान — यह प्रक्रियाओं या उपकरणों के बीच काम नहीं करता।
Request Merging BFF लेयर या एग्रीगेटर सेवा के लिए उपयुक्त है। सर्वर पर बैच एंडपॉइंट समर्थन की आवश्यकता है। सबसे अच्छा विकल्प जब फ्रंटएंड एक ही प्रकार के विभिन्न डेटा के लिए कई छोटे अनुरोध करता है।
DataLoader GraphQL सर्वर के लिए मानक है। यह स्वचालित रूप से N+1 समस्या को हल करता है और मैन्युअल कैश कॉन्फ़िगरेशन की आवश्यकता नहीं है। किसी भी सर्वर के लिए अनुशंसित जिसमें GraphQL लेयर है।
डिडुप्लिकेशन के साथ HTTP कैश — OkHttp (Android) या URLSession (iOS) स्तर पर, इंटरसेप्टर या डेलीगेट के माध्यम से डिडुप्लिकेशन कॉन्फ़िगर किया जा सकता है। OkHttp CacheInterceptor एक कस्टम इंटरसेप्टर है जो जाँचता है कि समान URL वाला अनुरोध पहले से चल रहा है या नहीं और उन्हें मर्ज कर देता है। यह विधि व्यवसाय लॉजिक स्तर से नीचे काम करती है और फीचर कोड बदले बिना सभी एप्लिकेशन अनुरोधों को कवर करती है।
अक्सर पूछे जाने वाले प्रश्न
डिडुप्लिकेशन डुप्लिकेट अनुरोध को निष्पादित होने से रोकता है जबकि पहला अभी भी चल रहा है। कैशिंग निष्पादन के बाद परिणाम सहेजती है। वे एक दूसरे के पूरक हैं: डिडुप्लिकेशन लोडिंग के दौरान बार-बार अनुरोधों से बचाता है, कैश बाद में बार-बार अनुरोधों से बचाता है।
यदि डिडुप्लिकेशन कुंजी गलत तरीके से चुनी गई है। उदाहरण के लिए, यदि सभी उपयोगकर्ता एक कुंजी का उपयोग करते हैं, तो पहला अनुरोध बाकी सभी को अवरुद्ध कर देगा। कुंजी विशिष्ट होनी चाहिए: URL, पैरामीटर, उपयोगकर्ता ID शामिल करें। डिडुप्लिकेशन मीट्रिक में अनुरोधों की वास्तविक आवृत्ति को छिपाकर सर्वर समस्याओं को भी छुपा सकता है।
उपयोगकर्ता परिदृश्यों के लिए इष्टतम विंडो 20–50 मि.से. है। यह अनुरोधों का समूह एकत्र करने के लिए पर्याप्त है, लेकिन उपयोगकर्ता को देरी नोटिस करने के लिए पर्याप्त नहीं है। पृष्ठभूमि संचालन (लॉग, एनालिटिक्स) के लिए, विंडो को 200–500 मि.से. तक बढ़ाया जा सकता है। अनुभवजन्य नियम: विंडो एक अनुरोध के निष्पादन समय के 10% से अधिक नहीं होनी चाहिए।
हाँ, वही सिद्धांत लागू होता है: यदि ऐप के कई भाग एक ही WebSocket चैनल की सदस्यता लेते हैं, तो डिडुप्लिकेटर एक एकल कनेक्शन खोलता है और सभी सब्सक्राइबर्स को संदेश प्रसारित करता है। क्लाइंट पर WebSocket संदेशों को डिडुप्लिकेट करने के लिए RxJava Share या Kotlin SharedFlow आदर्श उपकरण हैं।
Android के लिए MockWebServer (OkHttp) या iOS के लिए OHHTTPStubs का उपयोग करें। समान पैरामीटर के साथ 10 समानांतर अनुरोध चलाएँ और जाँच करें कि सर्वर को केवल एक कॉल प्राप्त हुई। CountDownLatch या coroutineScope परीक्षण में समानांतर कॉल को सिंक्रोनाइज़ करने में मदद करते हैं।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें