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

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

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

กระบวนการย้ายข้อมูล

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

ยืนยัน 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 Subpackage เป็นแบบแยกส่วน และเราขอแนะนำให้นำเข้าเฉพาะโมดูลที่ต้องการ เท่านั้น

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

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

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

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

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

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

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

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

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

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/v1");

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

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

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

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/v1");

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 ถึง 1,000

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.

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

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