อัปเกรดฟังก์ชัน Node.js รุ่นที่ 1 เป็นรุ่นที่ 2

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

ตัวอย่างในหน้านี้จะถือว่าคุณกำลังใช้ JavaScript กับโมดูล CommonJS (การนำเข้ารูปแบบ require) แต่หลักการเดียวกันนี้จะใช้กับ JavaScript ที่มี ESM (การนำเข้าสไตล์ import … from รายการ) และ TypeScript ด้วยเช่นกัน

ขั้นตอนการย้ายข้อมูล

ฟังก์ชันรุ่นที่ 1 และ 2 สามารถอยู่ร่วมกันในไฟล์เดียวกันได้ ซึ่งจะช่วยให้คุณทำการย้ายข้อมูลทีละส่วนได้ง่าย ๆ เมื่อคุณพร้อม เราขอแนะนำให้ย้ายข้อมูล 1 ฟังก์ชันทีละรายการ โดยทำการทดสอบและยืนยันก่อนดำเนินการ

ยืนยันเวอร์ชัน Firebase CLI และ firebase-function

ตรวจสอบว่าคุณกำลังใช้ Firebase CLI เวอร์ชัน 12.00 และ firebase-functions เวอร์ชัน 4.3.0 เป็นอย่างน้อย เวอร์ชันใหม่กว่าจะรองรับรุ่นที่ 2 รวมถึงรุ่นที่ 1 ด้วย

อัปเดตการนำเข้า

ระบบนำเข้าฟังก์ชันรุ่นที่ 2 จากแพ็กเกจย่อย v2 ใน SDK ของ firebase-functions เส้นทางการนำเข้าที่แตกต่างกันนี้คือ Firebase CLI ทั้งหมดที่จำเป็นต้องพิจารณาว่าจะทำให้โค้ดฟังก์ชันใช้งานได้เป็นฟังก์ชันรุ่นที่ 1 หรือ 2

แพ็กเกจย่อย v2 เป็นโมดูลย่อย และเราขอแนะนำให้นำเข้าเฉพาะโมดูลที่คุณต้องการเท่านั้น

ก่อน: รุ่นที่ 1

const functions = require("firebase-functions");

หลัง: รุ่นที่ 2

// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

อัปเดตคำจำกัดความของทริกเกอร์

เนื่องจาก SDK รุ่นที่ 2 รองรับการนำเข้าแบบโมดูล โปรดอัปเดตคำจำกัดความของทริกเกอร์เพื่อแสดงการนำเข้าที่เปลี่ยนแปลงจากขั้นตอนก่อนหน้า

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

ก่อน: รุ่นที่ 1

const functions = require("firebase-functions");

exports.date = functions.https.onRequest((req, res) => {
  // ...
});

exports.uppercase = functions.firestore
  .document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

หลัง: รุ่นที่ 2

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

exports.date = onRequest({cors: true}, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

ใช้การกำหนดค่าที่ทำเป็นพารามิเตอร์

ฟังก์ชันรุ่นที่ 2 จะลดการรองรับ functions.config เพื่อให้ได้อินเทอร์เฟซที่ปลอดภัยมากขึ้นสำหรับการกำหนดพารามิเตอร์การกำหนดค่าตามประกาศภายในฐานของโค้ด เมื่อใช้โมดูล params ใหม่ CLI จะบล็อกการทำให้ใช้งานได้ เว้นแต่ว่าพารามิเตอร์ทั้งหมดจะมีค่าที่ถูกต้อง ซึ่งทำให้ฟังก์ชันไม่ได้ทำให้ใช้งานได้โดยที่ขาดการกำหนดค่า

ย้ายข้อมูลไปยังแพ็กเกจย่อย params

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

ก่อน: รุ่นที่ 1

const functions = require("firebase-functions");

exports.date = functions.https.onRequest((req, res) => {
  const date = new Date();
  const formattedDate =
date.toLocaleDateString(functions.config().dateformat);

  // ...
});

หลัง: รุ่นที่ 2

const {onRequest} = require("firebase-functions/v2/https");
const {defineString} = require("firebase-functions/params");

const dateFormat = defineString("DATE_FORMAT");

exports.date = onRequest((req, res) => {
  const date = new Date();
  const formattedDate = date.toLocaleDateString(dateFormat.value());

  // ...
});

ตั้งค่าพารามิเตอร์

ครั้งแรกที่คุณทำให้ใช้งานได้ Firebase CLI จะแจ้งทุกค่าของพารามิเตอร์ และบันทึกค่าในไฟล์ dotenv หากต้องการส่งออกค่าfunctions.config ให้เรียกใช้ firebase functions:config:export

เพื่อความปลอดภัยยิ่งขึ้น คุณสามารถระบุประเภทพารามิเตอร์และกฎการตรวจสอบได้ด้วย

กรณีพิเศษ: คีย์ API

โมดูล params ผสานรวมกับ Cloud Secret Manager ซึ่งทำให้ควบคุมการเข้าถึงค่าที่มีความละเอียดอ่อน เช่น คีย์ API ได้อย่างละเอียด ดูข้อมูลเพิ่มเติมได้ในพารามิเตอร์ลับ

ก่อน: รุ่นที่ 1

const functions = require("firebase-functions");

exports.getQuote = functions.https.onRequest(async (req, res) => {
  const quote = await fetchMotivationalQuote(functions.config().apiKey);
  // ...
});

หลัง: รุ่นที่ 2

const {onRequest} = require("firebase-functions/v2/https");
const {defineSecret} = require("firebase-functions/params");

// Define the secret parameter
const apiKey = defineSecret("API_KEY");

exports.getQuote = onRequest(
  // make the secret available to this function
  { secrets: [apiKey] },
  async (req, res) => {
    // retrieve the value of the secret
    const quote = await fetchMotivationalQuote(apiKey.value());
    // ...
  }
);

ตั้งค่าตัวเลือกรันไทม์

การกำหนดค่าตัวเลือกรันไทม์มีการเปลี่ยนแปลงระหว่างรุ่นที่ 1 กับรุ่นที่ 2 รุ่นที่ 2 ยังเพิ่มความสามารถใหม่ในการตั้งค่าตัวเลือกสำหรับฟังก์ชันทั้งหมด

ก่อน: รุ่นที่ 1

const functions = require("firebase-functions");

exports.date = functions
  .runWith({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  })
  // locate function closest to users
  .region("asia-northeast1")
  .https.onRequest((req, res) => {
    // ...
  });

exports.uppercase = functions
  // locate function closest to users and database
  .region("asia-northeast1")
  .firestore.document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

หลัง: รุ่นที่ 2

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions/v2");

// locate all functions closest to users
setGlobalOptions({ region: "asia-northeast1" });

exports.date = onRequest({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  }, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

ใช้การเกิดขึ้นพร้อมกัน

ข้อดีที่สำคัญของฟังก์ชันรุ่นที่ 2 คือความสามารถในการที่อินสแตนซ์ฟังก์ชันเดียวตอบสนองต่อคำขอมากกว่า 1 รายการพร้อมกัน วิธีนี้จะลดจำนวน Cold Start ที่ผู้ใช้ปลายทางพบได้อย่างมาก ตามค่าเริ่มต้น การเกิดขึ้นพร้อมกันจะตั้งไว้ที่ 80 แต่คุณตั้งเป็นค่าใดก็ได้ตั้งแต่ 1 ถึง 1000

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // set concurrency value
    concurrency: 500
  },
  (req, res) => {
    // ...
});

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

ตรวจสอบการใช้งานตัวแปรร่วม

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

ขณะอัปเกรด คุณสามารถกำหนด CPU ของฟังก์ชันเป็น gcf_gen1 และตั้งค่า concurrency เป็น 1 เพื่อคืนค่าการทำงานของรุ่นที่ 1 ได้โดยทำดังนี้

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // TEMPORARY FIX: remove concurrency
    cpu: "gcf_gen1",
    concurrency: 1
  },
  (req, res) => {
    // ...
});

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

ย้ายการรับส่งข้อมูลไปยังฟังก์ชันรุ่นที่ 2 ใหม่

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

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

Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.

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

  1. เปลี่ยนชื่อฟังก์ชันในโค้ดฟังก์ชัน เช่น เปลี่ยนชื่อ resizeImage เป็น resizeImageSecondGen
  2. ติดตั้งใช้งานฟังก์ชันเพื่อให้ทั้งฟังก์ชันรุ่นที่ 1 เดิมและฟังก์ชันรุ่นที่ 2 ทํางานอยู่
    1. ในกรณีของทริกเกอร์ที่เรียกใช้ได้ คิวงาน และทริกเกอร์ HTTP ให้เริ่มนำไคลเอ็นต์ทั้งหมดไปยังฟังก์ชันรุ่นที่ 2 โดยการอัปเดตรหัสไคลเอ็นต์ด้วยชื่อหรือ URL ของฟังก์ชันรุ่นที่ 2
    2. เมื่อใช้ทริกเกอร์ในเบื้องหลัง ทั้งฟังก์ชันรุ่นที่ 1 และ 2 จะตอบสนองต่อทุกเหตุการณ์ทันทีที่ทำให้ใช้งานได้
  3. เมื่อปิดการย้ายข้อมูลการรับส่งข้อมูลทั้งหมด ให้ลบฟังก์ชันรุ่นที่ 1 โดยใช้คำสั่ง firebase functions:delete ของ Firebase CLI
    1. (ไม่บังคับ) เปลี่ยนชื่อฟังก์ชันรุ่นที่ 2 ให้ตรงกับชื่อฟังก์ชันรุ่นที่ 1