Gestisci gli eventi del ciclo di vita della tua estensione

L'estensione può includere funzioni Cloud Tasks che si attivano quando un'istanza dell'estensione attraversa uno dei seguenti eventi del ciclo di vita:

  • Viene installata un'istanza dell'estensione
  • Un'istanza dell'estensione viene aggiornata a una nuova versione
  • La configurazione di un'istanza di estensione viene modificata

Uno dei casi d'uso più importanti di questa funzione è il backfill dei dati . Ad esempio, supponi di creare un'estensione che genera anteprime in miniatura delle immagini caricate in un bucket Cloud Storage. Il lavoro principale della tua estensione verrebbe svolto in una funzione attivata dall'evento onFinalize Cloud Storage. Tuttavia, verranno elaborate solo le immagini caricate dopo l'installazione dell'estensione. Includendo nella tua estensione una funzione attivata dall'evento del ciclo di vita onInstall , puoi anche generare anteprime in miniatura di qualsiasi immagine esistente quando l'estensione è installata.

Alcuni altri casi d'uso dei trigger di eventi del ciclo di vita includono:

  • Automatizza la configurazione post-installazione (creazione di record di database, indicizzazione, ecc.)
  • Se devi pubblicare modifiche non compatibili con le versioni precedenti, esegui automaticamente la migrazione dei dati all'aggiornamento

Gestori di eventi del ciclo di vita di breve durata

Se la tua attività può essere eseguita completamente entro la durata massima di Cloud Functions (9 minuti utilizzando l'API di prima generazione), puoi scrivere il gestore dell'evento del ciclo di vita come una singola funzione che si attiva sulla coda delle attività dell'evento 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).
  });

Quindi, nel file extension.yaml della tua estensione, procedi come segue:

  1. Registra la tua funzione come risorsa di estensione con il set di proprietà taskQueueTrigger . Se imposti taskQueueTrigger sulla mappa vuota ( {} ), la tua estensione eseguirà il provisioning di una coda di Cloud Tasks utilizzando le impostazioni predefinite; è possibile regolare facoltativamente queste impostazioni .

    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. Registra la tua funzione come gestore per uno o più eventi del ciclo di vita:

    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
    
    

    È possibile registrare le funzioni per uno qualsiasi dei seguenti eventi: onInstall , onUpdate e onConfigure . Tutti questi eventi sono facoltativi.

  3. Consigliato : se l'attività di elaborazione non è necessaria per il funzionamento dell'estensione, aggiungi un parametro configurato dall'utente che consenta agli utenti di scegliere se abilitarlo.

    Ad esempio, aggiungi un parametro simile al seguente:

    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
    

    E nella tua funzione, se il parametro è impostato su false , esci presto:

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

Esecuzione di attività di lunga durata

Se la tua attività non può essere completata entro la durata massima di Cloud Functions, suddividi l'attività in sottoattività ed esegui ogni sottoattività in sequenza accodando i processi con il metodo TaskQueue.enqueue() di Admin SDK.

Ad esempio, supponi di voler eseguire il backfill dei dati di Cloud Firestore. È possibile suddividere la raccolta di documenti in blocchi utilizzando i cursori di query . Dopo aver elaborato un blocco, fai avanzare l'offset iniziale e accoda un'altra chiamata di funzione come mostrato di seguito:

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

Aggiungi la funzione al tuo extension.yaml come descritto nella sezione precedente .

Stato della segnalazione

Al termine di tutte le funzioni di elaborazione, correttamente o con un errore, segnala lo stato dell'attività utilizzando i metodi di runtime dell'estensione dell'SDK Admin. Gli utenti possono visualizzare questo stato nella pagina dei dettagli dell'estensione nella console Firebase.

Completamento riuscito ed errori non fatali

Per segnalare il completamento riuscito e gli errori non irreversibili (errori che non mettono l'estensione in uno stato non funzionante), utilizza il metodo di runtime dell'estensione setProcessingState() dell'Admin SDK:

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

// ...

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

È possibile impostare i seguenti stati:

Stati non fatali
PROCESSING_COMPLETE

Utilizzare per segnalare il completamento dell'attività con successo. Esempio:

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

Utilizzare per segnalare un successo parziale. Esempio:

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

Utilizzare per segnalare errori che impediscono il completamento dell'attività, ma non lasciare l'estensione inutilizzabile. Esempio:

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

Per segnalare errori che rendono l'estensione inutilizzabile, chiama setFatalError() .

NONE

Utilizzare per cancellare lo stato dell'attività. È possibile utilizzarlo facoltativamente per cancellare il messaggio di stato dalla console (ad esempio, dopo che è trascorso un po' di tempo dall'impostazione di PROCESSING_COMPLETE ). Esempio:

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

Errori fatali

Se si verifica un errore che impedisce il funzionamento dell'estensione, ad esempio un'attività di installazione richiesta non riuscita, segnalare l'errore irreversibile con setFatalError() :

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

// ...

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

Ottimizzazione della coda delle attività

Se imposti la proprietà taskQueueTrigger su {} , la tua estensione eseguirà il provisioning di una coda di Cloud Tasks con le impostazioni predefinite quando viene installata un'istanza dell'estensione. In alternativa, puoi regolare i limiti di concorrenza della coda delle attività e ripetere il comportamento fornendo valori specifici:

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

Consulta Configurare le code di Cloud Tasks nella documentazione di Google Cloud per i dettagli su questi parametri.

Esempi

Le storage-resize-images , firestore-bigquery-export e firestore-translate-text utilizzano tutte gestori di eventi del ciclo di vita per il backfill dei dati.