Vercel KV เป็นเครื่องมืออันล้ำค่าสำหรับโครงการเว็บ แต่การใช้งานอย่างกว้างขวางสามารถนำไปสู่การเพิ่มจำนวนคำขอ HTTP อย่างรวดเร็ว ซึ่งอาจส่งผลกระทบต่อประสิทธิภาพการทำงาน แม้ว่าไปป์ไลน์ Redis เสนอวิธีการชุดคำสั่งและลดคำขอ แต่ก็อาจนำไปใช้ได้ยาก มีวิธีแก้ไขปัญหาที่รวมประสิทธิภาพของไปป์ไลน์เข้ากับความเรียบง่ายของคำสั่ง Redis พื้นฐานหรือไม่ การวางท่ออัตโนมัติ เข้ามา – เสนอวิธีที่ราบรื่นในการเพิ่มประสิทธิภาพโดยไม่ต้องซับซ้อนในการเขียนโค้ด
ปัญหา
ลองนึกภาพคุณได้สร้างหน้าเว็บที่ Redis ถูกใช้เป็นแหล่งข้อมูลอย่างกว้างขวาง คอมโพเนนต์หลายรายการทำการเรียก Redis ของตัวเอง โดยบางส่วนถูกเรนเดอร์หลายครั้งด้วยเนื้อหาที่แตกต่างกัน
การตั้งค่านี้สามารถสร้างคำขอจำนวนมากไปยัง Redis ในแต่ละครั้งที่เปิดหน้าเว็บ ปริมาณคำขอที่แท้จริงทำให้เกิดค่าใช้จ่ายจำนวนมาก ซึ่งส่งผลต่อประสิทธิภาพการทำงาน นอกจากนี้ ในสภาพแวดล้อมเช่น Cloudflare Workers ซึ่งจำนวนคำขอ HTTP ที่เกิดขึ้นพร้อมกันถูกจำกัดอย่างมาก สิ่งนี้จะกลายเป็นปัญหาร้ายแรง
ไปป์ไลน์ปกติ
วิธีแก้ปัญหาทั่วไปในสถานการณ์เช่นนี้คือการใช้ไปป์ไลน์ Redis แทนที่จะส่งคำขอทีละรายการ ไปป์ไลน์ช่วยให้คุณสามารถรวบรวมคำสั่งหลายคำสั่งและดำเนินการพร้อมกันได้ ด้วยวิธีนี้ คำขอ HTTP เดียวจะมีคำสั่ง Redis หลายคำสั่ง ซึ่งช่วยลดจำนวนคำขอ 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"] ข้อเสียของไปป์ไลน์
อย่างไรก็ตาม การใช้ไปป์ไลน์จะทำให้เกิดค่าใช้จ่ายที่สำคัญจากมุมมองของโปรแกรมเมอร์ Pipeline API แตกต่างจาก Redis API มาตรฐาน ทำให้การเปิดหรือปิดใช้งานเป็นงานที่ไม่สำคัญ
นอกจากนี้ หากคุณต้องการขอข้อมูลสำหรับส่วนประกอบต่างๆ ภายในไปป์ไลน์เดียว คุณต้องเขียนตรรกะการดึงข้อมูลแยกจากตำแหน่งที่จะใช้ข้อมูล การแบ่งแยกนี้อาจนำไปสู่การมีโค้ดเบสกระจัดกระจายและบำรุงรักษาได้น้อยกว่า
การวางท่ออัตโนมัติ
การวางท่ออัตโนมัติช่วยแก้ไขปัญหาเหล่านี้ได้อย่างราบรื่น เมื่อใช้ไปป์ไลน์อัตโนมัติ คุณสามารถเปิดใช้งานฟังก์ชันไปป์ไลน์ในโปรเจ็กต์ของคุณได้โดยไม่ต้องเปลี่ยนโค้ดที่มีอยู่ คุณสามารถใช้ Redis ต่อไปได้ตามปกติ ในขณะที่ไคลเอ็นต์ Redis จะจัดกลุ่มคำสั่งโดยอัตโนมัติทุกครั้งที่เป็นไปได้ เพิ่มประสิทธิภาพการทำงานได้อย่างง่ายดาย
เรามาสำรวจว่าการวางท่ออัตโนมัติช่วยเพิ่มประสิทธิภาพสถานการณ์ทั่วไปได้อย่างไร:การดึงค่าสำหรับหลายคีย์:
const keys = ["key1", "key2", "key3"];
const values = await Promise.all(keys.map(key => kv.get(key))); หากไม่มีไปป์ไลน์ โค้ดนี้จะส่งคำขอ HTTP สามคำขอไปยัง Redis อย่างไรก็ตาม เมื่อเปิดใช้งานการไปป์ไลน์อัตโนมัติ คำขอเหล่านี้จะถูกรวมเป็นคำขอ HTTP เดียว
ไปป์ไลน์อัตโนมัติสำหรับส่วนประกอบเซิร์ฟเวอร์ React แบบสำนวน
React Server Components (RSC) สามารถดึงข้อมูลของตนเองได้ ตัวอย่างทั่วไปคือองค์ประกอบทวีตที่อาจนำไปใช้ในลักษณะนี้
async function Tweet({id}) {
const tweet = kv.get(`tweets:${id}`)
return <div>{tweet.text}</div>
} หากคุณเรียกส่วนประกอบนี้ในลักษณะวนซ้ำเช่น
{tweetIds.map(id => <Tweet id={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<Pipeline, Promise<Array<unknown>>>();
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 เห็นได้จากการปรับปรุงในหน้า Landing Page หน้า Landing Page จะแสดงตัวอย่างจากคำค้นหาและรุ่นที่ผ่านมา ขั้นแรก ระบบจะดึงรายการที่จะแสดง จากนั้นทำการดึงข้อมูลแต่ละรายการสำหรับแต่ละรายการ ซึ่งส่งผลให้มีคำขอ Redis จำนวนมาก

หลังจากเปิดใช้งานการวางท่ออัตโนมัติ กระบวนการนี้จะได้รับการปรับปรุงให้เหมาะสมด้วยการกดสวิตช์ ตอนนี้ ส่วนที่สองซึ่งเคยเกี่ยวข้องกับการดึงข้อมูลหลายรายการ ได้ถูกรวมเป็นการดำเนินการไปป์ไลน์เดียว
ก่อนการใช้งานไปป์ไลน์อัตโนมัติ คำขอ Redis เหล่านี้นำไปสู่คำขอ HTTP แต่ละรายการจำนวนมาก ซึ่งทำให้เวลาในการโหลดหน้าเว็บช้าลง อย่างไรก็ตาม เมื่อเปิดใช้งานไปป์ไลน์อัตโนมัติ คำขอเหล่านี้จะถูกรวมกลุ่มเข้ากับไปป์ไลน์อย่างมีประสิทธิภาพ ส่งผลให้จำนวนคำขอ HTTP ที่จำเป็นลดลง ด้วยเหตุนี้ เวลาในการโหลดหน้าเว็บจึงได้รับการปรับปรุง โดยลดลงจากประมาณ 450ms เหลือประมาณ 200ms
ในตอนแรกทีมงาน v0 ได้จัดส่งการวางท่ออัตโนมัติเพื่อเป็นการแฮ็กสำหรับตนเอง การวางท่ออัตโนมัติพร้อมใช้งานแล้วในเวอร์ชัน 1.31.3 ของไคลเอ็นต์ Redis ของ Upstash รวมถึงเวอร์ชัน 2.0 ของ Vercel KV