ลองใช้ฟังก์ชันแบบอะซิงโครนัสอีกครั้ง

เอกสารนี้อธิบายวิธีขอฟังก์ชันพื้นหลังแบบอะซิงโครนัส (ไม่ใช่ 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 โดยจะเพิ่มการหยุดชั่วคราวระหว่าง 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 กับ Promise

หากฟังก์ชันเปิดใช้การลองใหม่ ข้อผิดพลาดที่ไม่ได้จัดการจะทําให้เกิดการลองใหม่ ตรวจสอบว่าโค้ดจับข้อผิดพลาดที่ไม่ควรทำให้เกิดการลองใหม่

ตัวอย่างสิ่งที่คุณควรทำมีดังนี้

return doFooAsync().catch((err) => {
    if (isFatal(err)) {
        console.error(`Fatal error ${err}`);
    }
    return Promise.reject(err);
});

ทำให้ฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์ที่ลองใหม่ได้เป็นฟังก์ชัน Idempotent

ฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์ซึ่งลองอีกครั้งได้ต้องเป็นฟังก์ชันที่ไม่มีผลข้างเคียง ต่อไปนี้คือหลักเกณฑ์ทั่วไปสำหรับการทำให้ฟังก์ชันดังกล่าวเป็นแบบ Idempotent

  • API ภายนอกหลายรายการ (เช่น Stripe) อนุญาตให้คุณระบุคีย์การดำเนินการซ้ำ เป็นพารามิเตอร์ หากคุณใช้ API ดังกล่าว คุณควรใช้รหัสเหตุการณ์เป็น คีย์การดำเนินการซ้ำ
  • Idempotency ทำงานได้ดีกับการนำส่งแบบ "อย่างน้อย 1 ครั้ง" เนื่องจากทำให้การลองใหม่ปลอดภัย ดังนั้นแนวทางปฏิบัติแนะนำทั่วไปในการเขียนโค้ดที่เชื่อถือได้คือการรวม การดำเนินการซ้ำกับความสามารถในการดำเนินการซ้ำ
  • ตรวจสอบว่าโค้ดของคุณเป็น Idempotent ภายใน เช่น
    • ตรวจสอบว่าการเปลี่ยนแปลงเกิดขึ้นได้มากกว่า 1 ครั้งโดยไม่เปลี่ยน ผลลัพธ์
    • ค้นหาสถานะฐานข้อมูลในธุรกรรมก่อนที่จะเปลี่ยนสถานะ
    • ตรวจสอบว่าผลข้างเคียงทั้งหมดเป็นแบบ Idempotent
  • กำหนดการตรวจสอบธุรกรรมภายนอกฟังก์ชันโดยไม่ขึ้นอยู่กับโค้ด เช่น เก็บสถานะไว้ที่ใดที่หนึ่งเพื่อบันทึกว่ามีการประมวลผลรหัสเหตุการณ์ที่ระบุแล้ว
  • จัดการการเรียกใช้ฟังก์ชันที่ซ้ำกันนอกแบนด์ เช่น มีกระบวนการล้างข้อมูลแยกต่างหาก ซึ่งจะล้างข้อมูลหลังจากมีการเรียกฟังก์ชันที่ซ้ำกัน

กำหนดค่านโยบายการลองใหม่

คุณอาจต้องกำหนดค่านโยบายการลองใหม่โดยตรง ทั้งนี้ขึ้นอยู่กับความต้องการของฟังก์ชัน ซึ่งจะช่วยให้คุณตั้งค่าการผสมผสานใดๆ ต่อไปนี้ได้

  • ลดกรอบเวลาลองอีกครั้งจาก 7 วันเหลือเพียง 10 นาที
  • เปลี่ยนเวลาหยุดชั่วคราวขั้นต่ำและสูงสุดสำหรับกลยุทธ์การลองใหม่แบบ Exponential Backoff
  • เปลี่ยนกลยุทธ์การลองใหม่เป็นการลองใหม่ทันที
  • กำหนดค่าหัวข้อจดหมายที่ส่งไม่ได้
  • กำหนดจำนวนครั้งสูงสุดและต่ำสุดในการพยายามนำส่ง

วิธีกำหนดค่านโยบายการลองใหม่

  1. เขียนฟังก์ชัน HTTP
  2. ใช้ Pub/Sub API เพื่อสร้างการสมัครใช้บริการ Pub/Sub โดยระบุ URL ของฟังก์ชันเป็นเป้าหมาย

ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดค่า Pub/Sub โดยตรงได้ในเอกสารประกอบ Pub/Sub เกี่ยวกับการจัดการข้อผิดพลาด