Vercel KV वेब प्रोजेक्ट्स के लिए एक अमूल्य टूल है, फिर भी इसका व्यापक उपयोग HTTP अनुरोधों की संख्या में तेजी से वृद्धि का कारण बन सकता है, जिससे संभावित रूप से परफॉरमेंस प्रभावित हो सकती है। जबकि रेडिस पाइपलाइन्स कमांड्स को बैच करके और अनुरोधों को कम करके एक तरीका प्रदान करते हैं, उन्हें लागू करना मुश्किल हो सकता है। क्या कोई समाधान है जो बेसिक रेडिस कमांड्स की सादगी के साथ पाइपलाइन्स की दक्षता को जोड़ता है? आते हैं ऑटो-पाइपलाइनिंग - कोडिंग जटिलता के बिना परफॉरमेंस बढ़ाने का एक सहज तरीका प्रदान करता है।
समस्या
कल्पना करें कि आपने एक वेबपेज बनाया है जहां रेडिस का बड़े पैमाने पर डेटा स्रोत के रूप में उपयोग किया जाता है। कई कंपोनेंट्स अपने स्वयं के रेडिस कॉल कर रहे हैं, कुछ को अलग-अलग कंटेंट के साथ कई बार रेंडर किया जा रहा है।
यह सेटअप हर बार वेबपेज खुलने पर रेडिस के लिए अनुरोधों की एक महत्वपूर्ण संख्या उत्पन्न कर सकता है। अनुरोधों की भारी मात्रा काफी ओवरहेड पेश करती है, जिससे परफॉरमेंस प्रभावित होती है। इसके अलावा, क्लाउडफ्लेयर वर्कर्स जैसे एनवायरनमेंट्स में, जहां समवर्ती HTTP अनुरोधों की संख्या गंभीर रूप से सीमित है, यह एक महत्वपूर्ण मुद्दा बन जाता है।
रेगुलर पाइपलाइन्स
ऐसी स्थितियों में एक पारंपरिक समाधान रेडिस पाइपलाइन्स का उपयोग करना है। अनुरोधों को एक-एक करके भेजने के बजाय, पाइपलाइन्स आपको कई कमांड्स को इकट्ठा करने और उन्हें एक साथ निष्पादित करने की अनुमति देते हैं। इस तरह, एक ही HTTP अनुरोध कई रेडिस कमांड्स को ले जाता है, जिससे HTTP अनुरोधों की संख्या काफी कम हो जाती है और परफॉरमेंस में सुधार होता है।
import { kv } from '@vercel/kv';
const pipeline = kv.pipeline();
pipeline.set("foo", "bar");
pipeline.get("foo");
const res = await pipeline.exec();
console.log(res); // ["OK", "bar"]
पाइपलाइन्स के नुकसान
हालांकि, पाइपलाइन्स का उपयोग प्रोग्रामर के दृष्टिकोण से महत्वपूर्ण ओवरहेड पेश करता है। पाइपलाइन API स्टैंडर्ड रेडिस API से भिन्न होता है, जिससे इसे सक्षम या अक्षम करना एक गैर-तुच्छ कार्य बन जाता है।
इसके अलावा, यदि आपको एक ही पाइपलाइन में विभिन्न कंपोनेंट्स के लिए डेटा का अनुरोध करने की आवश्यकता है, तो आपको फेचिंग लॉजिक को वहां से अलग लिखना होगा जहां डेटा उपयोग किया जाएगा। यह अलगाव एक खंडित और कम रखरखाव योग्य कोडबेस का कारण बन सकता है।
ऑटो-पाइपलाइनिंग
ऑटो-पाइपलाइनिंग इन मुद्दों को सहजता से संबोधित करता है। ऑटो-पाइपलाइनिंग के साथ, आप अपने मौजूदा कोड को बदले बिना अपने प्रोजेक्ट में पाइपलाइन कार्यक्षमता सक्षम कर सकते हैं। आप हमेशा की तरह रेडिस का उपयोग करना जारी रख सकते हैं, जबकि रेडिस क्लाइंट जब भी संभव हो कमांड्स को स्वचालित रूप से बैच करता है, जिससे परफॉरमेंस सहजता से बढ़ती है।
आइए पता लगाएं कि ऑटो-पाइपलाइनिंग एक सामान्य परिदृश्य को कैसे अनुकूलित करता है: कई कीज़ के लिए वैल्यूज़ प्राप्त करना:
const keys = ["key1", "key2", "key3"];
const values = await Promise.all(keys.map(key => kv.get(key)));
पाइपलाइन्स के बिना, यह कोड रेडिस को तीन HTTP अनुरोध भेजेगा। हालांकि, ऑटो-पाइपलाइनिंग सक्षम होने पर, ये अनुरोध एक ही HTTP अनुरोध में बैच हो जाएंगे।
इडियोमैटिक रिएक्ट सर्वर कंपोनेंट्स के लिए ऑटो-पाइपलाइन्स
रिएक्ट सर्वर कंपोनेंट्स (RSC) अपना डेटा स्वयं प्राप्त कर सकते हैं। एक सामान्य उदाहरण एक ट्वीट कंपोनेंट है जिसे इस तरह लागू किया जा सकता है
async function Tweet({id}) {
const tweet = kv.get(`tweets:${id}`)
return {tweet.text}
}
यदि आप इस कंपोनेंट को लूप में कॉल करते हैं जैसे
{tweetIds.map(id => )}
आप ऊपर दिए उदाहरण के समान N बैकएंड अनुरोधों को ट्रिगर करते हैं। एक बार फिर, ऑटो-पाइपलाइनिंग सक्रिय करने से N कमांड्स एक ही पाइपलाइन में बैच हो जाते हैं जबकि इडियोमैटिक रिएक्ट कोड बरकरार रहता है।
यह कैसे काम करता है
ऑटो-पाइपलाइनिंग बैकग्राउंड में एक 'एक्टिव पाइपलाइन' बनाए रखकर काम करता है। कमांड्स खुद को पाइपलाइन में जोड़ते हैं और deferExecution को इनवोक करते हैं:
private async deferExecution() {
await Promise.resolve()
return await Promise.resolve()
}
deferExecution को कॉल करने पर, कमांड Node.js मेन थ्रेड का नियंत्रण छोड़ देती है। अनुक्रम में अगली GET कमांड तब थ्रेड का नियंत्रण प्राप्त करती है और अपने निष्पादन के साथ आगे बढ़ती है, ठीक वही काम करती है जो पहली GET ने किया था: खुद को एक्टिव पाइपलाइन में जोड़ना और थ्रेड का नियंत्रण छोड़ना।
यहां ऑटो पाइपलाइन लॉजिक का सूडो-कोड है:
let activePipeline: Pipeline;
let pipelinePromises: new WeakMap>>();
let commandIndex: number;
const executeCommand = (command) => {
activePipeline = activePipeline || createNewPipeline();
activePipeline.addCommand(command);
commandIndex++;
const pipelinePromise = deferExecution().then(() => {
if (!pipelinePromises.has(activePipeline) {
const pipelinePromise = pipeline.exec();
pipelinePromises.set(pipeline, pipelinePromise);
activePipeline = null;
commandIndex = 0
};
return pipelinePromises.get(activePipeline)!;
});
const result = await pipelinePromise;
return result[commandIndex];
};
खुद को एक्टिव पाइपलाइन में जोड़ने के बाद, तीसरी GET भी deferExecution को कॉल करती है। इस बिंदु पर, चूंकि नियंत्रण छोड़ने के लिए कोई अन्य कमांड नहीं हैं, डिफर्ड GET कमांड्स में से एक नियंत्रण पुनः प्राप्त करेगी और पाइपलाइन निष्पादित करेगी।
यह पाइपलाइन हमारे उदाहरण में तीन परिणाम लौटाती है, प्रत्येक प्रारंभिक कमांड्स से मेल खाता है। प्रत्येक कमांड अपने तर्क के इंडेक्स को ट्रैक करती है, यह सुनिश्चित करते हुए कि यह बैच प्रतिक्रिया से सही परिणाम प्राप्त करती है।
ऑटो-पाइपलाइनिंग लॉजिक के पीछे का कोड यहां उपलब्ध है।
v0.dev में ऑटो-पाइपलाइनिंग
v0.dev के परफॉरमेंस पर ऑटो-पाइपलाइनिंग का प्रभाव लैंडिंग पेज में सुधार से स्पष्ट है। लैंडिंग पेज पिछले क्वेरीज़ और जेनरेशन से उदाहरण प्रदर्शित करता है। पहले, यह प्रदर्शित करने के लिए आइटम्स की सूची प्राप्त करता है और फिर प्रत्येक आइटम के लिए अलग-अलग फेच करता है, जिसके परिणामस्वरूप कई रेडिस अनुरोध होते हैं।

ऑटो-पाइपलाइनिंग सक्षम करने के बाद, यह प्रक्रिया एक स्विच के फ्लिप के साथ अनुकूलित हो जाती है। अब, दूसरा भाग, जिसमें पहले कई व्यक्तिगत फेच शामिल थे, एक ही पाइपलाइन ऑपरेशन में समेकित हो जाता है।
ऑटो-पाइपलाइनिंग के कार्यान्वयन से पहले, इन रेडिस अनुरोधों ने बड़ी संख्या में व्यक्तिगत HTTP अनुरोधों का नेतृत्व किया, जिससे पेज लोड समय धीमा हो गया। हालांकि, ऑटो-पाइपलाइनिंग सक्षम होने पर, ये अनुरोध कुशलतापूर्वक पाइपलाइन्स में बैच हो जाते हैं, जिससे आवश्यक HTTP अनुरोधों की संख्या कम हो जाती है। नतीजतन, पेज लोड समय में सुधार होता है, जो लगभग 450ms से घटकर लगभग 200ms हो जाता है।
v0 टीम ने शुरू में ऑटो-पाइपलाइनिंग को अपने लिए एक हैक के रूप में शिप किया। ऑटो-पाइपलाइनिंग अब अपस्टैश के रेडिस क्लाइंट के v1.31.3 में, साथ ही Vercel KV के v2.0 में उपलब्ध है।