Behandeln Sie die Lebenszyklusereignisse Ihrer Erweiterung

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

  • Eine Instanz der Erweiterung wird installiert
  • Eine Instanz der Erweiterung wird auf eine neue Version aktualisiert
  • Die Konfiguration einer Erweiterungsinstanz wird geändert

Einer der wichtigsten Anwendungsfälle dieser Funktion ist das Auffüllen von Daten . Angenommen, Sie erstellen eine Erweiterung, die Miniaturvorschauen von Bildern generiert, die in einen Cloud Storage-Bucket hochgeladen werden. Die Hauptarbeit Ihrer Erweiterung würde in einer Funktion erledigt, die durch das onFinalize Cloud Storage-Ereignis ausgelöst wird. Allerdings werden nur Bilder verarbeitet, die nach der Installation der Erweiterung hochgeladen wurden. Indem Sie in Ihre Erweiterung eine Funktion einbinden, die durch das onInstall Lebenszyklusereignis ausgelöst wird, können Sie bei der Installation der Erweiterung auch Miniaturvorschauen aller vorhandenen Bilder erstellen.

Zu den weiteren Anwendungsfällen von Lebenszyklus-Ereignisauslösern gehören:

  • Automatisieren Sie die Einrichtung nach der Installation (Erstellen von Datenbankeinträgen, Indizierung usw.)
  • Wenn Sie abwärtsinkompatible Änderungen veröffentlichen müssen, migrieren Sie die Daten bei der Aktualisierung automatisch

Kurzfristige Lebenszyklus-Ereignishandler

Wenn Ihre Aufgabe innerhalb der maximalen Cloud Functions-Dauer (9 Minuten mit der API der ersten Generation) vollständig ausgeführt werden kann, können Sie Ihren Lebenszyklus-Ereignishandler als einzelne Funktion schreiben, die das onDispatch Ereignis der Aufgabenwarteschlange auslöst:

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 Datei extension.yaml Ihrer Erweiterung Folgendes aus:

  1. Registrieren Sie Ihre Funktion als Erweiterungsressource mit dem Eigenschaftssatz taskQueueTrigger . Wenn Sie taskQueueTrigger auf die leere Karte ( {} ) 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 jedes der folgenden Ereignisse registrieren: onInstall , onUpdate und onConfigure . Alle diese Ereignisse sind optional.

  3. Empfohlen : Wenn die Verarbeitungsaufgabe für die Funktion Ihrer Erweiterung nicht erforderlich ist, fügen Sie einen vom Benutzer konfigurierten Parameter hinzu, mit dem Benutzer auswählen können, ob sie sie aktivieren möchten.

    Fügen Sie beispielsweise einen Parameter wie den folgenden 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
    

    Und wenn in Ihrer Funktion der Parameter auf false gesetzt 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.
        // ...
      });
    

Ausführung lang andauernder Aufgaben

Wenn Ihre Aufgabe nicht innerhalb der maximalen Cloud Functions-Dauer abgeschlossen werden kann, teilen Sie die Aufgabe in Teilaufgaben auf und führen Sie jede Teilaufgabe nacheinander aus, indem Sie Aufträge mit der TaskQueue.enqueue() Methode des Admin SDK in die Warteschlange stellen.

Angenommen, Sie möchten Cloud Firestore-Daten auffüllen. Sie können die Dokumentensammlung mithilfe von Abfragecursorn in Abschnitte aufteilen. Erhöhen Sie nach der Verarbeitung eines Blocks den Startoffset und stellen Sie einen weiteren Funktionsaufruf in die Warteschlange, wie unten gezeigt:

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 zu Ihrer extension.yaml hinzu, wie im vorherigen Abschnitt beschrieben.

Meldestatus

Wenn alle Ihre Verarbeitungsfunktionen erfolgreich oder mit einem Fehler abgeschlossen sind, melden Sie den Status der Aufgabe mithilfe der Erweiterungslaufzeitmethoden des Admin SDK. Benutzer können diesen Status auf der Seite mit den Erweiterungsdetails in der Firebase-Konsole sehen.

Erfolgreicher Abschluss und nicht schwerwiegende Fehler

Um einen erfolgreichen Abschluss und nicht schwerwiegende Fehler (Fehler, die die Erweiterung nicht in einen nicht funktionsfähigen Zustand versetzen) zu melden, verwenden Sie die Laufzeitmethode setProcessingState() der Admin SDK-Erweiterung:

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

// ...

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

Sie können folgende Zustände einstellen:

Nicht-tödliche Zustände
PROCESSING_COMPLETE

Verwenden Sie diese Option, um den erfolgreichen Abschluss einer Aufgabe zu melden. Beispiel:

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

Verwenden Sie diese Option, um einen Teilerfolg zu melden. 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 den Abschluss der Aufgabe verhindern, aber die Erweiterung nicht unbrauchbar machen. Beispiel:

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

Um Fehler zu melden, die die Erweiterung unbrauchbar machen , rufen Sie setFatalError() auf.

NONE

Verwenden Sie diese Option, um den Status der Aufgabe zu löschen. Sie können dies optional verwenden, um die Statusmeldung von der Konsole zu löschen (z. B. nachdem seit dem Festlegen von PROCESSING_COMPLETE eine gewisse Zeit vergangen ist). Beispiel:

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

Fatale Fehler

Wenn ein Fehler auftritt, der die Funktion der Erweiterung verhindert – beispielsweise wenn eine erforderliche Setup-Aufgabe fehlschlägt – melden Sie den schwerwiegenden Fehler mit setFatalError() :

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

// ...

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

Optimieren der Aufgabenwarteschlange

Wenn Sie die Eigenschaft taskQueueTrigger auf {} festlegen, stellt Ihre Erweiterung bei der Installation einer Erweiterungsinstanz eine Cloud Tasks-Warteschlange mit den Standardeinstellungen bereit. Alternativ können Sie die Parallelitätsgrenzen und das Wiederholungsverhalten der Aufgabenwarteschlange optimieren, 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

Einzelheiten zu diesen Parametern finden Sie unter Cloud Tasks-Warteschlangen konfigurieren in den Google Cloud-Dokumenten.

Versuchen Sie nicht, Aufgabenwarteschlangenparameter anzugeben, indem Sie sie an taskQueue() übergeben. Diese Einstellungen werden zugunsten der Konfiguration in extension.yaml und der Konfigurationsstandardwerte ignoriert.

Das wird zum Beispiel nicht funktionieren:

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-Ereignishandler, um Daten aufzufüllen.