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

เอกสารนี้อธิบายวิธีขอให้ฟังก์ชันเบื้องหลังแบบอะซิงโครนัส (ที่ไม่ใช่ HTTPS) ลองใหม่เมื่อล้มเหลว

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

ในบางครั้ง ฟังก์ชันอาจหยุดทำงานก่อนเวลาอันควรเนื่องจากข้อผิดพลาดภายใน และโดยค่าเริ่มต้น ระบบอาจลองฟังก์ชันใหม่โดยอัตโนมัติหรือไม่ก็ได้

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

  • ฟังก์ชันมีข้อบกพร่องและรันไทม์จะแสดงข้อยกเว้น
  • ฟังก์ชันไม่สามารถเข้าถึงปลายทางของบริการหรือหมดเวลาขณะพยายามเข้าถึง
  • ฟังก์ชันจะแสดงข้อยกเว้นโดยเจตนา (เช่น เมื่อพารามิเตอร์ไม่ผ่านการตรวจสอบ)
  • ฟังก์ชัน Node.js จะแสดงผล Promise ที่ถูกปฏิเสธ หรือส่งค่าที่ไม่ใช่ null ไปยัง Callback

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

ความหมายของการลองใหม่

Cloud Functions จะเรียกใช้ฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์อย่างน้อย 1 ครั้ง สำหรับแต่ละเหตุการณ์ที่แหล่งที่มาของเหตุการณ์ปล่อยออกมา โดยค่าเริ่มต้น หากการเรียกใช้ฟังก์ชันสิ้นสุดลงด้วยข้อผิดพลาด ระบบจะไม่เรียกใช้ฟังก์ชันอีกครั้งและจะทิ้งเหตุการณ์นั้น เมื่อคุณเปิดใช้การลองใหม่ในฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์ Cloud Functions จะลองเรียกใช้ฟังก์ชันที่ล้มเหลวอีกครั้งจนกว่าจะทำงานเสร็จสมบูรณ์ หรือกรอบเวลาการลองใหม่จะหมดอายุ

เมื่อไม่ได้เปิดใช้การลองใหม่สำหรับฟังก์ชัน (ซึ่งเป็นค่าเริ่มต้น) ฟังก์ชันจะรายงานเสมอว่าทำงานสำเร็จ และโค้ดตอบกลับ 200 OK อาจปรากฏในบันทึก ซึ่งจะเกิดขึ้นแม้ว่าฟังก์ชันจะพบข้อผิดพลาดก็ตาม โปรด รายงานข้อผิดพลาด อย่างเหมาะสมเพื่อให้ ทราบอย่างชัดเจนเมื่อฟังก์ชันพบข้อผิดพลาด

กำหนดค่าการลองใหม่จากโค้ดฟังก์ชัน

With Cloud Functions for Firebase, คุณสามารถเปิดใช้การลองใหม่ในโค้ดสำหรับa ฟังก์ชันได้ หากต้องการดำเนินการนี้สำหรับเหตุการณ์เบื้องหลัง เช่น การสร้างเอกสาร 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 กับสัญญา

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

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

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

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

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

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

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

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

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

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

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

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