Obsługa zdarzeń cyklu życia rozszerzenia

Rozszerzenie może zawierać Cloud Tasksfunkcje, które są wywoływane, gdy instancja rozszerzenia przechodzi przez dowolne z tych zdarzeń cyklu życia:

  • Zainstalowana jest instancja rozszerzenia
  • Instancja rozszerzenia jest aktualizowana do nowej wersji
  • Zmieniono konfigurację instancji rozszerzenia

Jednym z najważniejszych zastosowań tej funkcji jest wypełnianie danych historycznych. Załóżmy na przykład, że tworzysz rozszerzenie, które generuje miniatury podglądu obrazów przesyłanych do zasobnika Cloud Storage. Główna część pracy rozszerzenia będzie wykonywana w funkcji wywoływanej przez zdarzenie onFinalize Cloud Storage. Przetwarzane będą jednak tylko obrazy przesłane po zainstalowaniu rozszerzenia. Jeśli w rozszerzeniu umieścisz funkcję wywoływaną przez zdarzenie cyklu życia onInstall, możesz też generować podglądy miniatur istniejących obrazów po zainstalowaniu rozszerzenia.

Oto kilka innych zastosowań wyzwalaczy zdarzeń cyklu życia:

  • Automatyzacja konfiguracji po instalacji (tworzenie rekordów bazy danych, indeksowanie itp.)
  • Jeśli musisz opublikować zmiany, które nie są wstecznie kompatybilne, automatycznie przenieś dane podczas aktualizacji.

Moduły obsługi zdarzeń cyklu życia o krótkim czasie działania

Jeśli zadanie może zostać wykonane w całości w maksymalnym Cloud Functions czasie trwania (9 minut w przypadku interfejsu API pierwszej generacji), możesz napisać procedurę obsługi zdarzenia cyklu życia jako pojedynczą funkcję, która jest wywoływana przez zdarzenie onDispatch w kolejce zadań:

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

Następnie w pliku extension.yaml rozszerzenia wykonaj te czynności:

  1. Zarejestruj funkcję jako zasób rozszerzenia za pomocą zestawu właściwości taskQueueTrigger. Jeśli ustawisz taskQueueTrigger na pustą mapę ({}), rozszerzenie utworzy kolejkę Cloud Tasks z użyciem ustawień domyślnych. Możesz też dostosować te ustawienia.

    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. Zarejestruj funkcję jako moduł obsługi co najmniej jednego zdarzenia cyklu życia:

    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
    
    

    Funkcje możesz rejestrować w przypadku dowolnego z tych zdarzeń: onInstall, onUpdateonConfigure. Wszystkie te zdarzenia są opcjonalne.

  3. Zalecane: jeśli zadanie przetwarzania nie jest wymagane do działania rozszerzenia, dodaj parametr konfigurowany przez użytkownika, który umożliwia użytkownikom włączenie tej funkcji.

    Dodaj na przykład parametr w tym formacie:

    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
    

    Jeśli w funkcji parametr ma wartość false, zakończ działanie wcześniej:

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

Wykonywanie długotrwałych zadań

Jeśli zadanie nie może zostać wykonane w maksymalnym czasie Cloud Functions, podziel je na podzadania i wykonuj je kolejno, umieszczając zadania w kolejce za pomocą metody TaskQueue.enqueue() pakietu Admin SDK.

Załóżmy na przykład, że chcesz uzupełnić dane Cloud Firestore. Kolekcję dokumentów możesz podzielić na części za pomocą kursorów zapytań. Po przetworzeniu fragmentu przesuń początkowe przesunięcie i dodaj do kolejki kolejne wywołanie funkcji, jak pokazano poniżej:

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

Dodaj funkcję do extension.yaml zgodnie z opisem w poprzedniej sekcji.

Stan raportowania

Gdy wszystkie funkcje przetwarzania zakończą działanie (z powodzeniem lub z błędem), zgłoś stan zadania za pomocą metod środowiska wykonawczego rozszerzenia pakietu Admin SDK. Użytkownicy mogą zobaczyć ten stan na stronie szczegółów rozszerzenia w konsoli Firebase.

Ukończenie i błędy niekrytyczne

Aby zgłaszać pomyślne zakończenie i błędy niekrytyczne (błędy, które nie powodują, że rozszerzenie przestaje działać), użyj metody środowiska wykonawczego rozszerzenia w pakiecie Admin SDK:setProcessingState()

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

// ...

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

Możesz ustawić te stany:

Stany błędu niekrytycznego
PROCESSING_COMPLETE

Użyj tego parametru, aby zgłosić pomyślne ukończenie zadania. Przykład:

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

Użyj tego kodu, aby zgłosić częściowe powodzenie. Przykład:

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

Używaj tej opcji, aby zgłaszać błędy, które uniemożliwiają wykonanie zadania, ale nie powodują, że rozszerzenie staje się bezużyteczne. Przykład:

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

Aby zgłosić błędy, które uniemożliwiają korzystanie z rozszerzenia, zadzwoń pod numer setFatalError().

NONE

Służy do wyczyszczenia stanu zadania. Możesz opcjonalnie użyć tej funkcji, aby usunąć komunikat o stanie z konsoli (np. po upływie określonego czasu od ustawienia PROCESSING_COMPLETE). Przykład:

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

Błędy krytyczne

Jeśli wystąpi błąd, który uniemożliwia działanie rozszerzenia, np. nie powiedzie się wymagane zadanie konfiguracji, zgłoś błąd krytyczny za pomocą kodu:setFatalError()

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

// ...

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

Dostrajanie kolejki zadań

Jeśli ustawisz właściwość taskQueueTrigger na {}, rozszerzenie utworzy kolejkę Cloud Tasks z ustawieniami domyślnymi po zainstalowaniu instancji rozszerzenia. Możesz też dostosować limity współbieżności kolejki zadań i zachowanie związane z ponawianiem, podając konkretne wartości:

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

Szczegółowe informacje o tych parametrach znajdziesz w artykule Konfigurowanie kolejek Cloud Tasks w dokumentacji Google Cloud.

Nie próbuj określać parametrów kolejki zadań, przekazując je do taskQueue(). Te ustawienia są ignorowane na rzecz konfiguracji w extension.yaml i ustawień domyślnych konfiguracji.

Na przykład to nie zadziała:

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

Właściwość taskQueueTriggerextension.yaml to jedyny sposób na skonfigurowanie kolejek zadań rozszerzenia.

Przykłady

Oficjalne rozszerzenia storage-resize-images, firestore-bigquery-exportfirestore-translate-text używają procedur obsługi zdarzeń cyklu życia do uzupełniania danych.