อัปเกรดฟังก์ชัน 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) => {
  /* ... */
});

อัปเดตบัญชีบริการเริ่มต้น (ไม่บังคับ)

แม้ว่าฟังก์ชันรุ่นที่ 1 จะใช้บัญชีบริการเริ่มต้นของ Google App Engine เพื่อ ให้สิทธิ์เข้าถึง Firebase API แต่ฟังก์ชันรุ่นที่ 2 จะใช้บัญชีบริการเริ่มต้นของ Compute Engine ความแตกต่างนี้อาจทำให้เกิดปัญหาเกี่ยวกับสิทธิ์สำหรับฟังก์ชันที่ย้ายข้อมูลไปยังรุ่นที่ 2 ในกรณีที่คุณให้สิทธิ์พิเศษแก่บัญชีบริการรุ่นที่ 1 หากไม่ได้เปลี่ยนสิทธิ์ของบัญชีบริการ ให้ข้ามขั้นตอนนี้

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

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

// Use the App Engine default service account for all functions
setGlobalOptions({serviceAccountEmail: '<my-project-number>@<wbr>appspot.gserviceaccount.com'});

// Now I use the App Engine default service account.
exports.date = onRequest({cors: true}, (req, res) => {
  // ...
});

// I do too!
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  // ...
});

หรือคุณอาจแก้ไขรายละเอียดบัญชีบริการให้ตรงกับสิทธิ์ที่จำเป็นทั้งหมดในทั้งบัญชีบริการเริ่มต้นของ App Engine (สำหรับรุ่นที่ 1) และบัญชีบริการเริ่มต้นของ Compute Engine (สำหรับรุ่นที่ 2)

ใช้การทำงานพร้อมกัน

ข้อได้เปรียบที่สำคัญของฟังก์ชันรุ่นที่ 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 (ไม่บังคับ)