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

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

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

من أهم حالات استخدام هذه الميزة إعادة ملء البيانات. على سبيل المثال، لنفترض أنّك بصدد إنشاء إضافة تنشئ معاينات مصغّرة للصور التي يتم تحميلها إلى حزمة 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() في حزمة 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.

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

للإبلاغ عن اكتمال العملية بنجاح والأخطاء غير الخطيرة (الأخطاء التي لا تؤدي إلى توقّف الإضافة عن العمل)، استخدِم طريقة وقت تشغيل الإضافة 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 معالجات أحداث مراحل النشاط لتعبئة البيانات السابقة.