Ponte al día con lo más destacado de Firebase en Google I/O 2023. Más información

Manejar los eventos del ciclo de vida de su extensión

Su extensión puede incluir funciones de Cloud Tasks que se activan cuando una instancia de extensión pasa por cualquiera de los siguientes eventos del ciclo de vida:

  • Se instala una instancia de la extensión.
  • Una instancia de la extensión se actualiza a una nueva versión
  • Se cambia la configuración de una instancia de extensión

Uno de los casos de uso más importantes de esta función es la reposición de datos . Por ejemplo, suponga que está creando una extensión que genera vistas previas en miniatura de las imágenes cargadas en un depósito de Cloud Storage. El trabajo principal de su extensión se realizaría en una función activada por el evento onFinalize Cloud Storage. Sin embargo, solo se procesarán las imágenes cargadas después de instalar la extensión. Al incluir en su extensión una función activada por el evento del ciclo de vida onInstall , también puede generar vistas previas en miniatura de cualquier imagen existente cuando se instala la extensión.

Algunos otros casos de uso de activadores de eventos del ciclo de vida incluyen:

  • Automatice la configuración posterior a la instalación (creación de registros de base de datos, indexación, etc.)
  • Si tiene que publicar cambios incompatibles con versiones anteriores, migre automáticamente los datos en la actualización

Controladores de eventos de ciclo de vida de ejecución corta

Si su tarea puede ejecutarse por completo dentro de la duración máxima de Cloud Functions (9 minutos con la API de primera generación), puede escribir su controlador de eventos de ciclo de vida como una sola función que se activa en el evento onDispatch de la cola de tareas:

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

Luego, en el archivo extension.yaml de su extensión, haga lo siguiente:

  1. Registre su función como un recurso de extensión con el conjunto de propiedades taskQueueTrigger . Si configura taskQueueTrigger en el mapa vacío ( {} ), su extensión aprovisionará una cola de Cloud Tasks con la configuración predeterminada; Opcionalmente, puede ajustar estos ajustes .

    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. Registre su función como controlador para uno o más eventos del ciclo de vida:

    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
    
    

    Puede registrar funciones para cualquiera de los siguientes eventos: onInstall , onUpdate y onConfigure . Todos estos eventos son opcionales.

  3. Recomendado : si la tarea de procesamiento no es necesaria para que su extensión funcione, agregue un parámetro configurado por el usuario que les permita elegir habilitarla.

    Por ejemplo, agregue un parámetro como el siguiente:

    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
    

    Y en su función, si el parámetro se establece en false , salga temprano:

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

Realización de tareas de larga duración

Si su tarea no puede completarse dentro de la duración máxima de Cloud Functions, divida la tarea en subtareas y realice cada subtarea en secuencia poniendo trabajos en cola con el método TaskQueue.enqueue() del SDK de administración.

Por ejemplo, suponga que desea rellenar los datos de Cloud Firestore. Puede dividir la colección de documentos en fragmentos utilizando cursores de consulta . Después de procesar un fragmento, avance el desplazamiento inicial y ponga en cola otra invocación de función como se muestra a continuación:

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

Agrega la función a tu extension.yaml como se describe en la sección anterior .

Estado de informe

Cuando finalicen todas sus funciones de procesamiento, ya sea con éxito o con un error, informe el estado de la tarea utilizando los métodos de tiempo de ejecución de extensión del SDK de administrador. Los usuarios pueden ver este estado en la página de detalles de la extensión en Firebase console.

Finalización exitosa y errores no fatales

Para informar sobre la finalización exitosa y los errores no fatales (errores que no ponen la extensión en un estado no funcional), use el método de tiempo de ejecución de la extensión setProcessingState() del SDK de administración:

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

// ...

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

Puede establecer los siguientes estados:

Estados no fatales
PROCESSING_COMPLETE

Úselo para informar sobre la finalización exitosa de la tarea. Ejemplo:

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

Se utiliza para informar de un éxito parcial. Ejemplo:

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

Úselo para informar errores que impiden que se complete la tarea, pero no deje la extensión inutilizable. Ejemplo:

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

Para informar errores que dejan la extensión inutilizable, llama setFatalError() .

NONE

Úselo para borrar el estado de la tarea. Opcionalmente, puede usar esto para borrar el mensaje de estado de la consola (por ejemplo, después de que haya pasado una cantidad de tiempo desde que se configuró PROCESSING_COMPLETE ). Ejemplo:

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

Errores fatales

Si ocurre un error que impide que la extensión funcione, por ejemplo, falla una tarea de configuración requerida, informe el error fatal con setFatalError() :

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

// ...

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

Ajuste de la cola de tareas

Si establece la propiedad taskQueueTrigger en {} , su extensión aprovisionará una cola de Cloud Tasks con la configuración predeterminada cuando se instale una instancia de extensión. Como alternativa, puede ajustar los límites de simultaneidad de la cola de tareas y el comportamiento de reintento proporcionando valores específicos:

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

Consulte Configurar las colas de Cloud Tasks en los documentos de Google Cloud para obtener detalles sobre estos parámetros.

Ejemplos

Las extensiones oficiales storage-resize-images , firestore-bigquery-export y firestore-translate-text utilizan controladores de eventos del ciclo de vida para rellenar los datos.