← กลับไปหน้าบทความ
ขั้นสูง (MarTech)

Vercel Serverless Function กับ Edge Function รับ Webhook ต่างกันตรงไหนเรื่อง Timeout

ทีมบรรณาธิการ linli12 ส.ค. 04:53อัปเดต 12 ส.ค. 04:53อ่าน 5 นาที
Vercel Serverless Function กับ Edge Function รับ Webhook ต่างกันตรงไหนเรื่อง Timeout
บทความนี้จัดทำเพื่อความรู้ ชื่อธุรกิจ ตัวเลข หรือบทสนทนาที่ไม่มีแหล่งอ้างอิงกำกับเป็นกรณีตัวอย่างเพื่ออธิบายแนวคิด ดูรายละเอียดที่ นโยบายกองบรรณาธิการ

สรุปสั้น ๆ

Vercel มี Function สองแบบสำหรับรับ webhook คือ Node.js Serverless Function ที่รองรับ Node API เต็มรูปแบบแต่มีเพดานเวลาทำงานจำกัดตามแพ็กเกจ กับ Edge Function ที่เริ่มทำงานเร็วกว่าแต่มี Node API จำกัดคล้าย Cloudflare Workers ตัว Vercel เองไม่ได้ retry การเรียก function ที่ล้มเหลวให้อัตโนมัติ ความรับผิดชอบเรื่อง retry จึงอยู่ที่ผู้ส่ง webhook ทำให้ต้องออกแบบ idempotency ผ่าน storage ภายนอกเองเสมอไม่ว่าจะเลือก function แบบไหน

ทีมที่สร้าง webhook receiver บน Vercel มักเจอทางแยกแรกตั้งแต่เริ่มต้นคือควรใช้ Node.js Serverless Function แบบดั้งเดิม หรือ Edge Function ที่ Vercel เปิดให้ใช้เพิ่มเติมในภายหลัง ทั้งสองแบบรับ HTTP request และตอบ response ได้เหมือนกันในภาพกว้าง แต่มีรายละเอียดที่ต่างกันมากพอจะทำให้ webhook ทำงานไม่ได้ตามที่คาดถ้าเลือกผิดสำหรับ workload ที่ต้องรับมือ

ปัญหาที่ทีมมักมองข้ามคือเรื่อง timeout ที่ต่างกันตามแพ็กเกจและประเภท function ที่เลือกใช้ รวมถึงความเข้าใจผิดว่า Vercel จะ retry การเรียก function ให้เองถ้าล้มเหลว ทั้งที่จริงแล้วความรับผิดชอบเรื่อง retry เป็นของผู้ส่ง webhook ฝั่งเดียว ทำให้การออกแบบ idempotency ต้องทำเองเสมอไม่ว่าจะเลือก function ประเภทไหน

บทความนี้จะไล่ความต่างระหว่าง Node.js Serverless Function กับ Edge Function บน Vercel โดยเฉพาะในบริบทของการรับ webhook พร้อมชี้จุดที่ทำให้ webhook ล้มเหลวเงียบ ๆ ถ้าไม่เข้าใจกลไกเรื่อง timeout และ retry ให้ถูกต้อง

Vercel มี Function สองแบบสำหรับรับ Webhook: Node.js Serverless กับ Edge Runtime

Node.js Serverless Function เป็นรูปแบบดั้งเดิมที่ Vercel รองรับมาตั้งแต่ต้น รันบน Node runtime เต็มรูปแบบ เข้าถึง Node API ได้ครบ เช่น filesystem ชั่วคราวหรือ library ที่พึ่งพา native binding เฉพาะแพลตฟอร์ม เหมาะกับ webhook ที่ logic ซับซ้อนหรือต้องใช้ library ที่ยังไม่รองรับ edge runtime

Edge Function เป็นตัวเลือกที่เพิ่มมาทีหลัง รันบน runtime ที่ใกล้เคียง Web API มาตรฐานคล้ายกับ Cloudflare Workers เริ่มทำงานเร็วกว่าเพราะ isolate เบากว่า container ของ Node function แต่ก็มีข้อจำกัดคล้ายกันคือ Node API เฉพาะทางบางตัวใช้ไม่ได้ตรง ๆ ต้องตรวจสอบ compatibility ของ dependency ก่อนเลือกใช้

การเลือกระหว่างสองแบบนี้สำหรับ webhook receiver ควรพิจารณาจาก logic ที่ต้องทำหลังตรวจ signature เป็นหลัก ถ้าต้องเรียก library เฉพาะทางของ Node หรือทำงานที่ใช้เวลานานและซับซ้อน Node.js Serverless Function อาจเหมาะกว่า ถ้า logic เบาและเน้นตอบเร็วเป็นหลัก Edge Function อาจได้เปรียบเรื่อง cold start ที่สั้นกว่า

Timeout ต่างกันตามแพ็กเกจ Vercel ส่งผลต่อ Webhook ยังไง

เพดานเวลาทำงานสูงสุดต่อ request ของ Vercel Function ไม่เท่ากันในทุกแพ็กเกจ แพ็กเกจฟรีมีเพดานสั้นกว่าแพ็กเกจที่เสียเงิน และ Edge Function มักมีเพดานเวลาที่ต่างจาก Node.js Serverless Function ในแพ็กเกจเดียวกันด้วย ทีมที่ออกแบบ webhook receiver ที่มี logic ทำงานนาน เช่นเรียก API ภายนอกหลายตัวต่อกันก่อนตอบ response ควรตรวจเพดานเวลาจริงของแพ็กเกจที่ใช้อยู่ ไม่ใช่สมมติว่าจะได้เวลาเท่ากันทุกที่

ปัญหาที่เกิดขึ้นจริงคือ webhook ที่ logic ทำงานใกล้เพดานเวลาพอดี อาจทำงานสำเร็จเกือบทุกครั้งตอนทดสอบ แต่พอเข้า production ที่มี load สูงกว่าเดิมเล็กน้อย เวลาที่ใช้จริงอาจเกินเพดานได้บ้างเป็นครั้งคราว ทำให้บาง request ล้มเหลวแบบสุ่ม ๆ ที่ debug ยาก เพราะดูจาก log แล้ว logic ก็ถูกต้องทุกอย่าง เพียงแต่ใช้เวลานานเกินเพดานพอดีในบาง request

แนวทางที่ปลอดภัยกว่าคือออกแบบ webhook receiver ให้ทำเฉพาะงานที่จำเป็นก่อนตอบ response เช่นตรวจ signature และบันทึกว่าได้รับ event แล้วเท่านั้น ส่วนงานที่ใช้เวลานานควรแยกไปทำ asynchronous แทน เพื่อไม่ให้เวลาทำงานรวมต่อ request เข้าใกล้เพดานเวลาของแพ็กเกจที่ใช้อยู่ ไม่ว่าจะเลือก function ประเภทไหนก็ตาม

Retry เกิดจากฝั่งไหน Vercel เองไม่ Retry แต่ผู้ส่ง Webhook อาจ Retry

ความเข้าใจผิดที่พบบ่อยคือคิดว่า Vercel จะ retry การเรียก function ให้อัตโนมัติถ้า function นั้นล้มเหลวหรือ timeout แต่ในความเป็นจริง Vercel ทำหน้าที่แค่รัน function ตาม request ที่เข้ามาแล้วส่ง response กลับไปเท่านั้น ไม่มีกลไก retry ฝั่ง Vercel เองสำหรับ invocation ที่ล้มเหลว

ผู้ที่ตัดสินใจว่าจะ retry หรือไม่คือฝั่งผู้ส่ง webhook เข้ามา ถ้าผู้ส่งไม่ได้รับ response ที่ถือว่าสำเร็จภายในเวลาที่กำหนด ผู้ส่งจะตัดสินใจส่ง event เดิมซ้ำเข้ามาเองตามนโยบายของแต่ละราย ซึ่งบางรายไม่มีนโยบาย retry เลย ถ้า request ล้มเหลวแล้ว event นั้นอาจหายไปเลยโดยไม่มีใครแจ้งเตือน

ผลที่ตามมาคือทีมต้องออกแบบสองเรื่องคู่กัน คือทำให้ function ตอบ response สำเร็จให้ได้มากที่สุดเท่าที่จะทำได้เพื่อไม่ให้ต้องพึ่งพา retry จากผู้ส่ง และมีระบบ monitoring แยกต่างหากที่ตรวจจับได้เองถ้า webhook endpoint เริ่มมี error หรือ timeout สูงผิดปกติ ไม่ใช่รอให้ผู้ส่ง webhook มาแจ้งปัญหาก่อนถึงจะรู้ตัว

Idempotency บน Vercel Function ต้องพึ่ง Storage ภายนอกเสมอ

เพราะ Vercel Function เป็น serverless ที่ instance แต่ละตัวไม่มี state ค้างระหว่าง request ต่อกัน การเก็บสถานะว่า event ไหนเคยประมวลผลไปแล้วบ้างต้องพึ่ง storage ภายนอกเสมอ ไม่สามารถเก็บไว้ใน memory ของ function แล้วหวังว่า instance เดิมจะรับ request ถัดไปได้ เพราะ Vercel อาจสร้าง instance ใหม่รับ request แต่ละครั้งโดยไม่รับประกันว่าจะเป็น instance เดิม

ตัวเลือก storage ที่ใช้กันจริงมีหลายแบบ เช่น Vercel KV สำหรับเก็บสถานะแบบ key-value ที่อ่านเร็ว หรือฐานข้อมูลอย่าง Supabase Postgres ที่ใช้ unique constraint ช่วยกัน event ซ้ำที่ระดับฐานข้อมูลเอง ซึ่งเป็นแนวทางเดียวกับที่ใช้ได้ผลบน Supabase Edge Functions ตามที่อธิบายไว้ในบทความ สร้าง Webhook ด้วย Supabase Edge Functions ทีมที่ใช้ทั้ง Vercel และ Supabase อยู่แล้วในระบบเดียวกันสามารถใช้ตารางเดียวกันนี้เก็บสถานะ idempotency ของ webhook จากหลายแหล่งได้

จุดสำคัญคือต้องเขียนโค้ดตรวจสอบและบันทึกสถานะให้ทนต่อ race condition เมื่อมี retry สอง request เข้ามาพร้อมกัน ไม่ใช่แค่ตรวจสอบก่อนแล้วค่อยเขียนแยกสองขั้นตอนธรรมดา เพราะช่วงเวลาสั้น ๆ ระหว่างสองขั้นตอนนั้นอาจมี request อื่นแทรกเข้ามาได้เสมอในสภาพแวดล้อม serverless ที่ scale ขึ้นหลาย instance พร้อมกัน

  1. รับ identifier เฉพาะตัวของ event จาก header หรือ payload ก่อนทำงานอื่น
  2. พยายามบันทึก identifier นี้ลง storage ภายนอกด้วยคำสั่งที่ป้องกันการเขียนซ้ำในตัว เช่น unique constraint หรือ atomic operation
  3. ถ้าบันทึกสำเร็จ แสดงว่าเป็น event ใหม่ ให้ประมวลผลต่อตามปกติ
  4. ถ้าบันทึกไม่สำเร็จเพราะ identifier นี้มีอยู่แล้ว แสดงว่าเป็น retry ของ event เดิม ให้ตอบ response สำเร็จกลับไปทันทีโดยไม่ทำงานซ้ำ

ตอบเร็วโดยไม่บล็อก Client ด้วยการแยกงานหลังตอบ Response

เช่นเดียวกับแนวทางที่ใช้ได้ผลบน serverless platform อื่น การแยกงานที่ไม่จำเป็นต้องเสร็จก่อนตอบ response ออกไปทำหลังตอบแล้ว ช่วยลดเวลาที่ผู้ส่ง webhook ต้องรอได้มาก Vercel Function รองรับการสั่งงานที่ทำต่อในพื้นหลังหลังตอบ response ไปแล้ว ทำให้สามารถตอบสำเร็จกลับไปหาผู้ส่งได้ทันทีที่ตรวจสอบและบันทึก event เสร็จ แล้วค่อยทำงานที่เหลือต่อโดยผู้ส่งไม่ต้องรอ

ข้อควรระวังที่เหมือนกับ platform อื่นคืองานที่ทำหลังตอบ response แล้วต้องมีระบบตรวจสอบความสำเร็จของตัวเอง เพราะผู้ส่ง webhook จะไม่รู้เลยถ้างานพื้นหลังนั้นล้มเหลว ควรมี log หรือ alert แยกต่างหากสำหรับตรวจสอบ ไม่ใช่ปล่อยให้ error เงียบหายไปเฉย ๆ โดยไม่มีใครสังเกตเห็น

สำหรับ webhook ที่ต้องประมวลผลข้อมูลจำนวนมากต่อ event เช่นไฟล์แนบขนาดใหญ่ ควรพิจารณาส่งต่อไปเข้า queue ภายนอกแทนการพยายามทำทุกอย่างในงานพื้นหลังของ function เดียว เพราะแม้จะแยกออกจากเส้นทางตอบ response แล้ว ก็ยังมีเพดานเวลาทำงานรวมของ function อยู่ดีไม่ว่าจะทำก่อนหรือหลังตอบ response ก็ตาม

ตารางเทียบ Node.js Serverless Function กับ Edge Function สำหรับรับ Webhook

สรุปความต่างที่สำคัญที่สุดสำหรับใช้ตัดสินใจเลือก function ประเภทไหนสำหรับ webhook receiver

ประเด็นNode.js Serverless FunctionEdge Function
Node API เฉพาะทางใช้ได้เต็มรูปแบบใช้ได้จำกัด ต้องเช็ค compatibility
ความเร็วในการเริ่มทำงาน (cold start)ช้ากว่าเล็กน้อยเพราะ container หนักกว่าเร็วกว่าเพราะ isolate เบากว่า
เหมาะกับ Logic แบบไหนlogic ซับซ้อนหรือใช้ library เฉพาะทางของ Nodelogic เบา เน้นตอบเร็วเป็นหลัก
ตรวจ Signature ด้วย Web Crypto APIทำได้ผ่าน Node crypto module เช่นกันทำได้ผ่าน Web Crypto API มาตรฐาน

ตัวอย่างสมมติ: Webhook ล้มเหลวเงียบ ๆ เพราะเวลาทำงานใกล้เพดานพอดี

ลองดูตัวอย่างสมมติของทีมที่สร้าง Node.js Serverless Function รับ webhook จากระบบภายนอก โดย logic ภายในเรียก API ภายในอีกสองตัวต่อกันก่อนตอบ response กลับ ตอนทดสอบในเครื่องและ staging เวลาทำงานรวมอยู่ที่ราวสามในสี่ของเพดานเวลาที่แพ็กเกจกำหนดไว้ ทีมจึงคิดว่าปลอดภัยเพียงพอ (ตัวเลขทั้งหมดเป็นตัวอย่างสมมติเพื่อประกอบการอธิบาย)

พอเข้า production จริง ช่วงที่ API ภายในตัวที่สองมี load สูงกว่าปกติเล็กน้อยเพราะมีงานอื่นเรียกใช้พร้อมกัน เวลาตอบสนองของ API นั้นช้าลงกว่าที่เคยวัดไว้ตอนทดสอบ ทำให้เวลาทำงานรวมของ webhook function เกินเพดานเวลาไปเล็กน้อยในบาง request ผู้ส่ง webhook ที่รอ response ไม่ทันจึงตัดสินใจว่า request นั้นล้มเหลว แต่ทีมพัฒนากลับไม่เห็น error ชัดเจนใน log เพราะ function ยังคงทำงานต่อจนเสร็จอยู่ดี เพียงแต่ตอบ response ไปหลังจากผู้ส่งเลิกรอไปแล้ว

เพราะผู้ส่ง webhook รายนี้ไม่มีนโยบาย retry เมื่อ request ไม่สำเร็จ event ที่เกิดปัญหาจึงหายไปเลยโดยไม่มีใครรู้ตัว จนกว่าจะมีคนสังเกตเห็นว่าข้อมูลบางส่วนหายไปจากรายงานสรุปประจำสัปดาห์ ทีมใช้เวลานานพอสมควรกว่าจะสืบย้อนกลับไปเจอสาเหตุที่แท้จริง

ทีมแก้ปัญหาด้วยการปรับ webhook receiver ให้ตอบ response ทันทีหลังตรวจ signature และบันทึก event เสร็จ แล้วย้ายการเรียก API ภายในทั้งสองตัวไปทำแบบ asynchronous หลังตอบ response พร้อมเพิ่ม monitoring แยกต่างหากที่แจ้งเตือนทันทีถ้ามี event ที่ไม่ถูกประมวลผลให้เสร็จภายในเวลาที่กำหนด ไม่ต้องพึ่งพาการสังเกตด้วยตาจากรายงานสรุปอีกต่อไป

ทำแบบนี้แล้วพัง เพราะอะไร

ข้อผิดพลาดที่พบบ่อยเมื่อทีมสร้าง webhook receiver บน Vercel Function โดยเข้าใจผิดเรื่อง timeout และ retry

  • เชื่อว่า Vercel จะ retry การเรียก function ที่ล้มเหลวให้เอง — ทำให้ไม่มีการออกแบบ monitoring หรือ idempotency รองรับ event ที่หายไปเมื่อ function ทำงานไม่ทันเวลา
  • เก็บสถานะ idempotency ไว้ใน memory ของ function — ใช้ไม่ได้จริงเพราะ instance ของ serverless function ไม่รับประกันว่าจะเป็นตัวเดิมทุก request
  • ทำงานที่ใช้เวลานานทั้งหมดก่อนตอบ response — เพิ่มความเสี่ยงที่เวลาทำงานรวมจะเกินเพดานของแพ็กเกจในช่วงที่ระบบภายนอกตอบสนองช้ากว่าปกติ
  • เลือก Edge Function โดยไม่ตรวจ compatibility ของ library ที่ใช้ — เจอ error ตอน build หรือ runtime เพราะ library นั้นพึ่งพา Node API ที่ Edge Function ไม่รองรับ

สรุป

Vercel Function ทั้งสองแบบใช้รับ webhook ได้จริง แต่ต่างกันเรื่อง Node API ที่รองรับและความเร็วในการเริ่มทำงาน ส่วนเรื่อง timeout และ retry เป็นความเข้าใจที่ต้องถูกต้องไม่ว่าจะเลือก function แบบไหน เพราะ Vercel เองไม่ retry ให้ ความรับผิดชอบอยู่ที่การออกแบบ idempotency และ monitoring ของทีมเอง

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

  • Vercel ไม่ retry function ที่ล้มเหลวเอง ต้องออกแบบ idempotency และ monitoring เอง
  • เก็บสถานะ idempotency ผ่าน storage ภายนอกเสมอ ไม่พึ่ง memory ของ function
  • แยกงานหนักออกจากเส้นทางตอบ response หลัก เพื่อลดความเสี่ยงเกินเพดานเวลาของแพ็กเกจ

คำถามที่พบบ่อย

Vercel retry การเรียก function ที่ timeout ให้อัตโนมัติไหม

ไม่ Vercel ไม่มีกลไก retry การเรียก function เองถ้าล้มเหลวหรือ timeout ความรับผิดชอบเรื่อง retry อยู่ที่ผู้ส่ง webhook ซึ่งบางรายอาจไม่มีนโยบาย retry เลยด้วยซ้ำ

ควรเลือก Node.js Serverless Function หรือ Edge Function สำหรับ webhook

ขึ้นกับ logic ที่ต้องทำ ถ้าต้องใช้ library เฉพาะทางของ Node หรือ logic ซับซ้อน ใช้ Node.js Serverless Function ถ้า logic เบาและเน้นตอบเร็ว Edge Function อาจได้เปรียบเรื่อง cold start

เก็บสถานะ idempotency ไว้ที่ไหนดีบน Vercel

ต้องใช้ storage ภายนอกเสมอ เช่น Vercel KV หรือฐานข้อมูลที่มี unique constraint ช่วยกัน event ซ้ำ ไม่สามารถเก็บไว้ใน memory ของ function ได้เพราะ instance ไม่คงที่

เพดานเวลาทำงานของ Vercel Function เท่ากันทุกแพ็กเกจไหม

ไม่เท่ากัน แพ็กเกจฟรีมีเพดานสั้นกว่าแพ็กเกจที่เสียเงิน และ Edge Function กับ Node.js Serverless Function ในแพ็กเกจเดียวกันก็อาจมีเพดานต่างกันด้วย ควรตรวจค่าจริงของแพ็กเกจที่ใช้อยู่

ทำงานหลังตอบ response แล้ว ถ้าล้มเหลวจะรู้ได้ยังไง

ต้องมีระบบ log หรือ alert แยกต่างหากตรวจสอบความสำเร็จของงานพื้นหลัง เพราะผู้ส่ง webhook ได้รับ response สำเร็จไปแล้วจะไม่มีทางรู้ว่างานหลังจากนั้นล้มเหลว

ต้องใช้ queue ภายนอกเสมอไหมเมื่อรับ webhook บน Vercel

ไม่จำเป็นเสมอไป ถ้างานหลังตรวจ signature เบาและเสร็จเร็ว ทำแบบ asynchronous ในงานพื้นหลังของ function เดียวก็เพียงพอ แต่ถ้าต้องประมวลผลข้อมูลจำนวนมากต่อ event ควรพิจารณาส่งต่อเข้า queue แยกต่างหาก

ลองตรวจด้วยตัวเอง

LINE Webhook Payload Inspector

วาง JSON payload จาก LINE แล้วดูว่าเป็น event ชนิดไหน มี field ครบตาม schema ปัจจุบันหรือไม่

ตรวจ payload ฟรี

ใช้ฟรี ไม่ต้องสมัคร ไม่เก็บข้อมูลที่คุณกรอก

วัด Conversion LINE ทุกแบรนด์ รวมศูนย์ที่เดียว

องค์กรที่มีหลายแบรนด์ หลายสาขา หลาย LINE OA — linli รวม Tracking และส่ง Conversion กลับทุกแพลตฟอร์ม พร้อม Custom Limit เริ่มฟรี 14 วัน

ติดต่อทีม Sales

บทความที่เกี่ยวข้อง

ทำไม LINE Webhook ที่ตรวจ Signature ถูกแล้วยังโดน Cloudflare Workers ปฏิเสธ

ทำไม LINE Webhook ที่ตรวจ Signature ถูกแล้วยังโดน Cloudflare Workers ปฏิเสธ

ทำ LINE Webhook ด้วย Cloudflare Workers มีรายละเอียดเฉพาะที่ต่างจาก webhook ทั่วไป ทั้งรูปแบบ signature ของ LINE เอง เพดานเวลา reply token และโครงสร้าง event ที่ต้องแยกจัดการให้ถูก
ทราฟฟิกพุ่งกลางดึกแต่ Cloudflare Workers เชื่อม Supabase Postgres ไว้ทัน แก้ยังไง

ทราฟฟิกพุ่งกลางดึกแต่ Cloudflare Workers เชื่อม Supabase Postgres ไว้ทัน แก้ยังไง

เชื่อม Cloudflare Workers เข้ากับ Supabase Postgres และ Auth ทำได้ตรง ๆ ผ่าน REST API แต่พอ traffic พุ่งกะทันหันมีจุดที่ต้องออกแบบไว้ล่วงหน้า ไม่งั้นจะเจอ error กระจาย
ตั้ง LINE Webhook บน Supabase Edge Functions ออกแบบตาราง Event ยังไงให้ไม่พังตอนข้อมูลเยอะ

ตั้ง LINE Webhook บน Supabase Edge Functions ออกแบบตาราง Event ยังไงให้ไม่พังตอนข้อมูลเยอะ

ทำ LINE Webhook ด้วย Supabase Edge Functions ต้องคิดไกลกว่าแค่ตรวจ signature แล้วเขียนลง Postgres เพราะมีเรื่องออกแบบตาราง Event, เขียนหลาย Event ในครั้งเดียว, จังหวะตอบ Reply Token และสถานะ Follow/Unfollow ที่ต้องจัดการให้ครบ