API ของผมตอบสนองใน 4 ms แต่การนำทางยังรู้สึกช้าอยู่

ผมกำลังดีบั๊กแอปพลิเคชันจัดการโปรเจกต์ที่สร้างด้วย SvelteKit และ Rust API

บนเครื่อง Local ของผม ทุกอย่างดูรวดเร็วทันใจ แต่พออยู่บน VPS การนำทางกลับรู้สึกหน่วง การเปิดหน้าอย่าง Tickets หรือ Timeline ใช้เวลานานเกินไป การคลิกที่ตั๋ว (ticket) ทำให้เกิดความล่าช้าอย่างเห็นได้ชัดก่อนที่ตัวอย่าง (preview) จะปรากฏขึ้น

ผมคิดว่าปัญหาอยู่ที่เซิร์ฟเวอร์ ผมสงสัยว่าอาจจะเป็นเพราะการคิวรีฐานข้อมูลที่ช้า หรือ VPS ที่สเปกไม่แรงพอ

แต่ผลการวัดค่าพิสูจน์ว่าผมคิดผิด

ผมตรวจสอบ endpoint ของรายการฟีเจอร์ (feature list) สำหรับตั๋วเพียง 52 ใบ API ตอบสนองภายใน 4 ms ซึ่งฟังดูเร็วมาก แต่ขนาดของ response กลับสูงถึง 354 KB

Payload ของ SvelteKit route ก็แสดงปัญหาเดียวกัน ขนาดข้อมูลนั้นใหญ่เกินไปสำหรับรายการธรรมดาๆ

ปัญหาไม่ใช่เรื่องความเร็ว แต่เป็นเรื่องของน้ำหนัก

เฉพาะส่วนคำอธิบาย (descriptions) ก็คิดเป็น 80% ของ response ทั้งหมดแล้ว endpoint สำหรับรายการข้อมูลกำลังส่งคำอธิบายแบบ Markdown ฉบับเต็มกลับมาสำหรับทุกๆ รายการ

นี่คือกับดักที่พบได้บ่อย endpoint สำหรับรายการข้อมูลได้กลายเป็น endpoint สำหรับรายละเอียดไปเสียแล้ว ทุกครั้งที่หน้าเว็บต้องการรายการข้อมูล มันต้องแบกรับภาระข้อมูลที่ไม่ได้ใช้งานจริง

การใช้งานบน UI ไม่ได้เป็นตัวกำหนด payload แต่รูปแบบการคืนค่าของ loader ต่างหากที่เป็นตัวกำหนด

หาก loader ของคุณคืนค่าเป็น object แบบเต็ม SvelteKit จะทำการ serialize object ทั้งหมดนั้นลงใน route data แม้ว่าคุณจะไม่ได้นำ field นั้นมาแสดงผล (render) เลยก็ตาม แต่มันก็ยังต้องถูกส่งผ่านเครือข่ายอยู่ดี

ผมแก้ไขปัญหานี้ด้วยการแยกข้อมูลสรุป (summary data) ออกจากข้อมูลรายละเอียด (detail data)

ผมสร้าง contract สองรูปแบบที่แตกต่างกัน:

• A list contract สำหรับข้อมูลสรุป (ID, title, status, dates) • A detail contract สำหรับข้อมูลฉบับเต็ม (descriptions, commands)

ผมแยก API ออกเป็นสอง endpoint:

  • GET /features (คืนค่าข้อมูลสรุป)
  • GET /features/:id (คืนค่ารายละเอียดฉบับเต็มของรายการเดียว)

ผมยังเปลี่ยนวิธีการทำงานของ frontend ด้วย ตอนนี้แอปจะแสดงรายการทันทีโดยใช้ข้อมูลสรุป เมื่อคุณคลิกที่ตั๋ว แอปจะแสดงโครงร่าง (shell) ขึ้นมาก่อน แล้วจึงไปดึงข้อมูลรายละเอียดที่มีขนาดใหญ่ในพื้นหลัง (background)

ผลลัพธ์ที่ได้นั้นมหาศาล:

• Feature list API: ลดลง 89.6% • Tickets route data: ลดลง 91.4% • OpenSpec docs API: ลดลง 98.9%

ฐานข้อมูลนั้นเร็วอยู่แล้วเสมอ แต่คอขวดที่แท้จริงคือ data contract ระหว่าง API และหน้าเว็บ

บทเรียนสำหรับแอปที่มีรายการข้อมูลจำนวนมาก:

  • วัดขนาดของ response ด้วย ไม่ใช่แค่เวลาในการตอบสนอง (response time)
  • แยก payload ของหน้า list และหน้า detail ออกจากกัน
  • อย่าส่งฟิลด์ข้อความขนาดใหญ่กลับมาในหน้า list view
  • ตรวจสอบว่า SSR loader ของคุณกำลัง serialize ข้อมูลอะไรอยู่กันแน่
  • ทำ lazy-load สำหรับข้อมูลรายละเอียด แต่ให้แสดง UI shell ทันที
  • อย่าใช้ loading spinner เพื่อปกปิด payload ที่มีขนาดใหญ่

การ query ที่รวดเร็ว ไม่ได้การันตีว่าหน้าเว็บจะโหลดเร็วเสมอไป

แหล่งที่มา: https://dev.to/ahikmah/my-api-responded-in-4-ms-but-navigation-still-felt-slow-1hk8