على التطبيقات التي تستخدِم دوال الجيل الأول التفكير في نقل البيانات إلى الجيل الثاني باستخدام التعليمات الواردة في هذا الدليل. تستخدِم دوال الجيل الثاني Cloud Run لتوفير أداء أفضل وإعداد أفضل ومراقبة أفضل وغير ذلك.
تفترض الأمثلة الواردة في هذا المستند أنّك تستخدِم JavaScript مع وحدات CommonJS (عمليات الاستيراد بنمط require)، ولكن تنطبق المبادئ نفسها على JavaScript مع وحدات ESM (عمليات الاستيراد بنمط import … from) وTypeScript.
عملية نقل البيانات
يمكن أن تتواجد دوال الجيل الأول والجيل الثاني جنبًا إلى جنب في ملف المصدر نفسه. يتيح لك ذلك نقل قاعدة الرموز البرمجية جزءًا جزءًا، عندما تكون مستعدًا. يُرجى العِلم أنّ هذا المزيج من الحِزم لا يعمل ضمن دالة واحدة ومستقلة.
ننصح بنقل دالة واحدة في كل مرة، وإجراء الاختبار والتحقّق قبل المتابعة.
التحقّق من إصدارات Firebase وfirebase-functions
تأكَّد من أنّك تستخدِم على الأقل الإصدار 12.00 من Firebase والإصدار 4.3.0 من firebase-functions. ستتوافق أي إصدارات أحدث مع الجيل الثاني والجيل الأول.
تعديل عمليات الاستيراد
تستورِد دوال الجيل الثاني من الحزمة الفرعية v2 في حزمة تطوير البرامج (SDK) الخاصة بـ firebase-functions. إنّ مسار الاستيراد المختلف هو كل ما يحتاج إليه Firebase CLI لتحديد
ما إذا كان سيتم نشر رمز الدالة كدالة من الجيل الأول أو الجيل الثاني.
الحزمة الفرعية v2 هي حزمة نمطية، وننصحك باستيراد الوحدة النمطية المحدّدة التي تحتاج إليها فقط.
قبل: الجيل الأول
const functions = require("firebase-functions/v1");
بعد: الجيل الثاني
// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
تعديل تعريفات المشغّلات
بما أنّ حزمة تطوير البرامج (SDK) الخاصة بالجيل الثاني تفضّل عمليات الاستيراد النمطية، عدِّل تعريفات المشغّلات لتعكس عمليات الاستيراد التي تم تغييرها من الخطوة السابقة.
تغيّرت الوسيطات التي يتم تمريرها إلى عمليات معاودة الاتصال لبعض المشغّلات. في هذا المثال، يُرجى العِلم أنّه تم دمج وسيطات معاودة الاتصال onDocumentCreated في عنصر event واحد. بالإضافة إلى ذلك، تتضمّن بعض المشغّلات ميزات إعداد جديدة ومريحة، مثل خيار cors لمشغّل onRequest.
قبل: الجيل الأول
const functions = require("firebase-functions/v1");
exports.date = functions.https.onRequest((req, res) => {
// ...
});
exports.uppercase = functions.firestore
.document("my-collection/{docId}")
.onCreate((change, context) => {
// ...
});
بعد: الجيل الثاني
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
إذا كانت الدوال تتضمّن أجزاء معقّدة تعتمد بشكل كبير على سياق الجيل الأول أو المَعلمات الخاصة بمزوّد البيانات (مثل message أو snapshot)، يمكنك استخدام أدوات المساعدة المتوافقة مع الجيل الأول والمضمّنة في حزمة تطوير البرامج (SDK) الخاصة بالجيل الثاني.
تعمل حزمة تطوير البرامج (SDK) الخاصة بالجيل الثاني تلقائيًا على تصحيح عنصر الحدث باستخدام دوال getter التي تتطابق مع توقيعات الجيل الأول. يتيح لك ذلك استخدام تفكيك JavaScript لاستخراج هذه الخصائص مباشرةً في توقيع معالج الأحداث، ما يقلّل من الحاجة إلى إعادة كتابة منطق الدالة.
مرجع ربط مزوّد البيانات
| مزوّد البيانات | وسيطات الجيل الأول | تفكيك الحدث الذي تم تصحيحه في الجيل الثاني |
| 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 }) => { ... }
|
قبل (الجيل الأول):
export const myPubSubV1 = functions.pubsub.topic("my-topic").onPublish((message, context) => {
const data = message.json;
const eventId = context.eventId;
// ... rest of the logic
});
بديل جديد (الجيل الثاني مع التفكيك):
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
});
استخدام الإعدادات المحدّدة بالمعلّمات
تتوقّف دوال الجيل الثاني عن دعم functions.config لصالح واجهة أكثر أمانًا
لتحديد مَعلمات الإعداد بشكلٍ تصريحي
داخل قاعدة الرموز البرمجية.
باستخدام الوحدة النمطية الجديدة params، يمنع Firebase CLI عملية النشر ما لم يكن لجميع المَعلمات قيمة صالحة، ما يضمن عدم نشر دالة بدون إعدادات.
قبل: الجيل الأول
const functions = require("firebase-functions/v1");
exports.getQuote = functions.https.onRequest(async (req, res) => {
const quote = await fetchMotivationalQuote(functions.config().apiKey);
// ...
});
بعد: الجيل الثاني
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، يمكنك نقل هذا الإعداد كجزء من عملية الترقية إلى الجيل الثاني.
تم إيقاف واجهة برمجة التطبيقات functions.config نهائيًا وسيتم إيقافها في مارس 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، استخدِم واجهة برمجة التطبيقات
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>
ضبط خيارات وقت التشغيل
تغيّر إعداد خيارات وقت التشغيل بين الجيل الأول والجيل الثاني. يضيف الجيل الثاني أيضًا إمكانية جديدة لـ ضبط الخيارات لجميع الدوال.
قبل: الجيل الأول
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) => {
// ...
});
بعد: الجيل الثاني
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) => {
/* ... */
});
تعديل حساب الخدمة التلقائي (اختياري)
بينما تستخدِم دوال الجيل الأول حساب الخدمة التلقائي Google App Engine لتخويل الوصول إلى واجهات برمجة تطبيقات Firebase، تستخدِم دوال الجيل الثاني حساب الخدمة التلقائي Compute Engine. يمكن أن يؤدي هذا الاختلاف إلى حدوث مشاكل في الأذونات للدوال التي تم نقلها إلى الجيل الثاني في الحالات التي منحت فيها أذونات خاصة لحساب خدمة الجيل الأول. إذا لم تغيِّر أي أذونات لحساب الخدمة، يمكنك تخطّي هذه الخطوة.
الحلّ المقترَح هو تعيين حساب الخدمة التلقائي الحالي في App Engine للجيل الأول بشكلٍ صريح للدوال التي تريد نقلها إلى الجيل الثاني، ما يؤدي إلى إلغاء حساب الخدمة التلقائي في الجيل الثاني.App Engine يمكنك إجراء ذلك من خلال التأكّد من أنّ كل دالة تم نقلها تضبط القيمة الصحيحة لـ 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 (للجيل الأول) وحساب الخدمة التلقائي في Compute Engine (للجيل الثاني).
استخدام التزامن
من المزايا المهمة لدوال الجيل الثاني قدرة مثيل دالة واحد على معالجة أكثر من طلب واحد في الوقت نفسه. يمكن أن يقلّل ذلك بشكلٍ كبير من عدد عمليات البدء البارد التي يواجهها المستخدمون النهائيون. يتم ضبط التزامن تلقائيًا على 80، ولكن يمكنك ضبطه على أي قيمة من 1 إلى 1000:
const {onRequest} = require("firebase-functions/v2/https");
exports.date = onRequest({
// set concurrency value
concurrency: 500
},
(req, res) => {
// ...
});
يمكن أن يؤدي ضبط التزامن إلى تحسين الأداء وتقليل تكلفة الدوال. مزيد من المعلومات حول التزامن في مقالة السماح بالطلبات المتزامنة
تدقيق استخدام المتغيّرات العمومية
قد تستخدِم دوال الجيل الأول التي تمّت كتابتها بدون أخذ التزامن في الاعتبار متغيّرات عمومية يتم ضبطها وقراءتها في كل طلب. عند تفعيل التزامن وبدء مثيل واحد في معالجة طلبات متعددة في الوقت نفسه، قد يؤدي ذلك إلى ظهور أخطاء في الدالة لأنّ الطلبات المتزامنة تبدأ في ضبط المتغيّرات العمومية وقراءتها في الوقت نفسه.
أثناء الترقية، يمكنك ضبط وحدة المعالجة المركزية للدالة على gcf_gen1 وضبط concurrency على 1 لاستعادة سلوك الجيل الأول:
const {onRequest} = require("firebase-functions/v2/https");
exports.date = onRequest({
// TEMPORARY FIX: remove concurrency
cpu: "gcf_gen1",
concurrency: 1
},
(req, res) => {
// ...
});
ومع ذلك، لا ننصح باستخدام هذا الحلّ على المدى الطويل، لأنّه يؤدي إلى فقدان مزايا الأداء لدوال الجيل الثاني. بدلاً من ذلك، يمكنك تدقيق استخدام المتغيّرات العمومية في الدوال، وإزالة هذه الإعدادات المؤقتة عندما تكون مستعدًا.
نقل الزيارات إلى دوال الجيل الثاني الجديدة
كما هو الحال عند تغيير منطقة دالة أو نوع المشغّل، عليك منح دالة الجيل الثاني اسمًا جديدًا ونقل الزيارات إليها ببطء.
لا يمكن ترقية دالة من الجيل الأول إلى الجيل الثاني بالاسم نفسه
وتشغيل 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
هذه المشغّلات هي عمليات استدعاء مباشرة. بما أنّ دالة الجيل الثاني ستحمل اسمًا جديدًا (وعنوان URL جديدًا لمشغّلات HTTP)، يمكنك نقل الزيارات من خلال تعديل العملاء.
- أعِد تسمية الدالة في الرمز البرمجي (على سبيل المثال، أعِد تسمية
myCallableإلىmyCallableV2). - انشر الدالة. تعمل الآن كلٌّ من دالتي الجيل الأول والجيل الثاني.
- عدِّل رمز العميل أو المتصل للإشارة إلى اسم دالة الجيل الثاني الجديدة أو عنوان URL الخاص بها.
- بعد نقل جميع الزيارات إلى الدالة الجديدة، احذف دالة الجيل الأول باستخدام الأمر
firebase functions:deleteفي Firebase.
نقل مشغّلات الخلفية
تستجيب مشغّلات الخلفية (مثل Pub/Sub وCloud Firestore و Cloud Storage مشغّلات) للأحداث في مشروعك. لتجنُّب فقدان أي أحداث أثناء عملية الانتقال، عليك تشغيل كلٍّ من دالتي الجيل الأول و الجيل الثاني جنبًا إلى جنب مؤقتًا.
أثناء فترة الانتقال، سيتم تشغيل كلتا الدالتَين عند وقوع الحدث نفسه. يعني ذلك أنّ منطق مؤسستك سيتم تشغيله مرّتين لكل حدث. تأكَّد من أنّ الدالة غير حساسة لتكرار التنفيذ قبل المتابعة.
أضِف دالة الجيل الثاني بجانب دالة الجيل الأول، مع الاحتفاظ بدالة الجيل الأول الحالية في الرمز البرمجي، وأضِف دالة الجيل الثاني التي تستمع إلى مصدر الحدث نفسه.
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. تكون كلتا الدالتَين نشطتَين الآن وتستمعان إلى الأحداث نفسها.تأكَّد من أنّ دالة الجيل الثاني تتلقّى الزيارات. راقِب السجلّات لكلتا الدالتَين. تأكَّد من استدعاء دالة الجيل الثاني لجميع الأحداث ومن نجاح عمليات الاستدعاء.
بعد التأكّد من أنّ الدالة تعمل بشكلٍ صحيح، انقل منطق المؤسسة الفعلي من دالة الجيل الأول إلى جزء دالة الجيل الثاني. إذا استخدمت طريقة التمرير، أزِل استدعاء
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 ... });انشر هذا التغيير.
أزِل تعريف دالة الجيل الأول من الرمز البرمجي وأعِد النشر. سيطلب منك Firebase CLI حذف دالة الجيل الأول من Google Cloud.