Upgrade delle funzioni Node.js di 1a generazione alla 2a generazione

Le app che utilizzano funzioni di 1ª gen. devono valutare la migrazione alla 2ª gen. seguendo le istruzioni riportate in questa guida. Le funzioni di seconda generazione utilizzano Cloud Run per offrire prestazioni migliori, configurazione migliore, monitoraggio migliore e altro ancora.

Gli esempi in questa pagina presuppongono che tu stia utilizzando JavaScript con i moduli CommonJS (importazioni di stile require), ma gli stessi principi si applicano a JavaScript con ESM (importazioni di stile import … from) e TypeScript.

Il processo di migrazione

Le funzioni di 1ª e 2ª generazione possono coesistere nello stesso file. Ciò consente una facile migrazione pezzo per pezzo, quando sei pronto. Ti consigliamo di eseguire la migrazione di una funzione alla volta, eseguendo test e verifiche prima di procedere.

Verifica le versioni dell'interfaccia a riga di comando di Firebase e di firebase-function

Assicurati di utilizzare almeno la versione 12.00 dell'interfaccia a riga di comando di Firebase e la versione 4.3.0 di firebase-functions. Qualsiasi versione più recente supporterà sia la 2ª gen. sia la 1ª gen.

Aggiornare le importazioni

Le funzioni di seconda generazione vengono importate dal sottopacchetto v2 dell'SDK firebase-functions. Questo percorso di importazione diverso è tutto ciò che serve all'interfaccia a riga di comando di Firebase per determinare se eseguire il deployment del codice della funzione come funzione di 1ª o 2ª gen.

Il pacchetto secondario v2 è modulare e ti consigliamo di importare solo il modulo specifico di cui hai bisogno.

Prima: 1ª gen.

const functions = require("firebase-functions/v1");

Dopo: 2ª gen.

// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

Aggiornare le definizioni dei trigger

Poiché l'SDK di seconda generazione favorisce le importazioni modulari, aggiorna le definizioni dei trigger in modo che riflettano le importazioni modificate del passaggio precedente.

Gli argomenti passati ai callback per alcuni trigger sono stati modificati. In questo esempio, nota che gli argomenti del callback onDocumentCreated sono stati consolidati in un unico oggetto event. Inoltre, alcuni trigger dispongono di nuove funzionalità di configurazione pratiche, come l'opzione cors del trigger onRequest.

Prima: 1ª gen.

const functions = require("firebase-functions/v1");

exports.date = functions.https.onRequest((req, res) => {
  // ...
});

exports.uppercase = functions.firestore
  .document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

Dopo: 2ª gen.

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

exports.date = onRequest({cors: true}, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

Utilizzare la configurazione con parametri

Le funzioni di 2ª gen. non supportano più functions.config a favore di un'interfaccia più sicura per definire i parametri di configurazione in modo dichiarativo all'interno del codebase. Con il nuovo modulo params, la CLI blocca il deployment a meno che tutti i parametri non abbiano un valore valido, garantendo che una funzione non venga sottoposta a deployment con una configurazione mancante.

Eseguire la migrazione al sottopacchetto params

Se hai utilizzato la configurazione dell'ambiente con functions.config, puoi eseguire la migrazione della configurazione esistente alla configurazione parametrizzata.

Prima: 1ª gen.

const functions = require("firebase-functions/v1");

exports.date = functions.https.onRequest((req, res) => {
  const date = new Date();
  const formattedDate =
date.toLocaleDateString(functions.config().dateformat);

  // ...
});

Dopo: 2ª gen.

const {onRequest} = require("firebase-functions/v2/https");
const {defineString} = require("firebase-functions/params");

const dateFormat = defineString("DATE_FORMAT");

exports.date = onRequest((req, res) => {
  const date = new Date();
  const formattedDate = date.toLocaleDateString(dateFormat.value());

  // ...
});

Impostare i valori di un parametro

La prima volta che esegui il deployment, la CLI Firebase richiede tutti i valori dei parametri e li salva in un file dotenv. Per esportare i valori di functions.config, esegui firebase functions:config:export.

Per una maggiore sicurezza, puoi anche specificare tipi di parametri e regole di convalida.

Caso speciale: chiavi API

Il modulo params si integra con Cloud Secret Manager, che fornisce un controllo dell'accesso granulare a valori sensibili come le chiavi API. Per saperne di più, consulta la sezione Parametri segreti.

Prima: 1ª gen.

const functions = require("firebase-functions/v1");

exports.getQuote = functions.https.onRequest(async (req, res) => {
  const quote = await fetchMotivationalQuote(functions.config().apiKey);
  // ...
});

Dopo: 2ª gen.

const {onRequest} = require("firebase-functions/v2/https");
const {defineSecret} = require("firebase-functions/params");

// Define the secret parameter
const apiKey = defineSecret("API_KEY");

exports.getQuote = onRequest(
  // make the secret available to this function
  { secrets: [apiKey] },
  async (req, res) => {
    // retrieve the value of the secret
    const quote = await fetchMotivationalQuote(apiKey.value());
    // ...
  }
);

Impostare le opzioni di runtime

La configurazione delle opzioni di runtime è cambiata tra la 1ª e la 2ª gen. La 2ª gen aggiunge anche una nuova funzionalità per impostare le opzioni per tutte le funzioni.

Prima: 1ª gen.

const functions = require("firebase-functions/v1");

exports.date = functions
  .runWith({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  })
  // locate function closest to users
  .region("asia-northeast1")
  .https.onRequest((req, res) => {
    // ...
  });

exports.uppercase = functions
  // locate function closest to users and database
  .region("asia-northeast1")
  .firestore.document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

Dopo: 2ª gen.

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions/v2");

// locate all functions closest to users
setGlobalOptions({ region: "asia-northeast1" });

exports.date = onRequest({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  }, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

Utilizzare la contemporaneità

Un vantaggio significativo delle funzioni di seconda generazione è la capacità di una singola istanza di funzione di gestire più richieste contemporaneamente. Ciò può ridurre drasticamente il numero di avvii a freddo riscontrati dagli utenti finali. Per impostazione predefinita, la contemporaneità è impostata su 80, ma puoi impostarla su qualsiasi valore compreso tra 1 e 1000:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // set concurrency value
    concurrency: 500
  },
  (req, res) => {
    // ...
});

La regolazione della concorrenza può migliorare le prestazioni e ridurre i costi delle funzioni. Scopri di più sulla concorrenza in Consenti richieste simultanee.

Controllare l'utilizzo delle variabili globali

Le funzioni di prima generazione scritte senza tenere conto della concorrenza potrebbero utilizzare variabili globali che vengono impostate e lette a ogni richiesta. Quando la concorrenza è abilitata e una singola istanza inizia a gestire più richieste contemporaneamente, potrebbero verificarsi bug nella funzione, poiché le richieste simultanee iniziano a impostare e leggere le variabili globali contemporaneamente.

Durante l'upgrade, puoi impostare la CPU della funzione su gcf_gen1 e impostare concurrency su 1 per ripristinare il comportamento di 1ª gen.:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // TEMPORARY FIX: remove concurrency
    cpu: "gcf_gen1",
    concurrency: 1
  },
  (req, res) => {
    // ...
});

Tuttavia, questa soluzione non è consigliata come correzione a lungo termine, in quanto rinuncia ai vantaggi in termini di prestazioni delle funzioni di seconda generazione. Esegui invece un controllo dell'utilizzo delle variabili globali nelle tue funzioni e rimuovi queste impostazioni temporanee quando è tutto pronto.

Eseguire la migrazione del traffico alle nuove funzioni di 2ª gen.

Proprio come quando modificavi la regione o il tipo di trigger di una funzione, dovrai assegnare un nuovo nome alla funzione di 2ª gen. e migrare lentamente il traffico.

Non è possibile eseguire l'upgrade di una funzione dalla 1ª alla 2ª gen. con lo stesso nome ed eseguire firebase deploy. In questo modo, verrà visualizzato l'errore:

Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.

Prima di seguire questi passaggi, assicurati che la funzione sia idempotente, poiché durante la modifica verranno eseguite contemporaneamente sia la nuova versione che quella precedente. Ad esempio, se hai una funzione di 1ª gen. che risponde agli eventi di scrittura in Firestore, assicurati che la risposta a una scrittura due volte, una volta dalla funzione di 1ª gen. e una volta dalla funzione di 2ª gen., in risposta a questi eventi lasci la tua app in uno stato coerente.

  1. Rinomina la funzione nel codice delle funzioni. Ad esempio, rinomina resizeImage in resizeImageSecondGen.
  2. Esegui il deployment della funzione, in modo che siano in esecuzione sia la funzione di 1ª gen. originale sia la funzione di 2ª gen.
    1. Nel caso di trigger richiamabili, Task Queue e HTTP, inizia a indirizzare tutti i client alla funzione di 2ª gen. aggiornando il codice client con il nome o l'URL della funzione di 2ª gen.
    2. Con i trigger in background, le funzioni di 1ª e 2ª gen. risponderanno a ogni evento immediatamente dopo il deployment.
  3. Una volta eseguita la migrazione di tutto il traffico, elimina la funzione di 1ª gen. utilizzando il comando firebase functions:delete dell'interfaccia a riga di comando di Firebase.
    1. (Facoltativo) Rinomina la funzione di 2ª gen. in modo che corrisponda al nome della funzione di 1ª gen.