Управляйте событиями жизненного цикла вашего расширения.

Ваше расширение может включать функции Cloud Tasks , которые запускаются при прохождении экземпляром расширения любого из следующих событий жизненного цикла:

  • Установлен экземпляр расширения.
  • Экземпляр расширения обновляется до новой версии.
  • Изменена конфигурация экземпляра расширения.

Одно из важнейших применений этой функции — заполнение данных задним числом . Например, предположим, вы разрабатываете расширение, которое генерирует миниатюрные предварительные просмотры изображений, загруженных в хранилище Cloud Storage . Основная работа вашего расширения будет выполняться в функции, запускаемой событием onFinalize Cloud Storage . Однако обрабатываться будут только изображения, загруженные после установки расширения. Включив в ваше расширение функцию, запускаемую событием жизненного цикла onInstall , вы также сможете генерировать миниатюрные предварительные просмотры любых существующих изображений после установки расширения.

К другим вариантам использования триггеров событий жизненного цикла относятся:

  • Автоматизация настройки после установки (создание записей в базе данных, индексирование и т. д.)
  • Если вам необходимо опубликовать изменения, несовместимые с предыдущими версиями, автоматически переносите данные при обновлении.

Кратковременные обработчики событий жизненного цикла

Если ваша задача может быть полностью выполнена в течение максимального времени, установленного 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

Подробную информацию об этих параметрах см. в разделе «Настройка очередей задач 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 используют обработчики событий жизненного цикла для заполнения данных.