ข้ามไปยังเนื้อหา

Caching & Edge

หน้า static คือ case ที่ดีที่สุดอยู่แล้ว: build ครั้งเดียว, serve จาก CDN, cache ทุกที่ คำถามที่น่าสนใจคือจะทำยังไงเมื่อ route ถูก render แบบ on-demand — เราไม่อยาก re-run render ให้ทุกผู้ชมถ้าผลลัพธ์เหมือนกันไปสักพัก นั่นคือหน้าที่ของ caching: เปลี่ยน render ที่ทำซ้ำ ๆ ให้เป็น lookup ที่ถูก

สำหรับ route ที่ render แบบ on-demand (prerender = false) เราตั้ง caching header บน response แล้ว browser และ CDN ที่อยู่หน้า server ของเราจะใช้ response ซ้ำแทนที่จะ render ใหม่:

---
export const prerender = false;
// Cache this response at the CDN for 1 hour; allow serving a stale copy
// for a day while revalidating in the background.
Astro.response.headers.set(
'Cache-Control',
'public, max-age=0, s-maxage=3600, stale-while-revalidate=86400'
);
const articles = await getArticles();
---
<ArticleList articles={articles} />

directive สำคัญ: s-maxage คุมว่า shared cache (CDN) เก็บไว้นานแค่ไหน และ stale-while-revalidate ให้ CDN serve สำเนาที่เก่าเล็กน้อยได้ทันทีขณะที่ refresh ใน background — user จึงแทบไม่ต้องรอ cold render

CDN cache response ไว้ที่ location แบบ edge ใกล้ user หน้าที่ cache แล้วจึงถูก serve จากเมืองใกล้ ๆ แทนที่จะเป็น origin ของเรา เว็บ Astro แบบ static ถูก edge-cache โดยธรรมชาติ ส่วนเว็บ on-demand จะ edge-friendly ทันทีที่เราเพิ่ม Cache-Control ที่ถูกต้อง — CDN cache HTML ที่ render แล้วไว้ที่ edge และเรียก server เฉพาะตอน cache หมดอายุ

flowchart LR
  user["User request"] --> edge["CDN edge"]
  edge -->|cache hit| fast["Serve cached HTML (fast)"]
  edge -->|cache miss| origin["Origin renders, then caches at edge"]
  origin --> edge
The edge serves cached HTML; the origin renders only on a miss

pure per-request SSR cache ยาก — ถ้าหน้ามี “Welcome back, Ana” เรา share response นั้นกับผู้ชมคนถัดไปไม่ได้ ทางแก้ที่สวยงามรวมสิ่งที่โมดูลก่อนหน้าสอน: ทำให้ page shell เป็น static/cache ได้ และ defer เฉพาะส่วนที่ personalize ไปเป็น server island (server:defer):

---
// This page is static and fully cacheable at the edge...
---
<Layout>
<ArticleList /> <!-- same for everyone: cached -->
<UserGreeting server:defer> <!-- per-user: deferred, fetched separately -->
<p slot="fallback">Loading…</p>
</UserGreeting>
</Layout>

ทีนี้ส่วนหนักที่ share กันถูก cache ที่ edge และ serve ทันที ส่วน island เล็ก ๆ ที่ personalize ถูก render ต่อ request เราจึงได้หน้าที่เร็วระดับ CDN พร้อม personalization — แทนที่จะทำให้ทั้งหน้า cache ไม่ได้เพราะคำทักทายเดียว

ถ้าหน้าส่วนใหญ่เหมือนกันสำหรับทุกคนโดยมีส่วน dynamic นิดหน่อย static (หรือ cached) + islands มักชนะ full SSR: cache ได้ดีกว่า, server load น้อยกว่า, response เร็วกว่า เก็บ full on-demand rendering ไว้สำหรับหน้าที่ต่างจริง ๆ ทุก request (dashboard ที่ personalize, ผลการ search) การเลือก option ที่ cache ได้ เองก็เป็นการตัดสินใจด้าน performance

สำหรับ route แบบ on-demand จะให้ CDN cache response ได้ยังไง?
`stale-while-revalidate` ให้ผลอะไร?
pattern "cacheable shell + server islands" คืออะไร?
static-with-islands ชนะ full SSR เมื่อไร?