Vercel KV เป็นเครื่องมือที่มีคุณค่าสำหรับโปรเจกต์เว็บ แตการใช้งานอย่างกว้างขวางอาจนำไปสู่การเพิ่มขึ้นอย่างรวดเร็วของจำนวน HTTP requests ซึ่งอาจส่งผลต่อประสิทธิภาพ ในขณะที่ Redis pipelines ให้วิธี batch commands และลด requests พวกมันอาจยากต่อการนำไปใช้ มีโซลูชันที่รวมประสิทธิภาพของ pipelines กับความเรียบง่ายของ basic Redis commands หรือไม่? เข้ามา auto-pipelining – นำเสนอวิธีที่ seamless เพื่อเพิ่มประสิทธิภาพโดยไม่มีความซับซ้อนของการเขียนโค้ด
ปัญหา (The Problem)
ลองจินตนาการว่าคุณสร้าง webpage ที่ใช้ Redis อย่างกว้างขวางเป็น data source Multiple components ทำ Redis calls ของตัวเอง โดยบางตัวถูก render หลายครั้งด้วยเนื้อหาที่แตกต่างกัน
Setup นี้สามารถสร้างจำนวน requests ที่มากไปยัง Redis ทุกครั้งที่ webpage ถูกเปิด จำนวน requests เยอะมากนำไปสู่ overhead ที่มาก ส่งผลต่อประสิทธิภาพ นอกจากนี้ ใน environments เช่น Cloudflare Workers ที่จำนวน concurrent HTTP requests ถูกจำกัดอย่างรุนแรง สิ่งนี้กลายเป็น critical issue
Regular Pipelines
โซลูชันแบบดั้งเดิมในสถานการณ์เช่นนี้คือการใช้ Redis pipelines แทนที่จะส่ง requests ทีละอัน pipelines อนุญาตให้คุณรวบรวม multiple commands และ execute พวกมันพร้อมกัน วิธีนี้ single HTTP request จะบรรทุก multiple Redis commands ลดจำนวน HTTP requests อย่างมากและปรับปรุงประสิทธิภาพ
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"]
Cons of Pipelines
อย่างไรก็ตาม การใช้ pipelines นำไปสู่ overhead ที่มากมายจากมุมมองของโปรแกรมเมอร์ Pipeline API แตกต่างจาก standard Redis API ทำให้การ enable หรือ disable มันเป็น non-trivial task
而且,如果您需要在单个 pipeline 中为不同的组件请求数据,您必须将获取逻辑与数据将被使用的位置分开编写。这种分离可能导致代码库变得碎片化且难以维护。
Auto-Pipelining
Auto-pipelining แก้ไขปัญหาเหล่านี้อย่าง seamless ด้วย auto-pipelining คุณสามารถ enable pipeline functionality ในโปรเจกต์ของคุณได้โดยไม่ต้องเปลี่ยน existing code คุณสามารถใช้ Redis ตามปกติได้ ในขณะที่ Redis client จะ batch commands อัตโนมัติเมื่อเป็นไปได้ เพิ่มประสิทธิภาพได้อย่างไร้ความพยายาม
มา探索ว่า auto-pipelining เพิ่มประสิทธิภาพสถานการณ์ทั่วไปอย่างไร: การดึงค่าสำหรับ multiple keys:
const keys = ["key1", "key2", "key3"]; const values = await Promise.all(keys.map(key => kv.get(key)));
โดยไม่มี pipelines โค้ดนี้จะส่งสาม HTTP requests ไปยัง Redis อย่างไรก็ตาม ด้วย auto-pipelining ที่เปิดใช้งาน requests เหล่านี้จะถูก batched เป็น single HTTP request
Auto-Pipelines สำหรับ idiomatic React Server Components
React Server Components (RSC) สามารถ fetch ข้อมูลของตัวเองได้ ตัวอย่างทั่วไปคือ tweet component ที่อาจ implement เหมือนนี้
async function Tweet({id}) { const tweet = kv.get(`tweets:${id}`) return <div>{tweet.text}</div> }
หากคุณเรียก component นี้ใน loop เหมือน
{tweetIds.map(id => <Tweet id={id} />)}
คุณทริกเกอร์ N backend requests เหมือนในตัวอย่างข้างต้น อีกครั้ง การเปิดใช้งาน auto-pipelining จะ batch N commands เข้าเป็น single pipeline ในขณะที่ยังคง idiomatic react code
มันทำงานอย่างไร (How it works)
Auto-pipelining ทำงานโดยการ maintain 'active pipeline' ใน background Commands เพิ่มตัวเองเข้า pipeline และ invoke deferExecution:
private async deferExecution() { await Promise.resolve() return await Promise.resolve() }
เมื่อเรียก deferExecution command จะยield control ของ Node.js main thread คำสั่ง GET ถัดไปใน sequence จะได้ control ของ thread และดำเนินการต่อ ทำสิ่งเดียวกับ GET แรก: เพิ่มตัวเองเข้า active pipeline และ yield control ของ thread
นี่คือ auto pipeline logic ในรูปแบบ pseudo-code:
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]; };
หลังจากเพิ่มตัวเองเข้า active pipeline GET ที่สามก็เรียก deferExecution ในจุดนี้ เนื่องจากไม่มีคำสั่งอื่นที่จะ yield ให้ หนึ่งใน GET commands ที่ deferred จะได้ control กลับมาและ execute pipeline
Pipeline นี้คืนค่าสามผลลัพธ์ในตัวอย่างของเรา แต่ละอันตรงกับคำสั่งเริ่มต้น แต่ละคำสั่งติดตาม index ของ argument ของมัน รับรองว่ามันได้รับผลลัพธ์ที่ถูกต้องจาก batch response
โค้ดเบื้องหลัง logic ของ auto-pipelining พร้อมใช้งานที่นี่
Auto-Pipelining ใน v0.dev
ผลกระทบของ auto-pipelining ต่อประสิทธิภาพของ v0.chเห็นได้ชัดจากการปรับปรุงของ landing page Landing page แสดงตัวอย่างจาก past queries และ generations ก่อนอื่น มัน fetch list of items เพื่อแสดง และจากนั้นทำ individual fetches สำหรับแต่ละ item ทำให้เกิด Redis requests จำนวนมาก

หลังจากเปิดใช้งาน auto-pipelining กระบวนการนี้ถูกเพิ่มประสิทธิภาพด้วยการสลับสวิตช์เพียงครั้งเดียว ตอนนี้ ส่วนที่สอง ซึ่งก่อนหน้านี้เกี่ยวข้องกับ multiple individual fetches ถูกรวมเป็น single pipeline operation
ก่อนการนำ auto-pipelining มาใช้ Redis requests เหล่านี้นำไปสู่ significant number ของ individual HTTP requests ทำให้ page load times ช้า อย่างไรก็ตาม ด้วย auto-pipelining ที่เปิดใช้งาน requests เหล่านี้ถูก batched เข้า pipelines อย่างมีประสิทธิภาพ ลดจำนวน HTTP requests ที่จำเป็น ดังนั้น page load time จึงปรับปรุง ลดจากประมาณ 450ms เป็นประมาณ 200ms
ทีม v0 ส่ง auto-pipelining เริ่มต้นเป็น hack สำหรับตัวเอง Auto-pipelining ตอนนี้พร้อมใช้งานใน v1.31.3 ของ Upstash's Redis client และ v2.0 ของ Vercel KV