Riprova le funzioni asincrone

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

Semantica del riprovare

Cloud Functions garantisce l'esecuzione almeno una volta di una funzione basata sugli eventi per ogni evento emesso da un'origine evento. Per impostazione predefinita, se l'invocazione di una funzione termina con un errore, la funzione non viene richiamata nuovamente e l'evento viene eliminato. Quando abiliti i tentativi su una funzione basata su eventi, Cloud Functions riprova una chiamata di funzione non riuscita finché non viene completata correttamente o finché non scade la finestra per i tentativi.

Per le funzioni di seconda generazione, questo periodo di riprova scade dopo 24 ore. Per le funzioni di prima generazione, scade dopo 7 giorni. Cloud Functions riprova le funzioni basate sugli eventi appena create utilizzando una strategia di backoff esponenziale, con un backoff crescente compreso tra 10 e 600 secondi. Questa policy viene applicata alle nuove funzioni la prima volta che le distribuisci. Non viene applicata retroattivamente alle funzioni esistenti che sono state distribuite per la prima volta prima che le modifiche descritte in questa nota sulla versione diventassero effettive, anche se si ridistribuiscono le funzioni.

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 200 OK . Ciò si verifica anche se la funzione ha riscontrato un errore. Per rendere chiaro quando la tua funzione rileva un errore, assicurati di segnalare gli errori in modo appropriato.

Perché le funzioni guidate dagli eventi non vengono completate

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

Più in genere, una funzione guidata dagli eventi potrebbe non essere completata correttamente a causa di errori generati nel codice della funzione stessa. I motivi per cui ciò potrebbe accadere includono:

  • La funzione contiene un bug e il runtime genera un'eccezione.
  • La funzione non riesce a raggiungere un endpoint del servizio o va in timeout durante il tentativo di farlo.
  • La funzione genera intenzionalmente un'eccezione (ad esempio, quando un parametro non supera la convalida).
  • Una funzione Node.js restituisce una promessa rifiutata o passa un valore diverso da null a un callback.

In uno qualsiasi dei casi precedenti, la funzione interrompe l'esecuzione per impostazione predefinita e l'evento viene scartato. Per riprovare la funzione quando si verifica un errore, è possibile modificare la policy di ripetizione predefinita impostando la proprietà "riprova in caso di errore" . Ciò fa sì che l'evento venga ritentato ripetutamente finché la funzione non viene completata correttamente o finché non scade il timeout dei tentativi.

Abilita o disabilita i tentativi

Configura i tentativi dalla console GCP

Se stai creando una nuova funzione:

  1. Dalla schermata Crea funzione , seleziona Aggiungi trigger e scegli il tipo di evento che fungerà da trigger per la tua funzione.
  2. Nel riquadro Trigger Eventarc , seleziona la casella di controllo Riprova in caso di errore per abilitare i nuovi tentativi.

Se stai aggiornando una funzione esistente:

  1. Dalla pagina Panoramica di Cloud Functions , fai clic sul nome della funzione che stai aggiornando per aprire la relativa schermata Dettagli funzione , quindi scegli Modifica dalla barra dei menu per visualizzare i riquadri di trigger HTTPS ed Eventarc .
  2. Nel riquadro del trigger Eventarc , fai clic sull'icona Modifica per modificare le impostazioni del trigger.
  3. Nel riquadro Trigger Eventarc , seleziona o deseleziona la casella di controllo Riprova in caso di errore per abilitare o disabilitare i nuovi tentativi.

Configura i tentativi dal codice funzione

Con Cloud Functions for Firebase, puoi abilitare i tentativi nel codice per una funzione. Per fare ciò per una funzione in background come functions.foo.onBar(myHandler); , utilizza runWith e configura una policy 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

In questa sezione vengono descritte le procedure consigliate per l'utilizzo dei nuovi tentativi.

Utilizzare Riprova per gestire gli errori temporanei

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

Imposta una condizione finale per evitare cicli infiniti di tentativi

È consigliabile proteggere la funzione dal loop continuo quando si utilizzano i nuovi tentativi. Puoi farlo includendo una condizione finale ben definita, prima che la funzione inizi l'elaborazione. Tieni presente che questa tecnica funziona solo se la tua 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 determinato periodo di tempo. Ciò aiuta a evitare esecuzioni eccessive quando gli errori sono persistenti o durano più a lungo del previsto.

Ad esempio, questo snippet 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 Promesse

Se la tua funzione ha i tentativi abilitati, qualsiasi errore non gestito attiverà un nuovo tentativo. Assicurati che il tuo codice catturi 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);
});

Rendere idempotenti le funzioni guidate da eventi ripetibili

Le funzioni guidate dagli eventi che possono essere ritentate 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 utilizzi 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. Pertanto, una procedura consigliata generale per scrivere codice affidabile consiste nel combinare l'idempotenza con i nuovi 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.
    • Interrogare lo stato del database in una transazione prima di modificare lo stato.
    • Assicurarsi che tutti gli effetti collaterali siano essi stessi idempotenti.
  • Imporre un controllo transazionale al di fuori della funzione, indipendente dal codice. Ad esempio, persiste lo stato da qualche parte registrando che un determinato ID evento è già stato elaborato.
  • Gestisci le chiamate di funzioni duplicate fuori banda. Ad esempio, disporre di un processo di pulizia separato che pulisca dopo le chiamate di funzione duplicate.

Configurare la policy relativa ai nuovi tentativi

A seconda delle esigenze della tua Cloud Function, potresti voler configurare direttamente la policy dei nuovi tentativi. Ciò ti consentirebbe di impostare qualsiasi combinazione di quanto segue:

  • Riduci il periodo di tempo per riprovare da 7 giorni a un minimo di 10 minuti.
  • Modificare il tempo di backoff minimo e massimo per la strategia di nuovo tentativo di backoff esponenziale.
  • Modificare la strategia di riprovare immediatamente.
  • Configura un argomento non recapitabile .
  • Imposta un numero massimo e minimo di tentativi di consegna.

Per configurare la policy dei tentativi:

  1. Scrivere una funzione HTTP.
  2. Utilizza l'API Pub/Sub per creare una sottoscrizione Pub/Sub, specificando l'URL della funzione come destinazione.

Consulta la documentazione di Pub/Sub sulla gestione degli errori per ulteriori informazioni sulla configurazione diretta di Pub/Sub.