Computer >> कंप्यूटर >  >> प्रोग्रामिंग >> Redis

सर्वर रहित की चुनौती:डेटाबेस कनेक्शन

सर्वर रहित के लिए एक डेटाबेस तैयार करना, हमारे दिमाग में सबसे बड़ी चुनौती एक ऐसे बुनियादी ढांचे का निर्माण करना था जो लाभदायक तरीके से प्रति अनुरोध मूल्य निर्धारण का समर्थन करता हो। हमें विश्वास है कि Upstash ने इसे हासिल किया है। उत्पाद लॉन्च करने के बाद, हमने देखा कि एक और बड़ी चुनौती थी:डेटाबेस कनेक्शन!

जैसा कि आप जानते हैं, सर्वर रहित कार्य 0 से अनंत तक स्केल करते हैं। इसका मतलब है कि जब आपके कार्यों को बहुत अधिक ट्रैफ़िक मिलता है, तो क्लाउड प्रदाता समानांतर में नए कंटेनर (लैम्ब्डा फ़ंक्शन) बनाता है और आपके बैकएंड को मापता है। यदि आप फ़ंक्शन के भीतर एक नया डेटाबेस कनेक्शन बनाते हैं तो आप अपने डेटाबेस की कनेक्शन सीमा तक तेज़ी से पहुँच सकते हैं।

यदि आप लैम्ब्डा फ़ंक्शन के बाहर कनेक्शन को कैश करने का प्रयास करते हैं तो एक और समस्या होती है। जब AWS आपके लैम्ब्डा फ़ंक्शन को फ्रीज कर देता है, तो यह कनेक्शन बंद नहीं करता है। तो आप कई निष्क्रिय/ज़ोंबी कनेक्शनों के साथ समाप्त हो सकते हैं जो अभी भी धमकी दे सकते हैं।

यह समस्या Redis के लिए विशिष्ट नहीं है, यह उन सभी डेटाबेस पर लागू होती है जो TCP कनेक्शन (Mysql, Postgre, MongoDB आदि) पर निर्भर करते हैं। आप देख सकते हैं कि सर्वर रहित समुदाय सर्वर रहित-mysql जैसे समाधान बना रहा है। ये क्लाइंट-साइड समाधान हैं। Upstash के रूप में, हमें सर्वर-साइड को लागू करने और बनाए रखने का लाभ मिलता है। इसलिए हमने कनेक्शनों की निगरानी करके और बेकार पड़े कनेक्शनों को हटाकर समस्या को कम करने का फैसला किया। तो यहां एल्गोरिदम:अधिकतम-समवर्ती-कनेक्शन के रूप में, हमारे पास डेटाबेस, सॉफ्ट-लिमिट और हार्ड-लिमिट के लिए दो सीमाएं हैं। जब कोई डेटाबेस सॉफ्ट-लिमिट तक पहुँच जाता है तो हम निष्क्रिय कनेक्शन को समाप्त करना शुरू कर देते हैं। हम हार्ड-लिमिट तक पहुंचने तक नए कनेक्शन अनुरोध स्वीकार करना जारी रखते हैं। यदि डेटाबेस हार्ड लिमिट तक पहुँच जाता है तो हम नए कनेक्शन को अस्वीकार करना शुरू कर देते हैं।

कनेक्शन बेदखली एल्गोरिथम

if( current_connection_count < SOFT_LIMIT ) {
    ACCEPT_NEW_CONNECTIONS
}

if( current_connection_count > SOFT_LIMIT && current_connection_count < HARD_LIMIT ) {
    ACCEPT_NEW_CONNECTIONS
    START_EVICTING_IDLE_CONNECTIONS
}

if( current_connection_count > HARD_LIMIT ) {
    REJECT_NEW_CONNECTIONS
}

ध्यान दें कि अपस्टैश डॉक्स में सूचीबद्ध अधिकतम समवर्ती कनेक्शन सीमाएं सॉफ्ट सीमाएं हैं।

क्षणिक कनेक्शन

उपरोक्त एल्गोरिथम को लागू करने के बाद, हमने सभी क्षेत्रों में अस्वीकृत कनेक्शनों की संख्या में भारी कमी देखी है। लेकिन फिर भी अगर आप सुरक्षित पक्ष में रहना चाहते हैं, तो आप अपनी तरफ भी समस्या का समाधान कर सकते हैं। कनेक्शन का पुन:उपयोग करने के बजाय, आप फ़ंक्शन के अंदर रेडिस कनेक्शन खोल सकते हैं, लेकिन जब भी आप रेडिस के साथ काम कर रहे हों तो उन्हें बंद भी कर सकते हैं:

exports.handler = async (event) => {
  const client = new Redis(process.env.REDIS_URL);
  /*
    do stuff with redis
    */
  await client.quit();
  /*
    do other stuff
    */
  return {
    response: "response",
  };
};

उपरोक्त कोड आपको समवर्ती कनेक्शन संख्या को कम करने में मदद करता है। लोग नए कनेक्शन के लेटेंसी ओवरहेड के बारे में पूछते हैं। रेडिस कनेक्शन बहुत हल्के होने के लिए जाने जाते हैं।

क्या Redis कनेक्शन वास्तव में हल्के हैं?

हमने यह देखने के लिए एक बेंचमार्क परीक्षण चलाया है कि रेडिस कनेक्शन कितने हल्के हैं। इस परीक्षण में हम दो दृष्टिकोणों की विलंबता संख्याओं की तुलना करते हैं:

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

2- पुन:उपयोग कनेक्शन:हम एक बार कनेक्शन बनाते हैं और सभी आदेशों के लिए उसी कनेक्शन का पुन:उपयोग करते हैं। यहां, हम ping() . की विलंबता को रिकॉर्ड करते हैं संचालन। देखें benchReuse() नीचे दी गई विधि।

async function benchReuse() {
  const client = new Redis(options);
  const hist = hdr.build();
  for (let index = 0; index < total; index++) {
    let start = performance.now() * 1000; // to μs
    client.ping();
    let end = performance.now() * 1000; // to μs
    hist.recordValue(end - start);
    await delay(10);
  }
  client.quit();
  console.log(hist.outputPercentileDistribution(1, 1));
}

async function benchEphemeral() {
  const hist = hdr.build();
  for (let index = 0; index < total; index++) {
    let start = performance.now() * 1000; // to μs
    const client = new Redis(options);
    client.ping();
    client.quit();
    let end = performance.now() * 1000; // to μs
    hist.recordValue(end - start);
    await delay(10);
  }
  console.log(hist.outputPercentileDistribution(1, 1));
}

यदि आप स्वयं बेंचमार्क चलाना चाहते हैं तो रेपो देखें।

हमने इस बेंचमार्क कोड को AWS EU-WEST-1 क्षेत्र में दो अलग-अलग सेटअपों में चलाया। पहला सेटअप SAME ZONE है जहां क्लाइंट और डेटाबेस समान उपलब्धता क्षेत्र में हैं। दूसरा सेटअप इंटर ज़ोन है जहाँ क्लाइंट डेटाबेस से भिन्न उपलब्धता क्षेत्र में चलता है। हमने डेटाबेस सर्वर के रूप में Upstash Standard प्रकार का उपयोग किया है।

हमने देखा है कि एक नया कनेक्शन (अल्पकालिक दृष्टिकोण) बनाने और बंद करने का ओवरहेड केवल 75 माइक्रोसेकंड (99 वाँ प्रतिशत) है। इंटरज़ोन सेटअप (80 माइक्रोसेकंड) में ओवरहेड बहुत समान है।

फिर हमने एडब्ल्यूएस लैम्ब्डा फ़ंक्शन के अंदर उसी परीक्षण को दोहराने का फैसला किया। परिणाम अलग थे। खासकर जब हम लैम्ब्डा फ़ंक्शन की मेमोरी को कम (128Mb) सेट करते हैं, तो हमने रेडिस कनेक्शन का बड़ा ओवरहेड देखा है। हमने AWS लैम्ब्डा फ़ंक्शन के अंदर 6-7 मिसे तक विलंबता ओवरहेड देखा है।

रेडिस कनेक्शन के बारे में हमारे निष्कर्ष:

  • Redis कनेक्शन उचित मात्रा में CPU पावर वाले सिस्टम पर वास्तव में हल्के होते हैं। t2.micro पर भी।
  • डिफ़ॉल्ट एडब्ल्यूएस लैम्ब्डा कॉन्फ़िगरेशन के साथ सीपीयू पावर बहुत खराब है, जो लैम्ब्डा फ़ंक्शन के कुल निष्पादन समय के संबंध में टीसीपी कनेक्शन की लागत में काफी वृद्धि करता है।
  • यदि आप डिफ़ॉल्ट/न्यूनतम मेमोरी के साथ लैम्ब्डा फ़ंक्शन का उपयोग करते हैं, तो आप फ़ंक्शन के बाहर रेडिस कनेक्शन को बेहतर तरीके से कैश कर सकते हैं।

जमे हुए कंटेनर => ज़ोंबी कनेक्शन

यह महसूस करने के बाद कि कुछ एडब्ल्यूएस लैम्ब्डा सेटअप में कनेक्शन का उल्लेखनीय ओवरहेड हो सकता है, हमने reusing connection पर और परीक्षण करने का निर्णय लिया। एडब्ल्यूएस लैम्ब्डा में। हमें एक और समस्या का पता चला है। यह एक बड़ा मामला था जिसकी अभी तक किसी ने रिपोर्ट नहीं की है।

यहां समयरेखा है कि यह कैसे होता है:

STEP1 - टाइमर-0सेकंड: हम लैम्ब्डा फ़ंक्शन के बाहर कनेक्शन को कैशिंग करते हुए एक अनुरोध भेजते हैं।

if (typeof client === "undefined") {
  var client = new Redis("REDIS_URL");
}

module.exports.hello = async (event) => {
  let response = await client.get("foo");
  return { response: response + "-" + time };
};

STEP2 - टाइमर-5सेकंड: AWS थोड़े समय के बाद कंटेनर को फ्रीज कर देता है।

STEP3 - समय-60सेकंड: निष्क्रिय कनेक्शन के लिए Upstash में 60 सेकंड का समय समाप्त होता है। तो यह कनेक्शन को मारता है, लेकिन क्लाइंट से एसीके प्राप्त नहीं कर सकता क्योंकि यह जमे हुए है। तो सर्वर कनेक्शन FIN_WAIT_2 स्थिति में चला जाता है।

STEP4 - समय-90सेकंड: FIN_WAIT_2 स्थिति से बाहर निकलते हुए Upstash सर्वर कनेक्शन को पूरी तरह से बंद कर देता है।

STEP5 - समय-95सेकंड: क्लाइंट एक ही अनुरोध भेजता है और ETIMEDOUT अपवाद प्राप्त करता है। क्योंकि क्लाइंट मानता है कि कनेक्शन खुला है लेकिन ऐसा नहीं है।

STEP6 - समय-396सेकंड: अंतिम अनुरोध के 5 मिनट बाद, AWS कंटेनर को पूरी तरह से समाप्त कर देता है।

STEP7 - समय-400सेकंड: क्लाइंट उसी अनुरोध को भेजता है इस बार यह अच्छी तरह से काम करता है क्योंकि कंटेनर खरोंच से बनाया गया है इसलिए प्रारंभिक चरण को छोड़ दिया नहीं गया है। एक नया कनेक्शन बनाया गया है।

जैसा कि आप ऊपर देख सकते हैं, एडब्ल्यूएस एक कंटेनर को पिघला देता है और कनेक्शन का पुन:उपयोग करता है। लेकिन सर्वर की ओर से कनेक्शन बंद कर दिया गया है और इसे संप्रेषित नहीं किया जा सका क्योंकि फ़ंक्शन फ़्रीज़ हो गया था। तो अपस्टैश के एक निष्क्रिय कनेक्शन को बेदखल करने और एक निष्क्रिय कार्य को संभालने वाले AWS के बीच एक सिंक्रनाइज़ेशन समस्या है। इसलिए अगर हम एडब्ल्यूएस के किसी फ़ंक्शन को समाप्त करने के बाद ही एक निष्क्रिय कनेक्शन को मार देते हैं तो कोई समस्या नहीं होगी।

हमने यह मानते हुए अपस्टैश कनेक्शन टाइमआउट को 310 सेकंड में बदल दिया है कि AWS 300 सेकंड में एक निष्क्रिय फ़ंक्शन को समाप्त कर देता है। इस परिवर्तन के बाद, समस्या गायब हो गई। यहां समस्या यह है कि जब वे निष्क्रिय कार्यों को समाप्त करते हैं तो एडब्ल्यूएस पारदर्शी नहीं होता है। इसलिए हमें परीक्षण जारी रखने और यह पता लगाने की कोशिश करने की आवश्यकता है कि क्या समस्या फिर से होती है।

यह समस्या सर्वर रहित-mysql लाइब्रेरी पर देखी गई समस्या के समान है। टिप्पणियों में, ETIMEDOUT अपवाद पर अनुरोध को पुनः प्रयास करने का सुझाव दिया गया है। लेकिन पुनः प्रयास करने में दो कमियां हैं। पहले आप एक लिखित अनुरोध का पुन:प्रयास कर सकते हैं जिसे संसाधित किया गया हो सकता है और वास्तविक नेटवर्क समस्या के साथ समय समाप्त हो सकता है। दूसरी समस्या विफल अनुरोध की अतिरिक्त विलंबता है।

ग्राफक्यूएल बहुत मदद करता है

कनेक्शन रहित एपीआई रखने के लिए कनेक्शन समस्याओं से छुटकारा पाने का एक तरीका। Upstash Redis प्रोटोकॉल के अलावा GraphQL API को भी सपोर्ट करता है। GraphQL HTTP आधारित है इसलिए इसमें कनेक्शन सीमा की समस्या नहीं है। समर्थित आदेशों के लिए दस्तावेज़ देखें। सावधान रहें कि GraphQL API में Redis प्रोटोकॉल पर विलंबता ओवरहेड (लगभग 5msec) है।

निष्कर्ष

हम सर्वर रहित अनुप्रयोगों के लिए एक सहज अनुभव के लिए Upstash डेटाबेस को अनुकूलित करते हैं। हमारा नया सर्वर-साइड एल्गोरिथम उन निष्क्रिय कनेक्शनों को हटा देता है जो AWS लैम्ब्डा बहुतायत में बनाता है। आप लैम्ब्डा फ़ंक्शन के अंदर अपने रेडिस क्लाइंट को खोलकर/बंद करके कनेक्शन की संख्या को कम कर सकते हैं लेकिन अगर आपकी फ़ंक्शन मेमोरी 1GB से कम है तो इसमें विलंबता ओवरहेड हो सकती है।

निष्कर्ष के रूप में, सर्वर रहित उपयोग के मामलों के लिए हमारी अनुशंसा:

  • यदि आपका उपयोग मामला विलंबता संवेदनशील है (उदाहरण के लिए 6msec आपके लिए बड़ा है) तो Redis क्लाइंट का पुन:उपयोग करें।
  • यदि आप बहुत अधिक संख्या में समवर्ती क्लाइंट (1000 से अधिक) का अनुभव करते हैं तो Redis क्लाइंट का पुन:उपयोग करें।
  • यदि आपका उपयोग मामला विलंबता संवेदनशील नहीं है तो फ़ंक्शन के अंदर Redis क्लाइंट को खोलें/बंद करें।
  • यदि आपके फ़ंक्शन में कम से कम 1GB मेमोरी है तो फ़ंक्शन के अंदर Redis क्लाइंट को खोलें/बंद करें।

हमें Twitter या Discord पर अपनी प्रतिक्रिया दें।


  1. सर्वर रहित और एज के लिए वैश्विक डेटाबेस

    हाल के वर्षों में, सर्वर रहित आर्किटेक्चर और एज कंप्यूटिंग अनुप्रयोग परिनियोजन के लिए बहुत लोकप्रिय हो रहे हैं। लेकिन एप्लिकेशन स्टेट और सर्वर रहित और/या एज फ़ंक्शन के अंदर डेटा संग्रहीत करना एक अलग कहानी है। कई कठिनाइयाँ हैं जैसे; डेटाबेस से कनेक्शन का प्रबंधन, कई स्थानों से डेटा को तेज़ एक्सेस के

  1. सर्वर रहित डेटाबेस के बीच विलंबता तुलना:DynamoDB बनाम FaunaDB बनाम Upstash

    इस लेख में, मैं एक सामान्य वेब उपयोग के मामले के लिए तीन सर्वर रहित डेटाबेस DynamoDB, FaunaDB, Upstash (Redis) की विलंबता की तुलना करूँगा। मैंने एक नमूना समाचार वेबसाइट बनाई है और मैं वेबसाइट से प्रत्येक अनुरोध के साथ डेटाबेस से संबंधित विलंबता रिकॉर्ड कर रहा हूं। वेबसाइट और स्रोत कोड की जाँच करें।

  1. LinkedIn पर कनेक्शन हार्वेस्टिंग को कैसे रोकें

    लिंक्डइन कभी-कभी वास्तव में परेशान कर सकता है। आपसे कम कनेक्शन वाले यादृच्छिक लोग आपके नेटवर्क में शामिल होने के इच्छुक हैं। कुछ लोगों को आपने कनेक्ट करने की मांग के बारे में कभी नहीं सुना होगा। कुछ केवल आपके नेटवर्क से जुड़ना चाहते हैं, आपको कुछ बेचने के लिए - आमतौर पर एक सेवा जो वे पेश कर रहे हैं।