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

Le app che attualmente utilizzano le funzioni di 1ª gen. dovrebbero valutare la possibilità di eseguire la migrazione alla 2ª gen. utilizzando le istruzioni riportate in questa guida. Le funzioni di 2ª gen. utilizzano Cloud Run per offrire un rendimento migliore, una configurazione migliore, un monitoraggio migliore e altro ancora.

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

Il processo di migrazione

Le funzioni di 1ª e 2ª generazione possono coesistere nello stesso file. In questo modo, puoi eseguire la migrazione facilmente, pezzo per pezzo, quando è tutto pronto. Ti consigliamo di eseguire la migrazione di una funzione alla volta, effettuando 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à la 2a e la 1a gen.,

Aggiorna importazioni

Le funzioni di 2ª generazione vengono importate dal sottopacchetto v2 nell'SDK firebase-functions. Questo percorso di importazione diverso è tutto ciò di cui l'interfaccia a riga di comando di Firebase ha bisogno per determinare se eseguire il deployment del codice della funzione come funzione di 1ª o 2ª generazione.

Il sottopacchetto 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 degli attivatori

Poiché l'SDK di 2a generazione favorisce le importazioni modulari, aggiorna le definizioni dei trigger per riflettere le importazioni modificate del passaggio precedente.

Gli argomenti passati ai callback per alcuni trigger sono stati modificati. In questo esempio, tieni presente che gli argomenti della chiamata di callback onDocumentCreated sono stati consolidati in un unico oggetto event. Inoltre, alcuni attivatori dispongono di nuove e comode funzionalità di configurazione, come l'opzione onRequest dell'attivatore cors.

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 2a generazione eliminano il supporto per 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, l'interfaccia a riga di comando blocca il deployment a meno che tutti i parametri non abbiano un valore valido, assicurando che non venga eseguito il deployment di una funzione se manca la configurazione.

Esegui la migrazione al sottopacchetto params

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

Prima: 1a generazione

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, l'interfaccia a riga di comando di Firebase richiede tutti i valori dei parametri e li salva in un file dotenv. Per esportare i valori functions.config, esegui firebase functions:config:export.

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

Caso speciale: chiavi API

Il modulo params si integra con Cloud Secret Manager, che fornisce un controllo granulare dell'accesso a valori sensibili come le chiavi API. Per ulteriori informazioni, consulta i parametri secret.

Prima: 1a generazione

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ª generazione. La 2ª generazione aggiunge inoltre 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: 2a generazione

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 concorrenza

Un vantaggio significativo delle funzioni di 2ª generazione è la capacità di una singola istanza di funzione di gestire più richieste contemporaneamente. In questo modo, è possibile ridurre notevolmente 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) => {
    // ...
});

L'ottimizzazione della concorrenza può migliorare le prestazioni e ridurre i costi delle funzioni. Scopri di più sulla concorrenza in Consenti richieste concorrenti.

Controllare l'utilizzo delle variabili globali

Le funzioni di 1ª gen. scritte senza tenere conto della concorrenza potrebbero utilizzare variabili globali che vengono impostate e lette a ogni richiesta. Quando la concorrenza è attivata e una singola istanza inizia a gestire più richieste contemporaneamente, potrebbero verificarsi bug nella funzione man mano che le richieste concorrenti iniziano a impostare e leggere contemporaneamente le variabili globali.

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

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 soluzione a lungo termine, in quanto comporta la perdita dei vantaggi in termini di prestazioni delle funzioni di 2ª generazione. Puoi invece controllare l'utilizzo delle variabili globali nelle tue funzioni e rimuovere queste impostazioni temporanee quando è tutto pronto.

Eseguire la migrazione del traffico alle nuove funzioni di 2ª generazione

Come quando modifichi la regione o il tipo di attivatore di una funzione, dovrai assegnare un nuovo nome alla funzione di 2ª generazione e migrare gradualmente il traffico verso questa.

Non è possibile eseguire l'upgrade di una funzione dalla 1a alla 2a generazione con lo stesso nome ed eseguire firebase deploy. Questa operazione causerà 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 che la vecchia versione. 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 originale di 1a generazione sia la funzione di 2a generazione.
    1. Nel caso di trigger chiamabili, coda di lavoro e HTTP, inizia a indirizzare tutti i client alla funzione di 2ª generazione aggiornando il codice client con il nome o l'URL della funzione di 2ª generazione.
    2. Con gli attivatori in background, sia le funzioni di 1ª che quelle di 2ª generazione risponderanno a ogni evento immediatamente dopo il deployment.
  3. Dopo aver disattivato la migrazione di tutto il traffico, elimina la funzione di 1a generazione utilizzando il comando firebase functions:delete dell'interfaccia a riga di comando di Firebase.
    1. Se vuoi, rinomina la funzione di 2ª generazione in modo che corrisponda al nome della funzione di 1ª generazione.