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

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

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

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

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

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