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

على التطبيقات التي تستخدم حاليًا وظائف الجيل الأول التفكير في نقل بياناتها إلى وظائف الجيل الثاني باستخدام التعليمات الواردة في هذا الدليل. تستخدِم الدوالّ من الجيل الثاني Cloud Run لتوفير أداء أفضل وإعدادات أفضل ومراقبة أفضل وغير ذلك.

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

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

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

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

تأكَّد من استخدام الإصدار 12.00 على الأقل من واجهة Firebase CLI و4.3.0 الإصدار 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 واحد. بالإضافة إلى ذلك، تتضمّن بعض عوامل التفعيل ميزات إعداد جديدة ملائمة، مثل خيار onRequestcors عامل التفعيل.

الإصدار السابق: الجيل الأول

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 الجديدة، تحظر وحدة التحكّم من سطر الأوامر عملية النشر ما لم تكن جميع المَعلمات ذات قيمة صالحة، ما يضمن عدم نشر دالة بدون إعدادات.

نقل البيانات إلى الحزمة الفرعية params

إذا كنت تستخدِم إعدادات البيئة مع functions.config، يمكنك نقل إعداداتك الحالية إلى الإعدادات المُعرَّفة بالمَعلمات.

الإصدار السابق: الجيل الأول

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

exports.date = functions.https.onRequest((req, res) => {
  const date = new Date();
  const formattedDate =
date.toLocaleDateString(functions.config().dateformat);

  // ...
});

بعد: الجيل الثاني

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.

لمزيد من الأمان، يمكنك أيضًا تحديد أنواع المَعلمات وقواعد التحقّق.

حالة خاصة: مفاتيح واجهة برمجة التطبيقات

يتم دمج وحدة params مع أداة Cloud Secret Manager التي توفّر إمكانية التحكّم الدقيق في الوصول إلى القيم الحسّاسة، مثل مفاتيح واجهة برمجة التطبيقات. اطّلِع على المَعلمات السرية للحصول على مزيد من المعلومات.

الإصدار السابق: الجيل الأول

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());
    // ...
  }
);

ضبط خيارات وقت التشغيل

تغيّر إعداد خيارات وقت التشغيل بين الجيل الأول والثاني، ويضيف الجيل الثاني أيضًا ميزة جديدة لتحديد خيارات لجميع الدوال.

الإصدار السابق: الجيل الأول

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) => {
  /* ... */
});

استخدام المزامنة

من المزايا المهمة لدوالّ الجيل الثاني هي إمكانية استخدام مثيل واحد للدالة في تنفيذ أكثر من طلب واحد في الوقت نفسه. ويمكن أن يؤدي ذلك إلى تقليل عدد عمليات التشغيل على البارد التي يواجهها المستخدمون النهائيون بشكل كبير. يتم تلقائيًا تحديد عدد عمليات المعالجة المتزامنة على 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.
    1. يمكنك اختياريًا إعادة تسمية الدالة من الجيل الثاني لتتطابق مع اسم الدالة من الجيل الأول.