Lebenszyklus-Ereignisse der Erweiterung verarbeiten

Ihre Erweiterung kann Cloud Tasks-Funktionen enthalten, die ausgelöst werden, wenn eine Erweiterungs-Instanz eines der folgenden Lebenszyklusereignisse durchläuft:

  • Eine Instanz der Erweiterung ist installiert
  • Eine Instanz der Erweiterung wird auf eine neue Version aktualisiert.
  • Die Konfiguration einer Erweiterungs-Instanz wird geändert

Einer der wichtigsten Anwendungsfälle dieser Funktion ist das Backfilling von Daten. Angenommen, Sie erstellen eine Erweiterung, die Miniaturansichten von Bildern generiert, die in einen Cloud Storage-Bucket hochgeladen wurden. Die Hauptarbeit Ihrer Erweiterung wird in einer Funktion ausgeführt, die durch das Ereignis onFinalize Cloud Storage ausgelöst wird. Es werden jedoch nur Bilder verarbeitet, die nach der Installation der Erweiterung hochgeladen wurden. Wenn Sie in Ihrer Erweiterung eine Funktion einbinden, die durch das Lebenszyklusereignis onInstall ausgelöst wird, können Sie auch Miniaturansichten von vorhandenen Bildern generieren, wenn die Erweiterung installiert ist.

Weitere Anwendungsfälle für Trigger für Lebenszyklus-Ereignisse:

  • Einrichtung nach der Installation automatisieren (z. B. Datenbankeinträge erstellen, Indexierung)
  • Wenn Sie abwärtskompatible Änderungen veröffentlichen müssen, Daten beim Update automatisch migrieren

Kurze Lebenszyklus-Event-Handler

Wenn Ihre Aufgabe innerhalb der maximalen Cloud Functions Dauer ausgeführt werden kann (9 Minuten bei der API der ersten Generation), können Sie den Lebenszyklusereignis-Handler als einzelne Funktion schreiben, die beim Ereignis onDispatch der Aufgabenwarteschlange ausgelöst wird:

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

Führen Sie dann in der extension.yaml-Datei Ihrer Erweiterung folgende Schritte aus:

  1. Registrieren Sie Ihre Funktion als Erweiterungsressource mit dem Property-Set taskQueueTrigger. Wenn Sie taskQueueTrigger auf die leere Map ({}) festlegen, stellt Ihre Erweiterung eine Cloud Tasks-Warteschlange mit den Standardeinstellungen bereit. Sie können diese Einstellungen optional anpassen.

    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. Registrieren Sie Ihre Funktion als Handler für ein oder mehrere Lebenszyklusereignisse:

    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
    
    

    Sie können Funktionen für die folgenden Ereignisse registrieren: onInstall, onUpdate und onConfigure. Alle diese Ereignisse sind optional.

  3. Empfohlen: Wenn die Verarbeitung Ihrer Erweiterung nicht erforderlich ist, fügen Sie einen vom Nutzer konfigurierten Parameter hinzu, über den Nutzer entscheiden können, ob sie sie aktivieren möchten.

    Fügen Sie beispielsweise einen Parameter wie diesen hinzu:

    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
    

    Wenn der Parameter in Ihrer Funktion auf false festgelegt ist, beenden Sie die Funktion vorzeitig:

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

Lang andauernde Aufgaben ausführen

Wenn Ihre Aufgabe nicht innerhalb der maximalen Dauer von Cloud Functions abgeschlossen werden kann, unterteilen Sie sie in Unteraufgaben und führen Sie jede Unteraufgabe der Reihe nach aus. Stellen Sie dazu Jobs mit der Methode TaskQueue.enqueue() des Admin SDK in die Warteschlange.

Angenommen, Sie möchten Cloud Firestore-Daten nachholen. Mit Abfragecursors können Sie die Dokumentensammlung in mehrere Teile aufteilen. Nach der Verarbeitung eines Chunks wird der Startoffset erhöht und eine weitere Funktionsaufruf wird in die Warteschlange gestellt, wie unten dargestellt:

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

Fügen Sie die Funktion Ihrer extension.yaml wie im vorherigen Abschnitt beschrieben hinzu.

Berichtsstatus

Wenn alle Verarbeitungsfunktionen abgeschlossen sind, entweder erfolgreich oder mit einem Fehler, melden Sie den Status der Aufgabe mithilfe der Methoden der Erweiterungslaufzeit des Admin SDKs. Nutzer sehen diesen Status auf der Seite mit den Details zur Erweiterung in der Firebase-Konsole.

Erfolgreicher Abschluss und nicht schwerwiegende Fehler

Wenn Sie den erfolgreichen Abschluss und nicht schwerwiegende Fehler (Fehler, die die Erweiterung nicht funktionsunfähig machen) melden möchten, verwenden Sie die setProcessingState()-Methode der Admin SDK-Erweiterungslaufzeit:

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

// ...

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

Sie können die folgenden Status festlegen:

Nicht schwerwiegende Status
PROCESSING_COMPLETE

Hiermit wird der erfolgreiche Abschluss einer Aufgabe gemeldet. Beispiel:

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

Verwenden Sie diese Option, wenn der Vorgang teilweise erfolgreich war. Beispiel:

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

Verwenden Sie diese Option, um Fehler zu melden, die das Ausführen der Aufgabe verhindern, die Erweiterung aber nicht unbrauchbar machen. Beispiel:

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

Wenn die Erweiterung durch einen Fehler nicht mehr verwendet werden kann, rufen Sie setFatalError() auf.

NONE

Damit wird der Status der Aufgabe gelöscht. Optional können Sie damit die Statusmeldung aus der Konsole löschen, z. B. wenn nach der Einstellung von PROCESSING_COMPLETE eine gewisse Zeit verstrichen ist. Beispiel:

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

Schwerwiegende Fehler

Wenn ein Fehler auftritt, der die Funktion der Erweiterung verhindert, z. B. wenn eine erforderliche Einrichtungsaufgabe fehlschlägt, melden Sie den schwerwiegenden Fehler mit setFatalError():

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

// ...

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

Aufgabenwarteschlange optimieren

Wenn Sie das Attribut taskQueueTrigger auf {} festlegen, stellt die Erweiterung bei der Installation einer Erweiterungsinstanz eine Cloud Tasks-Warteschlange mit den Standardeinstellungen bereit. Alternativ können Sie die Nebenläufigkeitslimits und das Wiederholungsverhalten der Aufgabenwarteschlange anpassen, indem Sie bestimmte Werte angeben:

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

Weitere Informationen zu diesen Parametern finden Sie in der Google Cloud-Dokumentation unter Cloud Tasks-Warteschlangen konfigurieren.

Versuchen Sie nicht, Parameter für Aufgabenwarteschlangen an taskQueue() zu übergeben. Diese Einstellungen werden zugunsten der Konfiguration in extension.yaml und der Standardkonfiguration ignoriert.

Folgendes funktioniert beispielsweise nicht:

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

Die Eigenschaft taskQueueTrigger in extension.yaml ist die einzige Möglichkeit, die Aufgabenwarteschlangen einer Erweiterung zu konfigurieren.

Beispiele

Die offiziellen Erweiterungen storage-resize-images, firestore-bigquery-export und firestore-translate-text verwenden alle Lebenszyklus-Event-Handler, um ein Backfill von Daten durchzuführen.