เอกสารนี้อธิบายวิธีขอให้ฟังก์ชันเบื้องหลังแบบอะซิงโครนัส (ไม่ใช่ HTTPS) ลองใหม่เมื่อดำเนินการไม่สำเร็จ
สาเหตุที่ฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์ทำงานไม่เสร็จสมบูรณ์
ในบางกรณีที่พบได้ไม่บ่อยนัก ฟังก์ชันอาจออกก่อนเวลาอันควรเนื่องจากข้อผิดพลาดภายใน และโดยค่าเริ่มต้น ระบบอาจลองใช้ฟังก์ชันอีกครั้งโดยอัตโนมัติหรือไม่ก็ได้
โดยทั่วไปแล้ว ฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์อาจทำงานไม่สำเร็จเนื่องจากข้อผิดพลาดที่เกิดขึ้นในโค้ดของฟังก์ชันเอง สาเหตุที่อาจทำให้เกิดปัญหานี้มีดังนี้
- ฟังก์ชันมีข้อบกพร่องและรันไทม์แสดงข้อยกเว้น
- ฟังก์ชันเข้าถึงปลายทางของบริการไม่ได้ หรือหมดเวลาขณะพยายามเข้าถึง
- ฟังก์ชันจะแสดงข้อยกเว้นโดยเจตนา (เช่น เมื่อพารามิเตอร์ไม่ผ่านการตรวจสอบ)
- ฟังก์ชัน Node.js แสดงผล Promise ที่ถูกปฏิเสธ หรือส่งค่าที่ไม่ใช่
nullไปยัง Callback
ในกรณีข้างต้น ฟังก์ชันจะหยุดทำงานและแสดงข้อผิดพลาด ทริกเกอร์เหตุการณ์ที่สร้างข้อความมีนโยบายการลองใหม่ซึ่งคุณสามารถปรับแต่งให้ตรงกับความต้องการของฟังก์ชันได้
ความหมายของการลองใหม่
Cloud Functions มีการดำเนินการอย่างน้อย 1 ครั้งของฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์สำหรับแต่ละเหตุการณ์ที่เกิดจากแหล่งที่มาของเหตุการณ์ โดยค่าเริ่มต้น หากการเรียกใช้ฟังก์ชันสิ้นสุดลงพร้อมข้อผิดพลาด ระบบจะไม่เรียกใช้ฟังก์ชันอีกและจะทิ้งเหตุการณ์นั้น เมื่อคุณเปิดใช้การลองใหม่ในฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์ Cloud Functions จะลองเรียกใช้ฟังก์ชันที่ล้มเหลวอีกครั้งจนกว่าจะสำเร็จหรือกรอบเวลาการลองใหม่หมดอายุ
เมื่อไม่ได้เปิดใช้การลองใหม่สำหรับฟังก์ชัน ซึ่งเป็นค่าเริ่มต้น ฟังก์ชันจะรายงานเสมอว่าดำเนินการสำเร็จ และโค้ดตอบกลับ 200 OK อาจปรากฏในบันทึก ซึ่งจะเกิดขึ้นแม้ว่าฟังก์ชันจะพบข้อผิดพลาดก็ตาม หากต้องการให้เห็นชัดเจนเมื่อฟังก์ชันพบข้อผิดพลาด โปรดรายงานข้อผิดพลาดอย่างเหมาะสม
กำหนดค่าการลองใหม่จากโค้ดฟังก์ชัน
เมื่อใช้ Cloud Functions for Firebase คุณจะเปิดใช้การลองใหม่ในโค้ดสำหรับฟังก์ชันได้ หากต้องการดำเนินการนี้สำหรับเหตุการณ์เบื้องหลัง เช่น การสร้างเอกสาร Firestore ใหม่ ให้ตั้งค่าตัวเลือกนโยบาย failurePolicy (รุ่นที่ 1) หรือ retry (รุ่นที่ 2) เป็น true ดังนี้
รุ่นที่ 1
exports.docCreated = functions
.runWith({
// retry on failure
failurePolicy: true,
})
.firestore.document("my-collection/{docId}")
.onCreate((change, context) => {
/* ... */
});
รุ่นที่ 2
const { onDocumentCreated } = require("firebase-functions/firestore");
exports.docCreated = onDocumentCreated(
{
// retry on failure
retry: true,
},
"my-collection/{docId}",
(event) => {
/* ... */
},
);
การตั้งค่า true ตามที่แสดงจะกำหนดค่าฟังก์ชันให้ลองอีกครั้งเมื่อดำเนินการไม่สำเร็จ
หน้าต่างลองใหม่
สำหรับฟังก์ชันรุ่นที่ 2 กรอบเวลาการลองใหม่นี้จะหมดอายุหลังจากผ่านไป 24 ชั่วโมง สำหรับฟังก์ชันรุ่นที่ 1 ฟังก์ชันจะหมดอายุหลังจากผ่านไป 7 วัน Cloud Functions จะลองใช้ฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์ที่สร้างขึ้นใหม่โดยใช้กลยุทธ์ Exponential Backoff โดยเพิ่ม Backoff ระหว่าง 10 ถึง 600 วินาที ระบบจะใช้นโยบายนี้กับฟังก์ชันใหม่ในครั้งแรกที่คุณติดตั้งใช้งาน และจะไม่มีผลย้อนหลังกับฟังก์ชันที่มีอยู่ซึ่งติดตั้งใช้งานครั้งแรกก่อนที่การเปลี่ยนแปลงที่อธิบายไว้ในบันทึกประจำรุ่นนี้จะมีผล แม้ว่าคุณจะติดตั้งใช้งานฟังก์ชันอีกครั้งก็ตามแนวทางปฏิบัติแนะนำ
ส่วนนี้จะอธิบายแนวทางปฏิบัติแนะนำสำหรับการใช้การลองใหม่
ใช้การลองใหม่เพื่อจัดการข้อผิดพลาดแบบชั่วคราว
เนื่องจากระบบจะลองเรียกใช้ฟังก์ชันซ้ำอย่างต่อเนื่องจนกว่าจะดำเนินการสำเร็จ คุณจึงควรนำข้อผิดพลาดถาวร เช่น ข้อบกพร่อง ออกจากโค้ดผ่านการทดสอบก่อนเปิดใช้การลองใหม่ การลองใหม่เหมาะที่สุดสำหรับการจัดการกับความล้มเหลวเป็นระยะๆ หรือชั่วคราวซึ่งมีแนวโน้มสูงที่จะแก้ไขได้เมื่อลองใหม่ เช่น ปลายทางบริการที่ไม่เสถียรหรือหมดเวลา
กำหนดเงื่อนไขการสิ้นสุดเพื่อหลีกเลี่ยงการวนซ้ำการลองใหม่แบบไม่สิ้นสุด
แนวทางปฏิบัติแนะนำคือการปกป้องฟังก์ชันจากการวนซ้ำอย่างต่อเนื่องเมื่อใช้การลองใหม่ ซึ่งทำได้โดยการใส่เงื่อนไขการสิ้นสุดที่ชัดเจนก่อนที่ฟังก์ชันจะเริ่มประมวลผล โปรดทราบว่าเทคนิคนี้จะใช้ได้ก็ต่อเมื่อฟังก์ชันเริ่มทำงานได้สำเร็จและสามารถประเมินเงื่อนไขสิ้นสุดได้
วิธีที่ง่ายแต่มีประสิทธิภาพคือการทิ้งเหตุการณ์ที่มีการประทับเวลาเก่ากว่าเวลาที่กำหนด ซึ่งจะช่วยหลีกเลี่ยงการดำเนินการที่มากเกินไปเมื่อเกิดความล้มเหลวอย่างต่อเนื่องหรือนานกว่าที่คาดไว้
ตัวอย่างเช่น ข้อมูลโค้ดนี้จะทิ้งเหตุการณ์ทั้งหมดที่เก่ากว่า 10 วินาที
const eventAgeMs = Date.now() - Date.parse(event.timestamp);
const eventMaxAgeMs = 10000;
if (eventAgeMs > eventMaxAgeMs) {
console.log(`Dropping event ${event} with age[ms]: ${eventAgeMs}`);
callback();
return;
}
ใช้ catch กับ Promises
หากฟังก์ชันเปิดใช้การลองใหม่ ข้อผิดพลาดที่ไม่ได้จัดการจะทริกเกอร์การลองใหม่ ตรวจสอบว่าโค้ดของคุณตรวจจับข้อผิดพลาดที่ไม่ควรส่งผลให้เกิดการลองใหม่
ตัวอย่างสิ่งที่คุณควรทำมีดังนี้
return doFooAsync().catch((err) => {
if (isFatal(err)) {
console.error(`Fatal error ${err}`);
}
return Promise.reject(err);
});
ทำให้ฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์ที่ลองใหม่ได้เป็นแบบ Idempotent
ฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์ซึ่งลองใหม่ได้ต้องเป็นแบบ Idempotent หลักเกณฑ์ทั่วไปในการทำให้ฟังก์ชันดังกล่าวเป็นแบบ Idempotent มีดังนี้
- API ภายนอกจำนวนมาก (เช่น Stripe) ให้คุณระบุคีย์ Idempotency เป็นพารามิเตอร์ได้ หากใช้ API ดังกล่าว คุณควรใช้รหัสเหตุการณ์เป็นคีย์ Idempotency
- Idempotency ทำงานได้ดีกับการนำส่งอย่างน้อย 1 ครั้ง เนื่องจากทำให้ลองใหม่ได้อย่างปลอดภัย ดังนั้น แนวทางปฏิบัติแนะนำทั่วไปในการเขียนโค้ดที่เชื่อถือได้คือการรวม Idempotency เข้ากับการลองใหม่
- ตรวจสอบว่าโค้ดของคุณเป็นแบบ Idempotent ภายใน เช่น
- ตรวจสอบว่าการเปลี่ยนแปลงเกิดขึ้นได้มากกว่า 1 ครั้งโดยไม่เปลี่ยนผลลัพธ์
- ค้นหาสถานะฐานข้อมูลในธุรกรรมก่อนที่จะเปลี่ยนแปลงสถานะ
- ตรวจสอบว่าผลข้างเคียงทั้งหมดเป็นแบบ Idempotent
- กำหนดการตรวจสอบธุรกรรมนอกฟังก์ชัน โดยไม่ขึ้นอยู่กับโค้ด เช่น คงสถานะไว้ที่ใดที่หนึ่งเพื่อบันทึกว่ารหัสเหตุการณ์หนึ่งๆ ได้รับการประมวลผลแล้ว
- จัดการกับการเรียกใช้ฟังก์ชันที่ซ้ำกันนอกแบนด์ เช่น มีกระบวนการล้างข้อมูลแยกต่างหากที่ล้างข้อมูลหลังจากการเรียกใช้ฟังก์ชันที่ซ้ำกัน
กำหนดค่านโยบายการลองใหม่
คุณอาจต้องกำหนดค่านโยบายการลองใหม่โดยตรง ทั้งนี้ขึ้นอยู่กับความต้องการของฟังก์ชัน ซึ่งจะช่วยให้คุณตั้งค่าชุดค่าผสมต่อไปนี้ได้
- ลดกรอบเวลาการลองใหม่จาก 7 วันเหลือเพียง 10 นาที
- เปลี่ยนเวลา Backoff ต่ำสุดและสูงสุดสำหรับกลยุทธ์การลองใหม่แบบ Exponential Backoff
- เปลี่ยนกลยุทธ์การลองใหม่เป็นลองใหม่ทันที
- กำหนดค่าหัวข้อ Dead Letter
- กำหนดจำนวนครั้งสูงสุดและต่ำสุดในการพยายามนำส่ง
วิธีกำหนดค่านโยบายการลองใหม่
- เขียนฟังก์ชัน HTTP
- ใช้ Pub/Sub API เพื่อสร้างการสมัครใช้บริการ Pub/Sub โดยระบุ URL ของฟังก์ชันเป็นเป้าหมาย
ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดค่า Pub/Sub โดยตรงได้ที่เอกสารประกอบของ Pub/Sub เกี่ยวกับการจัดการข้อผิดพลาด