Gestire le funzioni


Puoi distribuire, eliminare e modificare le funzioni utilizzando i comandi CLI di Firebase o impostando le opzioni di runtime nel codice sorgente delle funzioni.

Distribuire funzioni

Per distribuire le funzioni, esegui questo comando CLI di Firebase:

firebase deploy --only functions

Per impostazione predefinita, la CLI Firebase distribuisce tutte le funzioni all'interno dell'origine contemporaneamente. Se il tuo progetto contiene più di 5 funzioni, ti consigliamo di utilizzare il flag --only con nomi di funzioni specifici per distribuire solo le funzioni che hai modificato. La distribuzione di funzioni specifiche in questo modo accelera il processo di distribuzione e aiuta a evitare di incorrere in quote di distribuzione. Per esempio:

firebase deploy --only functions:addMessage,functions:makeUppercase

Quando si distribuiscono un numero elevato di funzioni, è possibile superare la quota standard e ricevere messaggi di errore HTTP 429 o 500. Per risolvere questo problema, distribuire le funzioni in gruppi di 10 o meno.

Consulta il riferimento alla CLI di Firebase per l'elenco completo dei comandi disponibili.

Per impostazione predefinita, la CLI di Firebase cerca il codice sorgente nella cartella functions/ . Se preferisci, puoi organizzare le funzioni in basi di codice o più set di file.

Elimina funzioni

È possibile eliminare le funzioni precedentemente distribuite in questi modi:

  • esplicitamente nella CLI Firebase con functions:delete
  • esplicitamente nella console Google Cloud .
  • implicitamente rimuovendo la funzione dall'origine prima della distribuzione.

Tutte le operazioni di cancellazione richiedono una conferma prima di rimuovere la funzione dalla produzione.

L'eliminazione esplicita delle funzioni nella CLI di Firebase supporta più argomenti e gruppi di funzioni e consente di specificare una funzione in esecuzione in una particolare regione. Inoltre, è possibile ignorare la richiesta di conferma.

# Delete all functions that match the specified name in all regions.
firebase functions:delete myFunction
# Delete a specified function running in a specific region.
firebase functions:delete myFunction --region us-east-1
# Delete more than one function
firebase functions:delete myFunction myOtherFunction
# Delete a specified functions group.
firebase functions:delete groupA
# Bypass the confirmation prompt.
firebase functions:delete myFunction --force

Con l'eliminazione implicita delle funzioni, firebase deploy analizza l'origine e rimuove dalla produzione tutte le funzioni che sono state rimosse dal file.

Modifica il nome, la regione o il trigger di una funzione

Se stai rinominando o modificando le regioni o attivando le funzioni che gestiscono il traffico di produzione, segui i passaggi in questa sezione per evitare di perdere eventi durante la modifica. Prima di seguire questi passaggi, assicurati innanzitutto che la tua funzione sia idempotente , poiché sia ​​la nuova versione che la vecchia versione della tua funzione verranno eseguite contemporaneamente durante la modifica.

Rinominare una funzione

Per rinominare una funzione, crea una nuova versione rinominata della funzione nell'origine e quindi esegui due comandi di distribuzione separati. Il primo comando distribuisce la funzione appena denominata e il secondo comando rimuove la versione precedentemente distribuita. Ad esempio, se disponi di un webhook attivato da HTTP che desideri rinominare, rivedi il codice come segue:

Node.js

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

exports.webhook = onRequest((req, res) => {
    res.send("Hello");
});

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

exports.webhookNew = onRequest((req, res) => {
    res.send("Hello");
});

Pitone

# before
from firebase_functions import https_fn

@https_fn.on_request()
def webhook(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello world!")

# after
from firebase_functions import https_fn

@https_fn.on_request()
def webhook_new(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello world!")

Quindi esegui i seguenti comandi per distribuire la nuova funzione:

# Deploy new function
firebase deploy --only functions:webhookNew

# Wait until deployment is done; now both functions are running

# Delete webhook
firebase functions:delete webhook

Modificare la regione o le regioni di una funzione

Se stai modificando le regioni specificate per una funzione che gestisce il traffico di produzione, puoi prevenire la perdita di eventi eseguendo questi passaggi in ordine:

  1. Rinominare la funzione e modificarne la regione o le regioni come desiderato.
  2. Distribuire la funzione rinominata, che comporta l'esecuzione temporanea dello stesso codice in entrambi i gruppi di regioni.
  3. Elimina la funzione precedente.

Ad esempio, se disponi di una funzione attivata da Cloud Firestore che si trova attualmente nell'area delle funzioni predefinite di us-central1 e desideri migrarla in asia-northeast1 , devi prima modificare il codice sorgente per rinominare la funzione e rivedere la Regione.

Node.js

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

// after
exports.firestoreTriggerAsia = onDocumentCreated(
  {
    document: "my-collection/{docId}",
    region: "asia-northeast1",
  },
  (event) => {},
);

Il codice aggiornato dovrebbe specificare il filtro eventi corretto (in questo caso document ) insieme alla regione. Per ulteriori informazioni, consulta le posizioni di Cloud Functions .

Pitone

# Before
@firestore_fn.on_document_created("my-collection/{docId}")
def firestore_trigger(event):
    pass

# After
@firestore_fn.on_document_created("my-collection/{docId}",
                                  region="asia-northeast1")
def firestore_trigger_asia(event):
    pass

Quindi distribuire eseguendo:

firebase deploy --only functions:firestoreTriggerAsia

Ora ci sono due funzioni identiche in esecuzione: firestoreTrigger è in esecuzione in us-central1 e firestoreTriggerAsia è in esecuzione in asia-northeast1 .

Quindi, elimina firestoreTrigger :

firebase functions:delete firestoreTrigger

Ora c'è solo una funzione: firestoreTriggerAsia , che è in esecuzione in asia-northeast1 .

Modificare il tipo di trigger di una funzione

Man mano che sviluppi la distribuzione di Cloud Functions for Firebase nel tempo, potrebbe essere necessario modificare il tipo di trigger di una funzione per vari motivi. Ad esempio, potresti voler passare da un tipo di evento Firebase Realtime Database o Cloud Firestore a un altro tipo.

Non è possibile modificare il tipo di evento di una funzione semplicemente modificando il codice sorgente ed eseguendo firebase deploy . Per evitare errori, modificare il tipo di trigger di una funzione con questa procedura:

  1. Modificare il codice sorgente per includere una nuova funzione con il tipo di trigger desiderato.
  2. Distribuire la funzione, che comporta l'esecuzione temporanea sia della vecchia che della nuova funzione.
  3. Elimina esplicitamente la vecchia funzione dalla produzione utilizzando la CLI Firebase.

Ad esempio, se avevi una funzione che veniva attivata quando un oggetto veniva eliminato, ma poi hai abilitato il controllo delle versioni dell'oggetto e vorresti invece iscriverti all'evento di archivio, prima rinomina la funzione e modificala per avere il nuovo tipo di trigger.

Node.js

// before
const {onObjectDeleted} = require("firebase-functions/v2/storage");

exports.objectDeleted = onObjectDeleted((event) => {
    // ...
});

// after
const {onObjectArchived} = require("firebase-functions/v2/storage");

exports.objectArchived = onObjectArchived((event) => {
    // ...
});

Pitone

# before
from firebase_functions import storage_fn

@storage_fn.on_object_deleted()
def object_deleted(event):
  # ...

# after 
from firebase_functions import storage_fn

@storage_fn.on_object_archived()
def object_archived(event):
  # ...

Quindi esegui i seguenti comandi per creare prima la nuova funzione, prima di eliminare la vecchia funzione:

# Create new function objectArchived
firebase deploy --only functions:objectArchived

# Wait until deployment is done; now both objectDeleted and objectArchived are running

# Delete objectDeleted
firebase functions:delete objectDeleted

Imposta le opzioni di esecuzione

Cloud Functions for Firebase ti consente di selezionare opzioni di runtime come la versione di runtime di Node.js e il timeout per funzione, l'allocazione di memoria e le istanze di funzione minima/massima.

Come best practice, queste opzioni (ad eccezione della versione Node.js) dovrebbero essere impostate su un oggetto di configurazione all'interno del codice funzione. Questo oggetto RuntimeOptions è la fonte attendibile per le opzioni di runtime della tua funzione e sovrascriverà le opzioni impostate tramite qualsiasi altro metodo (ad esempio tramite la console Google Cloud o la CLI gcloud).

Se il flusso di lavoro di sviluppo prevede l'impostazione manuale delle opzioni di runtime tramite la console Google Cloud o la CLI gcloud e non desideri che questi valori vengano sovrascritti a ogni distribuzione, imposta l'opzione preserveExternalChanges su true . Con questa opzione impostata su true , Firebase unisce le opzioni di runtime impostate nel codice con le impostazioni della versione attualmente distribuita della funzione con la seguente priorità:

  1. L'opzione è impostata nel codice delle funzioni: sovrascrive le modifiche esterne.
  2. L'opzione è impostata su RESET_VALUE nel codice delle funzioni: sovrascrive le modifiche esterne con il valore predefinito.
  3. L'opzione non è impostata nel codice delle funzioni, ma è impostata nella funzione attualmente distribuita: utilizzare l'opzione specificata nella funzione distribuita.

L'uso dell'opzione preserveExternalChanges: true non è consigliato per la maggior parte degli scenari perché il codice non sarà più la fonte completa di verità per le opzioni di runtime per le tue funzioni. Se lo utilizzi, controlla la console Google Cloud o utilizza la CLI gcloud per visualizzare la configurazione completa di una funzione.

Imposta la versione di Node.js

L'SDK Firebase per Cloud Functions consente una selezione del runtime Node.js. Puoi scegliere di eseguire tutte le funzioni in un progetto esclusivamente sull'ambiente runtime corrispondente a una di queste versioni Node.js supportate:

  • Node.js 20 (anteprima)
  • Node.js 18
  • Node.js 16
  • Node.js 14

Per impostare la versione di Node.js:

Puoi impostare la versione nel campo engines nel file package.json creato nella directory functions/ durante l'inizializzazione. Ad esempio, per utilizzare solo la versione 18, modifica questa riga in package.json :

  "engines": {"node": "18"}

Se utilizzi il gestore pacchetti Yarn o hai altri requisiti specifici per il campo engines , puoi invece impostare il runtime per Firebase SDK for Cloud Functions in firebase.json :

  {
    "functions": {
      "runtime": "nodejs18" // or nodejs14, nodejs16 or nodejs20
    }
  }

La CLI utilizza il valore impostato in firebase.json anziché qualsiasi valore o intervallo impostato separatamente in package.json .

Aggiorna il tuo runtime Node.js

Per aggiornare il tuo runtime Node.js:

  1. Assicurati che il tuo progetto sia incluso nel piano tariffario Blaze .
  2. Assicurati di utilizzare la CLI Firebase v11.18.0 o successiva.
  3. Modifica il valore engines nel file package.json creato nella directory functions/ durante l'inizializzazione. Ad esempio, se stai aggiornando dalla versione 16 alla versione 18, la voce dovrebbe essere simile a questa: "engines": {"node": "18"}
  4. Facoltativamente, prova le modifiche utilizzando Firebase Local Emulator Suite .
  5. Ridistribuire tutte le funzioni.

Imposta la versione di Python

Firebase SDK for Cloud Functions versioni 12.0.0 e successive consentono la selezione del runtime Python. Imposta la versione runtime in firebase.json come mostrato:

  {
    "functions": {
      "runtime": "python310" // or python311
    }
  }

Controllare il comportamento di ridimensionamento

Per impostazione predefinita, Cloud Functions for Firebase ridimensiona il numero di istanze in esecuzione in base al numero di richieste in entrata, riducendo potenzialmente fino a zero istanze in periodi di traffico ridotto. Tuttavia, se la tua app richiede una latenza ridotta e desideri limitare il numero di avvii a freddo, puoi modificare questo comportamento predefinito specificando un numero minimo di istanze di contenitore da mantenere attive e pronte a servire le richieste.

Allo stesso modo, puoi impostare un numero massimo per limitare il ridimensionamento delle istanze in risposta alle richieste in arrivo. Utilizza questa impostazione per controllare i costi o per limitare il numero di connessioni a un servizio di supporto, ad esempio a un database.

Utilizzando queste impostazioni insieme all'impostazione di concorrenza per istanza (nuova nella seconda generazione), puoi controllare e ottimizzare il comportamento di ridimensionamento delle tue funzioni. La natura dell'applicazione e della funzione determinerà quali impostazioni sono più convenienti e garantiranno le migliori prestazioni.

Per alcune app con traffico ridotto, un'opzione CPU inferiore senza multi-concurrency è ottimale. Per altri, in cui gli avviamenti a freddo rappresentano un problema critico, l'impostazione di una concorrenza elevata e di istanze minime significa che un insieme di istanze viene sempre mantenuto caldo per gestire grandi picchi di traffico.

Per le app su scala più piccola che ricevono pochissimo traffico, l'impostazione di istanze massime basse con elevata concorrenza significa che l'app può gestire picchi di traffico senza incorrere in costi eccessivi. Tuttavia, tieni presente che quando il numero massimo di istanze è impostato su un valore troppo basso, le richieste potrebbero essere abbandonate una volta raggiunto il limite.

Consenti richieste simultanee

In Cloud Functions for Firebase (1a generazione), ogni istanza poteva gestire una richiesta alla volta, quindi il comportamento di ridimensionamento veniva impostato solo con le impostazioni minime e massime delle istanze. Oltre a controllare il numero di istanze, in Cloud Functions for Firebase (2ª generazione) puoi controllare il numero di richieste che ciascuna istanza può servire contemporaneamente con l'opzione concurrency . Il valore predefinito per la concorrenza è 80, ma puoi impostarlo su qualsiasi numero intero compreso tra 1 e 1000.

Le funzioni con impostazioni di concorrenza più elevate possono assorbire picchi di traffico senza avvio a freddo perché ogni istanza probabilmente ha un certo margine. Se un'istanza è configurata per gestire fino a 50 richieste simultanee ma attualmente ne gestisce solo 25, può gestire un picco di 25 richieste aggiuntive senza richiedere l'avvio a freddo di una nuova istanza. Al contrario, con un'impostazione di concorrenza pari a solo 1, il picco di richieste potrebbe portare a 25 avvii a freddo.

Questo scenario semplificato dimostra i potenziali miglioramenti in termini di efficienza della concorrenza. In realtà, ridimensionare il comportamento per ottimizzare l’efficienza e ridurre gli avviamenti a freddo con la concorrenza è più complesso. La concorrenza in Cloud Functions for Firebase di seconda generazione è basata sulla tecnologia Cloud Run e segue le regole di scalabilità automatica delle istanze di container di Cloud Run.

Quando sperimenti impostazioni di concorrenza più elevate in Cloud Functions for Firebase (2ª generazione), tieni presente quanto segue:

  • Impostazioni di concorrenza più elevate potrebbero richiedere CPU e RAM più elevate per prestazioni ottimali fino al raggiungimento di un limite pratico. Una funzione che esegue un'elaborazione pesante di immagini o video, ad esempio, potrebbe non avere le risorse per gestire 1000 richieste simultanee, anche quando le impostazioni di CPU e RAM sono massimizzate.
  • Poiché Cloud Functions for Firebase (2ª generazione) è basato su Cloud Run, puoi fare riferimento anche alle indicazioni di Google Cloud per ottimizzare la concorrenza .
  • Assicurati di testare accuratamente la multiconcorrenza in un ambiente di test prima di passare alla multiconcorrenza in produzione.

Mantieni caldo un numero minimo di istanze

È possibile impostare il numero minimo di istanze per una funzione nel codice sorgente. Ad esempio, questa funzione imposta un minimo di 5 istanze per riscaldarsi:

Node.js

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

exports.getAutocompleteResponse = onCall(
  {
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  },
  (event) => {
    // Autocomplete user’s search term
  }
);

Pitone

@https_fn.on_call(min_instances=5)
def get_autocomplete_response(event: https_fn.CallableRequest) -> https_fn.Response:

Ecco alcuni aspetti da considerare quando si imposta un valore minimo per le istanze:

  • Se Cloud Functions for Firebase ridimensiona la tua app al di sopra della tua impostazione, noterai un avvio a freddo per ogni istanza al di sopra di tale soglia.
  • Gli avvii a freddo hanno l'effetto più grave sulle app con traffico intenso. Se la tua app presenta picchi di traffico e imposti un valore sufficientemente alto da ridurre gli avviamenti a freddo a ogni aumento del traffico, noterai una latenza notevolmente ridotta. Per le app con traffico costante, è improbabile che gli avvii a freddo influenzino gravemente le prestazioni.
  • L'impostazione di istanze minime può avere senso per gli ambienti di produzione, ma in genere dovrebbe essere evitata negli ambienti di test. Per scalare fino a zero nel tuo progetto di test ma ridurre comunque gli avvii a freddo nel tuo progetto di produzione, puoi impostare un valore minimo di istanze nella configurazione parametrizzata:

    Node.js

    const functions = require('firebase-functions');
    const { defineInt, defineString } = require('firebase-functions/params');
    
    // Define some parameters
    const minInstancesConfig = defineInt('HELLO_WORLD_MININSTANCES');
    const welcomeMessage = defineString('WELCOME_MESSAGE');
    
    // To use configured parameters inside the config for a function, provide them 
    // directly. To use them at runtime, call .value() on them.
    export const helloWorld = functions.runWith({ minInstances: minInstancesConfig}).https.onRequest(
      (req, res) => {
        res.send(`${welcomeMessage.value()}! I am a function.`);
      }
    );
    

    Pitone

    MIN_INSTANCES = params.IntParam("HELLO_WORLD_MININSTANCES")
    WELCOME_MESSAGE = params.StringParam("WELCOME_MESSAGE")
    
    @https_fn.on_request(min_instances=MIN_INSTANCES.value())
    def get_autocomplete_response(event: https_fn.Request) -> https_fn.Response:
        return https_fn.Response(f"{WELCOME_MESSAGE.value()} I'm a function.")
    

Limita il numero massimo di istanze per una funzione

È possibile impostare un valore per il numero massimo di istanze nel codice sorgente della funzione. Ad esempio questa funzione imposta un limite di 100 istanze per non sovraccaricare un ipotetico database legacy:

Node.js

const { onMessagePublished } = require("firebase-functions/v2/pubsub");

exports.mirrorevents = onMessagePublished(
  { topic: "topic-name", maxInstances: 100 },
  (event) => {
    // Connect to legacy database
  }
);

Pitone

@pubsub_fn.on_message_published(topic="topic-name", max_instances=100)
def mirrorevents(event: pubsub_fn.CloudEvent):
#  Connect to legacy database

Se una funzione HTTP viene scalata fino al limite massimo di istanze, le nuove richieste vengono accodate per 30 secondi e quindi rifiutate con un codice di risposta 429 Too Many Requests se entro quel momento non è disponibile alcuna istanza.

Per ulteriori informazioni sulle best practice per l'utilizzo delle impostazioni del numero massimo di istanze, consulta queste best practice per l'impostazione del numero massimo di istanze .

Imposta il timeout e l'allocazione della memoria

In alcuni casi, le funzioni potrebbero avere requisiti speciali per un valore di timeout lungo o una grande allocazione di memoria. Puoi impostare questi valori nella console Google Cloud o nel codice sorgente della funzione (solo Firebase).

Per impostare l'allocazione della memoria e il timeout nel codice sorgente delle funzioni, utilizzare le opzioni globali per la memoria e i secondi di timeout per personalizzare la macchina virtuale che esegue le funzioni. Ad esempio, questa funzione Cloud Storage utilizza 1GiB di memoria e va in timeout dopo 300 secondi:

Node.js

exports.convertLargeFile = onObjectFinalized({
  timeoutSeconds: 300,
  memory: "1GiB",
}, (event) => {
  // Do some complicated things that take a lot of memory and time
});

Pitone

@storage_fn.on_object_finalized(timeout_sec=300, memory=options.MemoryOption.GB_1)
def convert_large_file(event: storage_fn.CloudEvent):
# Do some complicated things that take a lot of memory and time.

Il valore massimo per i secondi di timeout è 540 o 9 minuti.

Per impostare l'allocazione della memoria e il timeout nella console Google Cloud:

  1. Nella console Google Cloud seleziona Cloud Functions for Firebase dal menu a sinistra.
  2. Selezionare una funzione facendo clic sul suo nome nell'elenco delle funzioni.
  3. Fai clic sull'icona Modifica nel menu in alto.
  4. Selezionare un'allocazione di memoria dal menu a discesa denominato Memoria allocata .
  5. Fare clic su Altro per visualizzare le opzioni avanzate e immettere un numero di secondi nella casella di testo Timeout .
  6. Fare clic su Salva per aggiornare la funzione.

Sostituisci le impostazioni predefinite della CPU

Fino a 2 GB di memoria allocata, ciascuna funzione in Cloud Functions for Firebase (2ª generazione) utilizza per impostazione predefinita una CPU, quindi aumenta a 2 CPU per 4 e 8 GB. Tieni presente che questo è significativamente diverso dal comportamento predefinito della prima generazione in modi che potrebbero portare a costi leggermente più elevati per le funzioni con memoria ridotta, come espresso nella tabella seguente:

RAM allocata CPU predefinita versione 1 (frazionaria) CPU predefinita della versione 2 Aumento del prezzo per ms
128 MB 1/12 1 10,5x
256 MB 1/6 1 5,3x
512 MB 1/3 1 2,7x
1 GB 7/12 1 1,6x
2GB 1 1 1x
4GB 2 2 1x
8 GB 2 2 1x
16 GB n / a 4 n / a

Se preferisci il comportamento di prima generazione per le funzioni di seconda generazione, imposta le impostazioni predefinite di prima generazione come opzione globale:

Node.js

// Turn off Firebase defaults
setGlobalOptions({ cpu: 'gcf_gen1' });

Pitone

# Use 1st gen behavior
set_global_options(cpu="gcf_gen1")

Per le funzioni ad uso intensivo della CPU, la seconda generazione offre la flessibilità di configurare CPU aggiuntiva. Puoi potenziare la CPU in base alla funzione, come mostrato:

Node.js

// Boost CPU in a function:
export const analyzeImage = onObjectFinalized({ cpu: 2 }, (event) => {
  // computer vision goes here
});

Pitone

# Boost CPU in a function:
@storage_fn.on_object_finalized(cpu=2)
def analyze_image(event: storage_fn.CloudEvent):
# computer vision goes here