ترقية وظائف Node.js من الجيل الأول إلى الجيل الثاني

على التطبيقات التي تستخدم دوال الجيل الأول أن تنقل بياناتها إلى الجيل الثاني باستخدام التعليمات الواردة في هذا الدليل. تستخدم دوال الجيل الثاني Cloud Run لتقديم أداء أفضل وإعدادات أفضل ومراقبة أفضل والمزيد.

تفترض الأمثلة الواردة في هذه الصفحة أنّك تستخدم JavaScript مع وحدات CommonJS (عمليات الاستيراد بنمط require)، ولكن تنطبق المبادئ نفسها على JavaScript مع وحدات ESM (عمليات الاستيراد بنمط import … from) وTypeScript.

عملية نقل البيانات

يمكن أن تتواجد دوال الجيل الأول والجيل الثاني جنبًا إلى جنب في الملف نفسه. يتيح ذلك نقل البيانات بسهولة جزءًا جزءًا، عندما تكون مستعدًا. ننصح بنقل دالة واحدة في كل مرة، وإجراء الاختبار والتحقّق قبل المتابعة.

التحقّق من إصدارات Firebase CLI وfirebase-function

تأكَّد من استخدام الإصدار 12.00 على الأقل من Firebase CLI والإصدار 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) => {
  /* ... */
});

استخدام الإعدادات المحدّدة حسب المَعلمة

تتوقّف دوال الجيل الثاني عن دعم 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. ننصحك بشدة بذلك لأنّها الطريقة الأكثر فعالية وأمانًا لنقل إعداداتك.

  1. تصدير الإعدادات باستخدام 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```
    
  2. تعديل رمز الدالة لربط الأسرار

    لاستخدام الإعدادات المخزّنة في السرّ الجديد في 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;
        // ...
    });
    
  3. تفعيل الوظائف

    انشر الدوال المعدَّلة لتطبيق التغييرات وربط أذونات السرّ.

    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 للجيل الأول بشكلٍ صريح للدوال التي تريد نقلها إلى الجيل الثاني، ما يؤدي إلى إلغاء الإعداد التلقائي للجيل الثاني. يمكنك إجراء ذلك من خلال التأكّد من أنّ كل دالة تم نقلها تضبط القيمة الصحيحة لـ 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.

قبل اتّباع هذه الخطوات، تأكَّد أولاً من أنّ الدالة غير قابلة للتكرار، لأنّ كلاً من الإصدار الجديد والإصدار القديم من الدالة سيتم تشغيلهما في الوقت نفسه أثناء التغيير. على سبيل المثال، إذا كان لديك دالة من الجيل الأول تستجيب لأحداث الكتابة في Firestore، تأكَّد من أنّ الاستجابة لعملية كتابة مرّتين، مرّة واحدة من خلال دالة الجيل الأول ومرّة واحدة من خلال دالة الجيل الثاني، ردًا على هذه الأحداث، تترك تطبيقك في حالة متّسقة.

  1. أعِد تسمية الدالة في رمز الدوال البرمجية. على سبيل المثال، أعِد تسمية resizeImage إلى resizeImageSecondGen.
  2. انشر الدالة، بحيث يتم تشغيل كلٍّ من دالة الجيل الأول الأصلية ودالة الجيل الثاني.
    1. في حالة المشغّلات القابلة للاستدعاء ومشغّلات "قائمة المهام" ومشغّلات HTTP، ابدأ بتوجيه جميع العملاء إلى دالة الجيل الثاني من خلال تعديل رمز العميل البرمجي باسم دالة الجيل الثاني أو عنوان URL الخاص بها.
    2. باستخدام مشغّلات الخلفية، ستستجيب كلٌّ من دالة الجيل الأول ودالة الجيل الثاني لكل حدث فور النشر.
  3. عند نقل كل حركة المرور، احذف دالة الجيل الأول باستخدام الأمر firebase functions:delete في Firebase CLI.
    1. يمكنك اختياريًا إعادة تسمية دالة الجيل الثاني لتتطابق مع اسم دالة الجيل الأول.