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.import … from

Il processo di migrazione

Le funzioni di 1ª e 2ª generazione possono coesistere nello stesso file. In questo modo, la migrazione può essere eseguita 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 di Firebase CLI e 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 successiva supporterà la 2ª gen. così come la 1ª gen.

Aggiorna le 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 2ª generazione favorisce le importazioni modulari, aggiorna le definizioni degli attivatori in modo che riflettano le importazioni modificate del passaggio precedente.

Gli argomenti passati ai callback per alcuni attivatori 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 2ª gen. non supportano più functions.config, ma offrono un'interfaccia più sicura per definire i parametri di configurazione in modo dichiarativo all'interno della base di codice. Con il nuovo modulo params, la CLI blocca il deployment a meno che tutti i parametri non abbiano un valore valido, garantendo che non venga eseguito il deployment di una funzione con configurazione mancante.

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: 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, 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 di functions.config, esegui firebase functions:config:export.

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

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: 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ª 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: 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 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 simultanee.

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 su 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. Controlla invece l'utilizzo delle variabili globali nelle funzioni e rimuovi 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 1ª alla 2ª generazione 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 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 sia la funzione di 1ª generazione originale sia quella di 2ª generazione siano in esecuzione.
    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 di 2ª generazione risponderanno a ogni evento immediatamente dopo il deployment.
  3. Una volta eseguita la migrazione di tutto il traffico, elimina la funzione di 1ª 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.