رویدادهای چرخه عمر برنامه افزودنی خود را مدیریت کنید

افزونه شما می‌تواند شامل توابع Cloud Tasks باشد که وقتی یک نمونه افزونه هر یک از رویدادهای چرخه عمر زیر را طی می‌کند، فعال می‌شوند:

  • یک نمونه از افزونه نصب شده است
  • یک نمونه از افزونه به نسخه جدید به‌روزرسانی می‌شود
  • پیکربندی یک نمونه افزونه تغییر کرده است

یکی از مهمترین موارد استفاده از این ویژگی، پر کردن مجدد داده‌ها است. برای مثال، فرض کنید در حال ساخت افزونه‌ای هستید که پیش‌نمایش‌های کوچک از تصاویر آپلود شده در یک مخزن Cloud Storage ایجاد می‌کند. کار اصلی افزونه شما در تابعی که توسط رویداد onFinalize Cloud Storage فعال می‌شود، انجام می‌شود. با این حال، فقط تصاویری که پس از نصب افزونه آپلود می‌شوند، پردازش می‌شوند. با گنجاندن تابعی که توسط رویداد onInstall lifecycle فعال می‌شود، می‌توانید هنگام نصب افزونه، پیش‌نمایش‌های کوچک از هر تصویر موجود نیز ایجاد کنید.

برخی دیگر از موارد استفاده از محرک‌های رویداد چرخه عمر عبارتند از:

  • خودکارسازی تنظیمات پس از نصب (ایجاد رکوردهای پایگاه داده، فهرست‌بندی و غیره)
  • اگر مجبورید تغییرات ناسازگار با نسخه‌های قبلی را منتشر کنید، داده‌ها را به طور خودکار در هنگام به‌روزرسانی منتقل کنید

گرداننده‌های رویداد چرخه عمر کوتاه‌مدت

اگر وظیفه شما می‌تواند به طور کامل در حداکثر مدت زمان Cloud Functions (9 دقیقه با استفاده از API نسل اول) اجرا شود، می‌توانید کنترل‌کننده رویداد چرخه عمر خود را به عنوان یک تابع واحد بنویسید که در رویداد 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 مشاهده کنند.

تکمیل موفقیت‌آمیز و خطاهای غیرمهلک

برای گزارش اتمام موفقیت‌آمیز و خطاهای غیرمهلک (خطاهایی که افزونه را در حالت غیرفعال قرار نمی‌دهند)، از متد زمان اجرای افزونه 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

برای جزئیات بیشتر در مورد این پارامترها، به بخش پیکربندی صف‌های وظایف ابری در اسناد گوگل کلود مراجعه کنید.

سعی نکنید پارامترهای صف وظایف را با ارسال آنها به 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 همگی از کنترل‌کننده‌های رویداد چرخه عمر برای پر کردن مجدد داده‌ها استفاده می‌کنند.