获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

Riprova le funzioni asincrone

Questo documento descrive come richiedere funzioni in background asincrone (non HTTPS) per riprovare in caso di errore.

Semantica del tentativo

Cloud Functions garantisce l'esecuzione almeno una volta di una funzione basata su eventi per ogni evento emesso da un'origine evento. Tuttavia, per impostazione predefinita, se una chiamata di funzione termina con un errore, la funzione non verrà richiamata nuovamente e l'evento verrà eliminato. Quando abiliti i tentativi su una funzione basata su eventi, Cloud Functions ritenterà una chiamata di funzione non riuscita fino a quando non viene completata correttamente o la finestra per i tentativi non scade (per impostazione predefinita, dopo 7 giorni).

Quando i tentativi non sono abilitati per una funzione, che è l'impostazione predefinita, la funzione segnala sempre che è stata eseguita correttamente e nei suoi registri potrebbero essere visualizzati codici di risposta 200 OK . Ciò si verifica anche se la funzione ha rilevato un errore. Per chiarire quando la tua funzione rileva un errore, assicurati di segnalare gli errori in modo appropriato.

Perché le funzioni guidate da eventi non vengono completate

In rare occasioni, una funzione potrebbe terminare prematuramente a causa di un errore interno e, per impostazione predefinita, la funzione potrebbe essere ripetuta o meno automaticamente.

Più in genere, una funzione guidata da eventi potrebbe non riuscire a completarsi correttamente a causa di errori generati nel codice della funzione stessa. Alcuni dei motivi per cui ciò potrebbe accadere sono i seguenti:

  • La funzione contiene un bug e il runtime genera un'eccezione.
  • La funzione non può raggiungere un endpoint del servizio o scade durante il tentativo di raggiungere l'endpoint.
  • La funzione genera intenzionalmente un'eccezione (ad esempio, quando un parametro non supera la convalida).
  • Quando le funzioni scritte in Node.js restituiscono una promessa rifiutata o passano un valore non null a un callback.

In uno qualsiasi dei casi precedenti, la funzione interrompe l'esecuzione per impostazione predefinita e l'evento viene scartato. Se si desidera riprovare la funzione quando si verifica un errore, è possibile modificare il criterio di ripetizione predefinito impostando la proprietà "riprova in caso di errore" . Ciò fa sì che l'evento venga ritentato ripetutamente per un massimo di più giorni fino al completamento della funzione.

Abilitazione e disabilitazione dei tentativi

Utilizzo della console GCP

Puoi abilitare o disabilitare i tentativi nella console di GCP come segue:

  1. Vai alla pagina Panoramica di Cloud Functions nella console di Cloud Platform.

  2. Fare clic su Crea funzione . In alternativa, fai clic su una funzione esistente per andare alla relativa pagina dei dettagli e fai clic su Modifica .

  3. Compila i campi richiesti per la tua funzione.

  4. Assicurati che il campo Trigger sia impostato su un tipo di trigger basato su eventi, ad esempio Cloud Pub/Sub o Cloud Storage.

  5. Espandi le impostazioni avanzate facendo clic su Altro .

  6. Selezionare o deselezionare la casella denominata Riprova in caso di errore .

Nel codice funzione

Con Cloud Functions per Firebase, puoi abilitare i tentativi nel codice per una funzione. Per fare ciò per una funzione in background come functions.foo.onBar(myHandler); , utilizzare runWith e configurare una politica di errore:

functions.runWith({failurePolicy: true}).foo.onBar(myHandler);

L'impostazione true come mostrato configura una funzione per riprovare in caso di errore.

Migliori pratiche

Questa sezione descrive le best practice per l'utilizzo dei tentativi.

Utilizzare riprova per gestire gli errori temporanei

Poiché la tua funzione viene ripetuta continuamente fino all'esecuzione corretta, gli errori permanenti come i bug dovrebbero essere eliminati dal tuo codice attraverso il test prima di abilitare i tentativi. I tentativi sono utilizzati al meglio per gestire errori intermittenti/transitori che hanno un'elevata probabilità di risoluzione al tentativo, ad esempio un endpoint di servizio instabile o un timeout.

Imposta una condizione di fine per evitare cicli infiniti di tentativi

È consigliabile proteggere la funzione dal loop continuo quando si utilizzano i tentativi. Puoi farlo includendo una condizione finale ben definita, prima che la funzione inizi l'elaborazione. Si noti che questa tecnica funziona solo se la funzione viene avviata correttamente ed è in grado di valutare la condizione finale.

Un approccio semplice ma efficace consiste nell'eliminare gli eventi con timestamp più vecchi di un certo tempo. Questo consente di evitare esecuzioni eccessive quando gli errori sono persistenti o durano più a lungo del previsto.

Ad esempio, questo frammento di codice elimina tutti gli eventi più vecchi di 10 secondi:

const eventAgeMs = Date.now() - Date.parse(event.timestamp);
const eventMaxAgeMs = 10000;
if (eventAgeMs > eventMaxAgeMs) {
  console.log(`Dropping event ${event} with age[ms]: ${eventAgeMs}`);
  callback();
  return;
}

Usa catch con Promises

Se la tua funzione ha i tentativi abilitati, qualsiasi errore non gestito attiverà un nuovo tentativo. Assicurati che il tuo codice acquisisca eventuali errori che non dovrebbero comportare un nuovo tentativo.

Ecco un esempio di cosa dovresti fare:

return doFooAsync().catch((err) => {
    if (isFatal(err)) {
        console.error(`Fatal error ${err}`);
    }
    return Promise.reject(err);
});

Rendi le funzioni guidate da eventi ripetibili idempotenti

Le funzioni guidate da eventi che possono essere ripetute devono essere idempotenti. Ecco alcune linee guida generali per rendere tale funzione idempotente:

  • Molte API esterne (come Stripe) ti consentono di fornire una chiave di idempotenza come parametro. Se stai utilizzando un'API di questo tipo, dovresti utilizzare l'ID evento come chiave di idempotenza.
  • L'idempotenza funziona bene con la consegna almeno una volta, perché rende sicuro riprovare. Quindi una best practice generale per scrivere codice affidabile è combinare l'idempotenza con i tentativi.
  • Assicurati che il tuo codice sia internamente idempotente. Per esempio:
    • Assicurati che le mutazioni possano verificarsi più di una volta senza modificare il risultato.
    • Interroga lo stato del database in una transazione prima di modificare lo stato.
    • Assicurati che tutti gli effetti collaterali siano essi stessi idempotenti.
  • Imporre un controllo transazionale al di fuori della funzione, indipendentemente dal codice. Ad esempio, lo stato persistente da qualche parte registra che un determinato ID evento è già stato elaborato.
  • Gestione delle chiamate di funzioni duplicate fuori banda. Ad esempio, disporre di un processo di pulizia separato che ripulisce dopo le chiamate di funzione duplicate.