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

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

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

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

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

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

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

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

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

  • ฟังก์ชันที่สร้างในคอนโซล Google Cloud หรือด้วย Cloud RunAdmin API กำหนดให้คุณต้องสร้างและจัดการทริกเกอร์เหตุการณ์แยกต่างหาก ทริกเกอร์มี ลักษณะการทำงานของการลองใหม่เริ่มต้นที่คุณปรับแต่งให้เหมาะกับความต้องการของ ฟังก์ชันได้
  • ฟังก์ชันที่สร้างด้วย Cloud Functions v2 API จะสร้างทริกเกอร์เหตุการณ์ที่จำเป็นโดยนัย เช่น หัวข้อ Pub/Sub หรือทริกเกอร์ Eventarc โดยค่าเริ่มต้น ระบบจะปิดใช้การลองใหม่สำหรับทริกเกอร์เหล่านี้ และเปิดใช้ใหม่ได้โดยใช้ Cloud Functions v2 API

ฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์ซึ่งสร้างด้วย Cloud Run

ฟังก์ชันที่สร้างในคอนโซล Google Cloud หรือด้วย Cloud Run Admin API จะกำหนดให้คุณสร้างและจัดการทริกเกอร์เหตุการณ์แยกกัน เราขอแนะนำเป็นอย่างยิ่ง ให้คุณตรวจสอบลักษณะการทำงานเริ่มต้นของทริกเกอร์แต่ละประเภท

ฟังก์ชันที่ขับเคลื่อนด้วยเหตุการณ์ซึ่งสร้างด้วย Cloud Functions v2 API

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

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

เปิดหรือปิดใช้การลองใหม่

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

Cloud Functions for Firebase ช่วยให้คุณเปิดใช้การลองใหม่ในโค้ดสำหรับฟังก์ชันได้ หากต้องการทำเช่นนี้สำหรับเหตุการณ์ในเบื้องหลัง เช่น การสร้างเอกสาร Firestore ใหม่ ให้ตั้งค่าตัวเลือก failurePolicy (รุ่นที่ 1) หรือretry policy (รุ่นที่ 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 กับ Promise

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

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

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

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

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

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

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

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

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

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

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

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