แอปที่ใช้ฟังก์ชันรุ่นที่ 1 ควรพิจารณาย้ายข้อมูลไปยังรุ่นที่ 2 โดยทำตามวิธีการในคู่มือนี้ ฟังก์ชันรุ่นที่ 2 ใช้ Cloud Run เพื่อให้ประสิทธิภาพที่ดีขึ้น การกำหนดค่าที่ดีขึ้น การตรวจสอบที่ดีขึ้น และอื่นๆ
ตัวอย่างในเอกสารนี้ถือว่าคุณใช้ JavaScript กับโมดูล CommonJS (การนำเข้าสไตล์ require) แต่หลักการเดียวกันนี้ใช้กับ JavaScript ที่มี ESM (การนำเข้าสไตล์ import … from) และ TypeScript
กระบวนการย้ายข้อมูล
ฟังก์ชันรุ่นที่ 1 และรุ่นที่ 2 สามารถอยู่ร่วมกันได้ในไฟล์ต้นฉบับเดียวกัน ซึ่งจะช่วยให้คุณย้ายข้อมูลฐานของโค้ดทีละส่วนได้เมื่อพร้อม อย่างไรก็ตาม โปรดทราบว่าการผสมแพ็กเกจนี้จะไม่ทำงานภายในฟังก์ชันเดียวที่แยกกัน
เราขอแนะนำให้ย้ายข้อมูลทีละฟังก์ชัน โดยทำการทดสอบและตรวจสอบก่อนดำเนินการต่อ
ยืนยันเวอร์ชัน Firebase CLI และ firebase-functions
ตรวจสอบว่าคุณใช้ 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/v1");
หลัง: รุ่นที่ 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/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) => {
/* ... */
});
ลดความพยายามในการเขียนใหม่ด้วยการแยกโครงสร้าง JavaScript
หากฟังก์ชันมีเนื้อหาที่ซับซ้อนซึ่งอาศัยบริบทของรุ่นที่ 1 หรือพารามิเตอร์เฉพาะผู้ให้บริการเป็นอย่างมาก (เช่น message หรือ snapshot) คุณสามารถใช้ตัวช่วยความเข้ากันได้กับรุ่นที่ 1 ที่มีอยู่ใน SDK รุ่นที่ 2
SDK รุ่นที่ 2 จะแพตช์ออบเจ็กต์เหตุการณ์โดยอัตโนมัติด้วย Getter ที่ตรงกับซิกเนเจอร์รุ่นที่ 1 ซึ่งจะช่วยให้คุณใช้การแยกโครงสร้าง JavaScript เพื่อดึงพร็อพเพอร์ตี้เหล่านี้ในลายเซ็นของแฮนเดิลได้โดยตรง ซึ่งจะช่วยลดความจำเป็นในการเขียนตรรกะของฟังก์ชันใหม่
ข้อมูลอ้างอิงการแมปผู้ให้บริการ
| ผู้ให้บริการ | อาร์กิวเมนต์รุ่นที่ 1 | การแยกโครงสร้างเหตุการณ์ที่แพตช์แล้วรุ่นที่ 2 |
| Pub/Sub | (message, context)
|
({ message, context }) => { ... }
|
| Cloud Firestore | (snapshot, context)
|
({ snapshot, context }) => { ... }
|
| Cloud Storage | (object, context)
|
({ object, context }) => { ... }
|
| Realtime Database | (snapshot, context)
|
({ snapshot, context }) => { ... }
|
| Remote Config | (version, context)
|
({ version, context }) => { ... }
|
| Cloud Scheduler | (context)
|
({ context }) => { ... }
|
| คิวงาน | (data, context)
|
({ data, context }) => { ... }
|
ก่อน (รุ่นที่ 1):
export const myPubSubV1 = functions.pubsub.topic("my-topic").onPublish((message, context) => {
const data = message.json;
const eventId = context.eventId;
// ... rest of the logic
});
ทางเลือกใหม่ (รุ่นที่ 2 พร้อมการแยกโครงสร้าง):
import { onMessagePublished } from "firebase-functions/v2/pubsub";
export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => {
// No need to change the function body!
const data = message.json; // Uses v1 Message wrapper
const eventId = context.eventId; // Uses v1 EventContext map
// ... rest of the logic
});
ใช้การกำหนดค่าที่มีพารามิเตอร์
ฟังก์ชันรุ่นที่ 2 จะไม่รองรับ functions.config เพื่อสนับสนุนอินเทอร์เฟซที่ปลอดภัยยิ่งขึ้นสำหรับการกำหนดพารามิเตอร์การกำหนดค่าแบบประกาศภายในฐานของโค้ด
โมดูล params ใหม่จะทำให้ CLI บล็อกการทำให้ใช้งานได้ เว้นแต่พารามิเตอร์ทั้งหมดจะมีค่าที่ถูกต้อง เพื่อให้มั่นใจว่าฟังก์ชันจะไม่ถูกทำให้ใช้งานได้โดยไม่มีการกำหนดค่า
ก่อน: รุ่นที่ 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());
// ...
}
);
หากคุณมีการกำหนดค่าสภาพแวดล้อมที่มีอยู่ด้วย functions.config ให้ย้ายข้อมูลการกำหนดค่านี้เป็นส่วนหนึ่งของการอัปเกรดเป็นรุ่นที่ 2
functions.config API เลิกใช้งานแล้วและจะหยุดให้บริการในเดือนมีนาคม 2027
หลังจากวันที่ดังกล่าว การติดตั้งใช้งานด้วย functions.config จะไม่สำเร็จ
เพื่อป้องกันไม่ให้การติดตั้งใช้งานล้มเหลว ให้ย้ายข้อมูลการกำหนดค่าไปยัง Cloud Secret Manager โดยใช้ Firebase CLI เราขอแนะนำอย่างยิ่งให้ใช้วิธีนี้เนื่องจากเป็นวิธีที่มีประสิทธิภาพและปลอดภัยที่สุดในการย้ายข้อมูลการกำหนดค่า
ส่งออกการกำหนดค่าด้วย Firebase CLI
ใช้คำสั่ง
config exportเพื่อส่งออกการกำหนดค่าสภาพแวดล้อมที่มีอยู่ไปยังข้อมูลลับใหม่ใน Cloud Secret Manager$ firebase functions:config:export i This command retrieves your Runtime Config values (accessed via functions.config()) and exports them as a Secret Manager secret. i Fetching your existing functions.config() from your project... ✔ Fetched your existing functions.config(). i Configuration to be exported: ⚠ This may contain sensitive data. Do not share this output. { ... } ✔ What would you like to name the new secret for your configuration? RUNTIME_CONFIG ✔ Created new secret version projects/project/secrets/RUNTIME_CONFIG/versions/1```อัปเดตโค้ดฟังก์ชันเพื่อเชื่อมโยงข้อมูลลับ
หากต้องการใช้การกำหนดค่าที่จัดเก็บไว้ในข้อมูลลับใหม่ใน Cloud Secret Manager ให้ใช้ API
defineJsonSecretในแหล่งที่มาของฟังก์ชัน นอกจากนี้ โปรดตรวจสอบว่าข้อมูลลับเชื่อมโยงกับฟังก์ชันทั้งหมดที่จำเป็นต้องใช้ข้อมูลลับก่อน
const functions = require("firebase-functions/v1"); exports.myFunction = functions.https.onRequest((req, res) => { const apiKey = functions.config().someapi.key; // ... });หลัง
const { onRequest } = require("firebase-functions/v2/https"); const { defineJsonSecret } = require("firebase-functions/params"); const config = defineJsonSecret("RUNTIME_CONFIG"); exports.myFunction = onRequest( // Bind secret to your function { secrets: [config] }, (req, res) => { // Access secret values via .value() const apiKey = config.value().someapi.key; // ... });ทำให้ฟังก์ชันใช้งานได้
ทำให้ฟังก์ชันที่อัปเดตใช้งานได้เพื่อใช้การเปลี่ยนแปลงและเชื่อมโยงสิทธิ์ของข้อมูลลับ
firebase deploy --only functions:<your-function-name>
ตั้งค่าตัวเลือกของรันไทม์
การกำหนดค่าตัวเลือกรันไทม์มีการเปลี่ยนแปลงระหว่างรุ่นที่ 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 เพื่อให้สิทธิ์เข้าถึง API ของ Firebase แต่ฟังก์ชันรุ่นที่ 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.
กลยุทธ์การย้ายข้อมูลจะขึ้นอยู่กับประเภททริกเกอร์ที่ฟังก์ชันใช้
ย้ายข้อมูลทริกเกอร์ที่เรียกใช้ได้ คิวงาน และ HTTP
ทริกเกอร์เหล่านี้เป็นการเรียกใช้โดยตรง เนื่องจากฟังก์ชันรุ่นที่ 2 จะมีชื่อใหม่ (และ URL ใหม่สำหรับทริกเกอร์ HTTP) คุณจึงย้ายข้อมูลการเข้าชมได้โดยการอัปเดตไคลเอ็นต์
- เปลี่ยนชื่อฟังก์ชันในโค้ด (เช่น เปลี่ยนชื่อ
myCallableเป็นmyCallableV2) - ทำให้ฟังก์ชันใช้งานได้ ตอนนี้ทั้งฟังก์ชันรุ่นที่ 1 และรุ่นที่ 2 กำลังทำงานอยู่
- อัปเดตโค้ดไคลเอ็นต์หรือผู้เรียกให้ชี้ไปยังชื่อหรือ URL ของฟังก์ชันรุ่นที่ 2 ใหม่
- เมื่อการเข้าชมทั้งหมดเปลี่ยนไปใช้ฟังก์ชันใหม่แล้ว ให้ลบฟังก์ชันรุ่นที่ 1 โดยใช้คำสั่ง
firebase functions:deleteของ Firebase CLI
ย้ายข้อมูลทริกเกอร์ในเบื้องหลัง
ทริกเกอร์เบื้องหลัง (เช่น ทริกเกอร์ Pub/Sub, Cloud Firestore และ Cloud Storage) จะตอบสนองต่อเหตุการณ์ในโปรเจ็กต์ คุณต้องเรียกใช้ทั้งฟังก์ชันรุ่นที่ 1 และรุ่นที่ 2 ควบคู่กันไปชั่วคราวเพื่อไม่ให้พลาดเหตุการณ์ใดๆ ในระหว่างการเปลี่ยนผ่าน
ในช่วงเปลี่ยนผ่าน ฟังก์ชันทั้ง 2 จะทริกเกอร์ในเหตุการณ์เดียวกัน ซึ่งหมายความว่าตรรกะทางธุรกิจจะทำงาน 2 ครั้งต่อเหตุการณ์ ตรวจสอบว่าฟังก์ชันเป็นแบบ Idempotent ก่อนดำเนินการต่อ
เพิ่มฟังก์ชันรุ่นที่ 2 ควบคู่ไปกับฟังก์ชันรุ่นที่ 1 โดยเก็บฟังก์ชันรุ่นที่ 1 ที่มีอยู่ในโค้ดไว้ และเพิ่มฟังก์ชันรุ่นที่ 2 ที่คอยฟังแหล่งที่มาของเหตุการณ์เดียวกัน
import * as functions from "firebase-functions/v1"; import { onMessagePublished } from "firebase-functions/v2/pubsub"; // --- Existing 1st gen function --- export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => { console.log("V1 handler running for event:", context.eventId); // ... existing v1 function logic ... }); // --- New v2 passthrough function --- export const myPubSubV2 = onMessagePublished("my-topic", async ({ message, context }) => { console.log("v2 handler triggering V1 for event:", context.eventId); // Call the v1 function's handler await myPubSub.run(message, context); });วิ่ง
firebase deployตอนนี้ทั้ง 2 ฟังก์ชันทำงานอยู่และคอยฟังเหตุการณ์เดียวกันยืนยันว่าฟังก์ชันรุ่นที่ 2 ได้รับการเข้าชม ตรวจสอบบันทึกของทั้ง 2 ฟังก์ชัน ตรวจสอบว่ามีการเรียกใช้ฟังก์ชันรุ่นที่ 2 สำหรับเหตุการณ์ทั้งหมดและการเรียกใช้สำเร็จ
เมื่อมั่นใจว่าฟังก์ชันทำงานได้อย่างถูกต้องแล้ว ให้ย้ายตรรกะทางธุรกิจจริงจากฟังก์ชันรุ่นที่ 1 ไปยังเนื้อหาของฟังก์ชันรุ่นที่ 2 หากคุณใช้วิธีการส่งผ่าน ให้นำการเรียกใช้
myPubSub.run()ออกimport * as functions from "firebase-functions/v1"; import { onMessagePublished } from "firebase-functions/v2/pubsub"; // --- Existing v1 function (to be removed next) --- export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => { console.log("v1 handler running for event:", context.eventId); // ... existing v1 function logic ... }); // --- New v2 function with full logic --- export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => { console.log("v2 handler running for event:", context.eventId); // ... existing v1 function logic WAS MOVED HERE ... });ทำให้การเปลี่ยนแปลงนี้มีผล
นำคำจำกัดความของฟังก์ชันรุ่นที่ 1 ออกจากโค้ดและทำให้ใช้งานได้อีกครั้ง CLI จะแจ้งให้คุณลบฟังก์ชันรุ่นที่ 1 ออกจาก Google Cloud