Node.js बैकएंड विकास बूटकैंप · पाठ

कैश स्टैम्पीड और थंडरिंग हर्ड रोकना

अनुरोध समेकन, जिटरयुक्त TTL और संभाव्य शीघ्र समाप्ति से स्टैम्पीड को कम कीजिए।

पाठ 4, कुल 4 में से13 चरण

कैश स्टैम्पीड और थंडरिंग हर्ड रोकना, CoddyKit पर Node.js बैकएंड विकास बूटकैंप का एक निःशुल्क पाठ है। यह 4 में से 4वाँ पाठ है। आप नीचे पूरा पाठ निःशुल्क पढ़ सकते हैं—फिर अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर के साथ ब्राउज़र में इसका व्यावहारिक अभ्यास कर सकते हैं। यह Node.js बैकएंड विकास बूटकैंप सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

कैश स्टैम्पीड क्या है

कैश स्टैम्पीड (जिसे थंडरिंग हर्ड भी कहा जाता है) तब होता है जब लोकप्रिय कैश किया गया मान समाप्त हो जाता है और बहुत-से समवर्ती अनुरोध ठीक उसी क्षण कैश में विफल हो जाते हैं। वे सभी उसी मान की फिर से गणना करने के लिए डेटाबेस की ओर दौड़ते हैं।

  • एक कुंजी समाप्त होती है → 5,000 प्रगतिरत अनुरोधों को कैश में मान नहीं मिलता
  • 5,000 एक जैसी क्वेरी एक साथ डेटाबेस पर पहुँचती हैं
  • DB संतृप्त हो जाता है, विलंबता बढ़ जाती है और कभी-कभी पूरा सिस्टम ठप हो जाता है

विडंबना यह है कि कैश डेटाबेस की रक्षा के लिए मौजूद है, लेकिन समाप्ति का क्षण सबसे खतरनाक क्षण बन जाता है।

कोड में समस्या देखना

यह कैश-ऐज़ाइड रीड का एक पारंपरिक सरल उदाहरण है। कम ट्रैफ़िक में यह ठीक काम करता है, लेकिन भारी समवर्तीता में हर कैश-विफलता अपनी loadFromDb कॉल शुरू कर देती है।

ध्यान दें कि एक ही कुंजी के लिए 1,000 कॉलर को एक साथ loadFromDb चलाने से रोकने वाला कुछ भी नहीं है।

async function getUser(redis, db, id) {
  const key = 'user:' + id;
  const cached = await redis.get(key);
  if (cached !== null) {
    return JSON.parse(cached);
  }
  // STAMPEDE RISK: every concurrent miss runs this
  const user = await db.loadUser(id);
  await redis.set(key, JSON.stringify(user), 'EX', 60);
  return user;
}

रणनीति 1: अनुरोधों का संयोजन

अनुरोधों का संयोजन (जिसे सिंगल-फ़्लाइट या प्रगतिरत अनुरोधों का डुप्लिकेशन हटाना भी कहते हैं) का अर्थ है: जब कई कॉलर एक ही समय में एक ही कुंजी चाहते हों, तो उनमें से केवल एक वास्तव में मान की गणना करे। बाकी उसी प्रॉमिस की प्रतीक्षा करें।

  • key → pending promise का इन-मेमोरी मैप रखें
  • पहला कॉलर काम शुरू करके उसका प्रॉमिस संग्रहीत करे
  • समवर्ती कॉलर लंबित प्रॉमिस ढूँढकर उसकी प्रतीक्षा करें
  • उसके पूरा होने पर प्रविष्टि हटा दें

इससे हर प्रक्रिया में N डेटाबेस कॉल घटकर 1 रह जाती हैं।

सिंगल-फ़्लाइट लागू करना

यह न्यूनतम, निर्भरता-मुक्त सिंगल-फ़्लाइट सहायक है। एक ही कुंजी के लिए हर समवर्ती कॉलर एक ही मूल प्रॉमिस साझा करता है। finally ब्लॉक प्रविष्टि साफ़ कर देता है, ताकि अगली बार फिर गणना हो सके।

यह स्निपेट पूरी तरह स्व-निहित है और धीमे काम का अनुकरण करके संयोजन का व्यवहार दिखाता है।

function createSingleFlight() {
  const inFlight = new Map();
  return function run(key, fn) {
    if (inFlight.has(key)) {
      return inFlight.get(key);
    }
    const p = Promise.resolve()
      .then(fn)
      .finally(() => inFlight.delete(key));
    inFlight.set(key, p);
    return p;
  };
}

async function main() {
  const flight = createSingleFlight();
  let dbCalls = 0;
  const load = () => new Promise(res => {
    dbCalls++;
    setTimeout(() => res('value-' + dbCalls), 50);
  });

  // 5 concurrent callers, same key
  const results = await Promise.all(
    Array.from({ length: 5 }, () => flight('user:42', load))
  );
  console.log('results:', results);
  console.log('actual db calls:', dbCalls);
}

main();

संयोजन की सीमा: यह प्रति-प्रक्रिया होता है

इन-मेमोरी सिंगल-फ़्लाइट केवल एक Node.js प्रक्रिया के भीतर डुप्लिकेट काम हटाता है। यदि आप लोड बैलेंसर के पीछे 20 इंस्टेंस चलाते हैं, तो प्रत्येक समाप्त कुंजी के लिए अब भी 20 एक साथ DB कॉल तक हो सकती हैं — हर प्रक्रिया के लिए एक।

  • हर इंस्टेंस के अंदर N अनुरोधों को 1 करने में बढ़िया
  • बड़े क्षैतिज समूहों के लिए अकेले पर्याप्त नहीं
  • प्रक्रियाओं के बीच सुरक्षा के लिए Redis में वितरित लॉक चाहिए

पूरे कवरेज के लिए संयोजन (सस्ता, स्थानीय) को वितरित लॉक या संभाव्य समाप्ति (पूरे क्लस्टर में) के साथ मिलाएँ।

रणनीति 2: वितरित लॉक

वितरित लॉक पूरे इंस्टेंस समूह में किसी एक इंस्टेंस को पुनर्गणना करने का अधिकार जीतने देता है। Redis SET key value NX PX ttl का उपयोग करें: यह कुंजी को केवल तब सेट करता है जब वह मौजूद न हो, और यह काम परमाण्विक होता है।

  • विजेता पुनर्गणना करके कैश में मान फिर भरता है
  • हारने वाले कैश को प्रतीक्षा करके फिर आज़मा सकते हैं या पुराना डेटा दे सकते हैं
  • लॉक पर हमेशा TTL सेट करें, ताकि क्रैश हुआ विजेता कुंजी को हमेशा के लिए डेडलॉक न कर सके

यह प्रक्रिया के भीतर होने वाले संयोजन का प्रक्रियाओं के बीच वाला पूरक है।

async function getWithLock(redis, db, id) {
  const key = 'user:' + id;
  const cached = await redis.get(key);
  if (cached !== null) return JSON.parse(cached);

  const lockKey = 'lock:' + key;
  const token = Math.random().toString(36).slice(2);
  // NX = only if absent, PX = lock TTL in ms
  const won = await redis.set(lockKey, token, 'NX', 'PX', 5000);

  if (won === 'OK') {
    try {
      const user = await db.loadUser(id);
      await redis.set(key, JSON.stringify(user), 'EX', 60);
      return user;
    } finally {
      // release only if we still own the lock
      if (await redis.get(lockKey) === token) await redis.del(lockKey);
    }
  }
  // Lost the race: briefly wait, then read the now-fresh cache
  await new Promise(r => setTimeout(r, 50));
  const retry = await redis.get(key);
  return retry !== null ? JSON.parse(retry) : db.loadUser(id);
}

रणनीति 3: जिटर वाले TTL

यदि आप एक लूप में 10,000 कुंजियों को एक ही TTL के साथ गर्म करते हैं, तो वे सभी उसी सेकंड समाप्त होंगी — कई कुंजियों में एक साथ समकालिक स्टैम्पीड। TTL जिटर हर TTL में छोटा यादृच्छिक ऑफ़सेट जोड़कर समाप्तियों को अलग-अलग समय पर फैलाता है।

  • आधार TTL 300s → वास्तविक TTL 270–330s, हर कुंजी के लिए यादृच्छिक
  • समाप्तियाँ एक ही टिक पर समूहित होने के बजाय एक समय-सीमा में फैल जाती हैं
  • सस्ता, बिना समन्वय के और हर दूसरी रणनीति के साथ संयोज्य

कई संबंधित कुंजियों को एक साथ भरते या रिफ़्रेश करते समय हमेशा TTL में जिटर जोड़ें।

// Add +/- jitterPct random spread around a base TTL
function jitteredTtl(baseSeconds, jitterPct = 0.1) {
  const spread = baseSeconds * jitterPct;
  const offset = (Math.random() * 2 - 1) * spread; // -spread..+spread
  return Math.max(1, Math.round(baseSeconds + offset));
}

// Demo: 5 keys warmed together get different lifetimes
for (let i = 0; i < 5; i++) {
  console.log('key' + i + ' ttl =', jitteredTtl(300));
}

रणनीति 4: संभाव्य प्रारंभिक समाप्ति

संभाव्य प्रारंभिक समाप्ति (XFetch एल्गोरिदम) किसी कुंजी के वास्तव में समाप्त होने से पहले उसे रिफ़्रेश करती है; समाप्ति निकट आने पर इसकी संभावना बढ़ती जाती है। इस तरह एक भाग्यशाली अनुरोध पहले पुनर्गणना करता है, जबकि बाकी सभी को पुराना मान मिलता रहता है।

पारंपरिक नियम के अनुसार पुनर्गणना तब होती है जब:

  • now - delta * beta * ln(random()) ≥ expiry

यहाँ delta पिछली पुनर्गणना में लगा समय है और beta (डिफ़ॉल्ट 1) आक्रामकता को समायोजित करता है। जिन कुंजियों की गणना धीमी है (बड़ा delta), वे पहले रिफ़्रेश होना शुरू करती हैं, जो ठीक वही है जो आप चाहते हैं।

कोड में XFetch

XFetch का उपयोग करने के लिए आप मान के साथ पुनर्गणना की अवधि (delta) और निरपेक्ष समाप्ति समय भी संग्रहीत करते हैं। हर रीड पर संभाव्य जाँच करते हैं। अधिकांश कॉलर कैश किया हुआ मान देते हैं; कभी-कभी कोई एक समाप्ति से पहले रिफ़्रेश कर देता है।

यह स्वतंत्र डेमो दिखाता है कि समाप्ति के निकट पहुँचने पर प्रारंभिक रिफ़्रेश की संभावना 1 की ओर बढ़ती है।

function shouldRecompute(deltaMs, expiryMs, now, beta = 1) {
  // XFetch: earlier refresh as we near expiry, scaled by recompute cost
  const xfetch = now - deltaMs * beta * Math.log(Math.random());
  return xfetch >= expiryMs;
}

const now = Date.now();
const delta = 200;          // last recompute took 200ms
const expiry = now + 1000;  // value expires in 1s

let refreshes = 0;
for (let i = 0; i < 1000; i++) {
  // sample 'now' uniformly across the key's lifetime
  const t = now + Math.random() * 1000;
  if (shouldRecompute(delta, expiry, t)) refreshes++;
}
console.log('early refreshes out of 1000 reads:', refreshes);

रणनीतियों को मिलाना

ये तकनीकें प्रतिस्पर्धी नहीं, बल्कि पूरक परतें हैं। उत्पादन कैश रीड में अक्सर कई तकनीकें साथ रखी जाती हैं:

  • संयोजन — हर प्रक्रिया के भीतर दोहराए गए काम को समेटना
  • वितरित लॉक — पूरे इंस्टेंस समूह में एक पुनर्गणना
  • जिटर वाला TTL — एक साथ होने वाली समाप्तियों को असमकालिक बनाना
  • संभाव्य प्रारंभिक समाप्ति — हॉट कुंजियों के कैश-विफल होने से पहले उन्हें रिफ़्रेश करना

जिटर + संयोजन से शुरुआत करें (सस्ता, बिना समन्वय के)। सबसे अधिक उपयोग वाली और महँगी कुंजियों के लिए लॉक या XFetch जोड़ें।

स्टेल-व्हाइल-रीवैलिडेट

इसे एक साथ जोड़ने वाला व्यावहारिक पैटर्न है: स्टेल-व्हाइल-रीवैलिडेट (SWR)। दो जीवन-अवधियाँ रखें — एक छोटी ताज़ा विंडो और एक लंबी पुरानी विंडो। पुरानी विंडो के दौरान पुराने मान को तुरंत दें और पृष्ठभूमि में रिफ़्रेश शुरू करें (सिंगल-फ़्लाइट द्वारा डुप्लिकेट हटाकर)।

  • उपयोगकर्ताओं को लगभग कभी ठंडी पुनर्गणना की प्रतीक्षा नहीं करनी पड़ती
  • रिफ़्रेश अनुरोध के महत्वपूर्ण पथ से अलग, केवल एक बार चलता है
  • जिटर के साथ मिलाएँ, ताकि पुरानी विंडो एक साथ समाप्त न हों

SWR कठिन कैश-विफलता (सभी प्रतीक्षा करते हैं) को आसान कैश-विफलता (एक पृष्ठभूमि रिफ़्रेश, सभी को तुरंत मान) में बदल देता है।

async function swrGet(redis, db, id, freshSec = 60, staleSec = 600) {
  const key = 'user:' + id;
  const raw = await redis.get(key);
  if (raw) {
    const { value, storedAt } = JSON.parse(raw);
    const ageSec = (Date.now() - storedAt) / 1000;
    if (ageSec > freshSec) {
      // stale but usable: refresh in background, serve now
      refreshInBackground(redis, db, id, key, staleSec);
    }
    return value;
  }
  return refreshInBackground(redis, db, id, key, staleSec);
}

त्वरित जाँच

आप लोड बैलेंसर के पीछे 30 Node.js इंस्टेंस चला रहे हैं। एक अत्यंत अधिक उपयोग वाली कुंजी समाप्त हो जाती है और आपको गारंटी देनी है कि डेटाबेस को उसके लिए अधिकतम एक पुनर्गणना क्वेरी मिले। कौन-सा तरीका यह सुनिश्चित करता है?

पुनरावलोकन

आपने Node.js में कैश स्टैम्पीड और थंडरिंग हर्ड से बचाव करना सीखा:

  • अनुरोधों का संयोजन समवर्ती डुप्लिकेट काम को एक प्रॉमिस में समेटता है — लेकिन केवल प्रति प्रक्रिया।
  • वितरित लॉक (SET NX PX) इस गारंटी को पूरे इंस्टेंस समूह तक बढ़ाते हैं; लॉक को हमेशा TTL दें।
  • जिटर वाले TTL एक साथ होने वाली समाप्तियों को असमकालिक बनाते हैं, ताकि बहुत-सी कुंजियाँ एक ही टिक पर समाप्त न हों।
  • संभाव्य प्रारंभिक समाप्ति (XFetch) हॉट और महँगी कुंजियों को कैश-विफल होने से पहले रिफ़्रेश करती है।
  • स्टेल-व्हाइल-रीवैलिडेट पुराने डेटा को तुरंत देता है और पृष्ठभूमि में एक बार रिफ़्रेश करता है।

इन्हें परतों में रखें: आधार के रूप में जिटर + संयोजन, फिर सबसे अधिक उपयोग वाली कुंजियों के लिए लॉक या XFetch।

शुरुआत निःशुल्क

एआई शिक्षक के साथ JavaScript सीखें — निःशुल्क

अपने ब्राउज़र में वास्तविक कोड लिखें और चलाएँ, चौबीसों घंटे एआई शिक्षक से तुरंत सहायता पाएँ, और वेब या ऐप पर वहीं से शुरू करें जहाँ आपने छोड़ा था।

पाठ्यक्रम
22
पाठ
92

अक्सर पूछे जाने वाले प्रश्न

क्या “कैश स्टैम्पीड और थंडरिंग हर्ड रोकना” पाठ निःशुल्क है?

हाँ—“कैश स्टैम्पीड और थंडरिंग हर्ड रोकना” का पूरा पाठ यहाँ वेब पर निःशुल्क पढ़ा जा सकता है। इंटरैक्टिव अभ्यास (अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर) करने और Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम का बाकी हिस्सा अनलॉक करने के लिए CoddyKit PRO लें। Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

“कैश स्टैम्पीड और थंडरिंग हर्ड रोकना” में मैं क्या सीखूँगा?

अनुरोध समेकन, जिटरयुक्त TTL और संभाव्य शीघ्र समाप्ति से स्टैम्पीड को कम कीजिए। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ Node.js बैकएंड विकास बूटकैंप का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।

क्या Node.js बैकएंड विकास बूटकैंप शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?

पहले के अनुभव की आवश्यकता नहीं है। CoddyKit पर Node.js बैकएंड विकास बूटकैंप शुरुआती से लेकर उन्नत शिक्षार्थियों तक सभी के लिए व्यवस्थित किया गया है, इसलिए आप यहीं से या शुरुआत से सीखना शुरू कर सकते हैं और अपनी गति से आगे बढ़ सकते हैं। यह 4 में से 4वाँ पाठ है।

“कैश स्टैम्पीड और थंडरिंग हर्ड रोकना” पाठ पूरा करने में कितना समय लगता है?

CoddyKit का अधिकांश पाठ लगभग 5–10 मिनट में पूरा हो जाता है। हर पाठ छोटा और संवादात्मक है, इसलिए आप लगातार प्रगति करते हैं और वेब या ऐप पर वहीं से सीखना जारी रख सकते हैं जहाँ आपने छोड़ा था।

क्या मैं इस Node.js बैकएंड विकास बूटकैंप पाठ में कोड लिख और चला सकता हूँ?

हाँ। हर Node.js बैकएंड विकास बूटकैंप पाठ में एक अंतर्निर्मित कोड संपादक शामिल है, जिससे आप सीधे अपने ब्राउज़र में वास्तविक कोड लिख और चला सकते हैं और तुरंत एआई प्रतिक्रिया पा सकते हैं—स्थानीय सेटअप की आवश्यकता नहीं है।

इस पाठ्यक्रम के सभी पाठ

  1. कैश-आसाइड, राइट-थ्रू और TTL रणनीतियाँ
  2. वितरित लॉक और Redlock एल्गोरिदम
  3. Redis से Pub/Sub, स्ट्रीम और दर सीमांकन
  4. कैश स्टैम्पीड और थंडरिंग हर्ड रोकना
← Node.js बैकएंड विकास बूटकैंप पर वापस जाएँ