التعامل مع أحداث مراحل نشاط الإضافة

يمكن أن تتضمّن إضافتك Cloud Tasks وظائف يتم تشغيلها عندما تمرّ نسخة من الإضافة بأي من أحداث مراحل النشاط التالية:

  • تثبيت نسخة من الإضافة
  • تعديل نسخة من الإضافة إلى إصدار جديد
  • تغيير إعدادات نسخة من الإضافة

من أهم حالات استخدام هذه الميزة إعادة ملء البيانات. على سبيل المثال، لنفترض أنّك تعمل على إنشاء إضافة تنشئ معاينات مصغّرة للصور التي يتم تحميلها إلى مجموعة Cloud Storage Cloud Storage. سيتم تنفيذ العمل الرئيسي لإضافتك في وظيفة يتم تشغيلها من خلال حدث onFinalize Cloud Storage. ومع ذلك، لن تتم معالجة سوى الصور التي تم تحميلها بعد تثبيت الإضافة. من خلال تضمين وظيفة في إضافتك يتم تشغيلها من خلال حدث يتم في مراحل النشاط onInstall، يمكنك أيضًا إنشاء معاينات مصغّرة لأي صور حالية عند تثبيت الإضافة.

تشمل بعض حالات الاستخدام الأخرى لمشغّلات أحداث مراحل النشاط ما يلي:

  • أتمتة الإعداد بعد التثبيت (إنشاء سجلّات قاعدة البيانات والفهرسة وما إلى ذلك)
  • نقل البيانات تلقائيًا عند التعديل إذا كان عليك نشر تغييرات غير متوافقة مع الإصدارات السابقة

معالِجات أحداث مراحل النشاط القصيرة المدى

إذا كان بإمكان مهمتك أن تعمل بالكامل خلال الحد الأقصى Cloud Functions للمدة (9 دقائق باستخدام الجيل الأول من واجهة برمجة التطبيقات)، يمكنك كتابة معالج حدث يتم في مراحل النشاط كوظيفة واحدة يتم تشغيلها عند حدث onDispatch في قائمة انتظار المهام:

export const myTaskFunction = functions.tasks.taskQueue()
  .onDispatch(async () => {
    // Complete your lifecycle event handling task.
    // ...

    // When processing is complete, report status to the user (see below).
  });

بعد ذلك، في ملف extension.yaml الخاص بإضافتك، اتّبِع الخطوات التالية:

  1. سجِّل وظيفتك كمورد إضافة مع ضبط السمة taskQueueTrigger. إذا ضبطت taskQueueTrigger على الخريطة الفارغة ({})، ستوفّر إضافتك قائمة انتظار Cloud Tasks باستخدام الإعدادات التلقائية. يمكنك اختياريًا تعديل هذه الإعدادات.

    resources:
      - name: myTaskFunction
        type: firebaseextensions.v1beta.function
        description: >-
          Describe the task performed when the function is triggered by a lifecycle
          event
        properties:
          location: ${LOCATION}
          taskQueueTrigger: {}
    
  2. سجِّل وظيفتك كمعالج لحدث واحد أو أكثر من أحداث مراحل النشاط:

    resources:
      - ...
    lifecycleEvents:
      onInstall:
        function: myTaskFunction
        processingMessage: Resizing your existing images
      onUpdate:
        function: myOtherTaskFunction
        processingMessage: Setting up your extension
      onConfigure:
        function: myOtherTaskFunction
        processingMessage: Setting up your extension
    
    

    يمكنك تسجيل وظائف لأي من الأحداث التالية: onInstall وonUpdate وonConfigure. وكل هذه الأحداث اختيارية.

  3. ننصحك بما يلي: إذا لم تكن مهمة المعالجة مطلوبة لكي تعمل إضافتك، أضِف مَعلمة يضبطها المستخدم تتيح له اختيار ما إذا كان يريد تفعيلها أم لا.

    على سبيل المثال، أضِف مَعلمة مثل ما يلي:

    params:
      - param: DO_BACKFILL
        label: Backfill existing images
        description: >
          Should existing, unresized images in the Storage bucket be resized as well?
        type: select
        options:
          - label: Yes
            value: true
          - label: No
            value: false
    

    وفي وظيفتك، إذا تم ضبط المَعلمة على false، اخرج مبكرًا:

    export const myTaskFunction = functions.tasks.taskQueue()
      .onDispatch(async () => {
        if (!process.env.DO_BACKFILL) {
          await runtime.setProcessingState(
            "PROCESSING_COMPLETE",
            "Existing images were not resized."
          );
          return;
        }
        // Complete your lifecycle event handling task.
        // ...
      });
    

تنفيذ المهام الطويلة المدى

إذا تعذّر إكمال مهمتك خلال الحد الأقصى لمدة Cloud Functions، قسِّم المهمة إلى مهام فرعية ونفِّذ كل مهمة فرعية بالتسلسل من خلال وضع المهام في قائمة الانتظار باستخدام طريقة TaskQueue.enqueue() في Admin SDK.

على سبيل المثال، لنفترض أنّك تريد إعادة ملء بيانات Cloud Firestore. يمكنك تقسيم مجموعة المستندات إلى أجزاء باستخدام مؤشرات طلب البحث. بعد معالجة جزء، يمكنك زيادة الإزاحة الأولية ووضع استدعاء وظيفة آخر في قائمة الانتظار كما هو موضّح أدناه:

import { getFirestore } from "firebase-admin/firestore";
import { getFunctions } from "firebase-admin/functions";

exports.backfilldata = functions.tasks.taskQueue().onDispatch(async (data) => {
  // When a lifecycle event triggers this function, it doesn't pass any data,
  // so an undefined offset indicates we're on our first invocation and should
  // start at offset 0. On subsequent invocations, we'll pass an explicit
  // offset.
  const offset = data["offset"] ?? 0;

  // Get a batch of documents, beginning at the offset.
  const snapshot = await getFirestore()
    .collection(process.env.COLLECTION_PATH)
    .startAt(offset)
    .limit(DOCS_PER_BACKFILL)
    .get();
  // Process each document in the batch.
  const processed = await Promise.allSettled(
    snapshot.docs.map(async (documentSnapshot) => {
      // Perform the processing.
    })
  );

  // If we processed a full batch, there are probably more documents to
  // process, so enqueue another invocation of this function, specifying
  // the offset to start with.
  //
  // If we processed less than a full batch, we're done.
  if (processed.length == DOCS_PER_BACKFILL) {
    const queue = getFunctions().taskQueue(
      "backfilldata",
      process.env.EXT_INSTANCE_ID
    );
    await queue.enqueue({
      offset: offset + DOCS_PER_BACKFILL,
    });
  } else {
      // Processing is complete. Report status to the user (see below).
  }
});

أضِف الوظيفة إلى ملف extension.yaml كما هو موضّح في الـ قسم السابق.

حالة إعداد التقارير

عندما تنتهي جميع وظائف المعالجة، سواء بنجاح أو مع حدوث خطأ، أبلِغ عن حالة المهمة باستخدام طرق وقت تشغيل الإضافة في Admin SDK. يمكن للمستخدمين الاطّلاع على هذه الحالة في صفحة تفاصيل الإضافة في الـ Firebase console.

الإكمال بنجاح والأخطاء غير الخطيرة

للإبلاغ عن الإكمال بنجاح والأخطاء غير الخطيرة (الأخطاء التي لا تؤدي إلى عدم عمل الإضافة)، استخدِم طريقة setProcessingState() في وقت تشغيل الإضافة في Admin SDK:

import { getExtensions } from "firebase-admin/extensions";

// ...

getExtensions().runtime().setProcessingState(processingState, message);

يمكنك ضبط الحالات التالية:

الحالات غير الخطيرة
PROCESSING_COMPLETE

استخدِم هذه الحالة للإبلاغ عن إكمال المهمة بنجاح. مثال:

getExtensions().runtime().setProcessingState(
  "PROCESSING_COMPLETE",
  `Backfill complete. Successfully processed ${numSuccess} documents.`
);
PROCESSING_WARNING

استخدِم هذه الحالة للإبلاغ عن النجاح الجزئي. مثال:

getExtensions().runtime().setProcessingState(
  "PROCESSING_WARNING",
  `Backfill complete. ${numSuccess} documents processed successfully.`
    + ` ${numFailed} documents failed to process. ${listOfErrors}.`
    + ` ${instructionsToFixTheProblem}`
);
PROCESSING_FAILED

استخدِم هذه الحالة للإبلاغ عن الأخطاء التي تمنع إكمال المهمة، ولكن لا تجعل الإضافة غير قابلة للاستخدام. مثال:

getExtensions().runtime().setProcessingState(
  "PROCESSING_FAILED",
  `Backfill failed. ${errorMsg} ${optionalInstructionsToFixTheProblem}.`
);

للإبلاغ عن الأخطاء التي تؤدي إلى عدم عمل الإضافة، استخدِم الدالة setFatalError().

NONE

استخدِم هذه الحالة لمحو حالة المهمة. يمكنك اختياريًا استخدام هذه الحالة لمحو رسالة الحالة من وحدة التحكّم (على سبيل المثال، بعد مرور بعض الوقت منذ ضبط PROCESSING_COMPLETE). مثال:

getExtensions().runtime().setProcessingState("NONE");

الأخطاء الخطيرة

إذا حدث خطأ يمنع الإضافة من العمل، على سبيل المثال، إذا تعذّر إكمال مهمة إعداد مطلوبة، أبلِغ عن الخطأ الخطير باستخدام setFatalError():

import { getExtensions } from "firebase-admin/extensions";

// ...

getExtensions().runtime().setFatalError(`Post-installation setup failed. ${errorMessage}`);

تعديل قائمة انتظار المهام

إذا ضبطت السمة taskQueueTrigger على {}، ستوفّر إضافتك قائمة انتظار Cloud Tasks بالإعدادات التلقائية عند تثبيت نسخة من الإضافة. بدلاً من ذلك، يمكنك تعديل حدود التزامن وسلوك إعادة المحاولة لقائمة انتظار المهام من خلال تقديم قيم معيّنة:

resources:
  - name: myTaskFunction
    type: firebaseextensions.v1beta.function
    description: >-
      Perform a task when triggered by a lifecycle event
    properties:
      location: ${LOCATION}
      taskQueueTrigger:
        rateLimits:
          maxConcurrentDispatches: 1000
          maxDispatchesPerSecond: 500
        retryConfig:
          maxAttempts: 100  # Warning: setting this too low can prevent the function from running
          minBackoffSeconds: 0.1
          maxBackoffSeconds: 3600
          maxDoublings: 16
lifecycleEvents:
  onInstall: 
    function: myTaskFunction
    processingMessage: Resizing your existing images
  onUpdate:
    function: myTaskFunction
    processingMessage: Setting up your extension
  onConfigure:
    function: myOtherTaskFunction
    processingMessage: Setting up your extension

راجِع مقالة إعداد قوائم انتظار Cloud Tasks في مستندات Google Cloud للحصول على تفاصيل حول هذه المَعلمات.

لا تحاول تحديد مَعلمات قائمة انتظار المهام من خلال تمريرها إلى taskQueue(). يتم تجاهل هذه الإعدادات لصالح الإعدادات في extension.yaml والإعدادات التلقائية للإعدادات.

على سبيل المثال، لن ينجح ما يلي:

export const myBrokenTaskFunction = functions.tasks
  // DON'T DO THIS IN AN EXTENSION! THESE SETTINGS ARE IGNORED.
  .taskQueue({
    retryConfig: {
      maxAttempts: 5,
      minBackoffSeconds: 60,
    },
    rateLimits: {
      maxConcurrentDispatches: 1000,
      maxDispatchesPerSecond: 10,
    },
  })
  .onDispatch(
    // ...
  );

إنّ السمة taskQueueTrigger في extension.yaml هي الطريقة الوحيدة لإعداد قوائم انتظار المهام في الإضافة.

أمثلة

تستخدم الإضافات الرسمية storage-resize-images، firestore-bigquery-export، و firestore-translate-text جميعها معالِجات أحداث يتم في مراحل النشاط لإضافة بيانات سابقة.