Best practice per le pipeline con batch di grandi dimensioni

Questo documento spiega come ridurre al minimo l'impatto degli errori di job per le pipeline batch di grandi dimensioni. Gli errori relativi a carichi di lavoro di grandi dimensioni hanno un impatto particolarmente significativo a causa del tempo e del denaro necessari per recuperarli e correggerli. Riprovare da zero a eseguire queste pipeline in caso di errore è costoso in termini di tempo e denaro.

Per ridurre i costosi errori delle pipeline batch, segui le linee guida riportate in questa pagina. Poiché non è sempre possibile evitare completamente gli elementi non riusciti e gli errori della pipeline, le tecniche fornite si concentrano sull'aumento della resilienza, sulla riduzione del costo degli errori e sulla semplificazione del debug e della comprensione degli errori quando si verificano.

Per le best practice generali relative alle pipeline, consulta Best practice per le pipeline di Dataflow.

Esegui piccoli esperimenti per job di grandi dimensioni

Prima di eseguire un job batch di grandi dimensioni, esegui uno o più job più piccoli su un sottoinsieme del set di dati. Questa tecnica può fornire una stima dei costi e aiutare a trovare potenziali punti di guasto.

Stima dei costi

L'esecuzione di esperimenti può fornire una stima minima del costo totale dell'esecuzione del job. In genere, il calcolo del costo del job è cost of test job*size(full dataset)/size(test dataset). A seconda della pipeline, il costo può aumentare in modo superlineare o, meno spesso, sottolineare. Tuttavia, questo passaggio spesso fornisce una buona stima approssimativa del costo del job. Puoi anche provare input di dimensioni diverse per ottenere una stima migliore dell'evoluzione dei costi. Utilizza queste informazioni per decidere se procedere con la pipeline esistente o se riprogettarla per ridurre i costi.

Trovare i punti di errore

L'esecuzione di esperimenti può rilevare bug, potenziali punti di errore o potenziali problemi di configurazione ed efficienza. Puoi anche esaminare altre metriche della pipeline, ad esempio le seguenti:

  • Se la pipeline utilizza quasi tutta la memoria disponibile, potrebbero verificarsi eccezioni di esaurimento della memoria (OOM) con un carico più elevato o con record eccezionalmente grandi. Potresti dover eseguire il provisioning di più memoria per il job finale per evitare questi errori di OOM.
  • Se la pipeline presenta cali di throughput, esamina i relativi log per determinare il motivo. Potresti trovare un elemento bloccato o una parte del tuo set di dati con un rendimento particolarmente scarso. Puoi elaborare questi punti dati separatamente o puoi applicare un timeout durante l'elaborazione degli elementi. Per ulteriori informazioni, consulta la sezione Impostare un timeout per i record di costo elevato in questo documento.
  • Se la pipeline ha un rendimento molto peggiore in un'attività su Dataflow rispetto a quello locale, esamina la logica della pipeline per capire il motivo. Ad esempio, se ottieni lo stesso throughput con otto core su Dataflow e con un core localmente, il job potrebbe avere un collo di bottiglia a causa della contesa per una risorsa. Se il rendimento è peggiore del previsto, valuta una o più delle seguenti opzioni:
    • Esegui altri esperimenti con configurazioni hardware o software diverse.
    • Esegui test in locale con più core contemporaneamente.
    • Controlla il codice per trovare potenziali colli di bottiglia durante il deployment su larga scala.

Se la tua pipeline contiene suggerimenti per Dataflow, seguili per migliorare il rendimento.

Utilizzare le code di messaggi inutilizzati per gestire dati errati imprevisti

Le pipeline spesso riescono con la maggior parte degli elementi di input, ma non con un piccolo sottoinsieme di input. Potresti non rilevare questo problema quando esegui piccoli esperimenti, perché questi testano solo un sottoinsieme degli input. Per impostazione predefinita, Dataflow esegue nuovamente queste attività non riuscite quattro volte in modalità batch e un numero illimitato di volte in modalità di streaming. In modalità batch, dopo aver raggiunto il limite di tentativi, l'intero job non va a buon fine. In modalità di streaming, può bloccarsi a tempo indeterminato.

In molti job, puoi escludere questi elementi con errori dalla pipeline e completare il resto del job utilizzando una coda di messaggi inutilizzati (coda di messaggi non elaborati). La coda delle email inutilizzate inoltra i record non riusciti a un output PCollection separato, che puoi gestire separatamente dall'output principale. Questa configurazione ti consente di progettare un criterio per questi record. Ad esempio, puoi scriverli manualmente in Pub/Sub, ispezionarli e ripulire i dati, quindi elaborare nuovamente i record.

Molte trasformazioni Apache Beam includono il supporto integrato per le code di messaggi non recapitati. In Java, puoi accedervi con un oggetto ErrorHandler. In Python, puoi accedervi utilizzando il metodo with_exception_handling. Alcune trasformazioni hanno modi personalizzati per definire le code di messaggi inutilizzati, che puoi consultare nella documentazione della trasformazione. Per ulteriori informazioni, consulta Utilizzare le code dead letter per la gestione degli errori.

Per determinare se il tuo job soddisfa i criteri per una coda delle email inutilizzate, consulta la sezione Limitazioni di questo documento.

Limitazioni della coda dei messaggi non recapitabili

Nei seguenti scenari, una coda delle email inutilizzate potrebbe non essere utile:

  • Errori nel ciclo di vita di worker completi o di DoFn. Se l'elaborazione non va a buon fine per l'intero worker o bundle, una coda di messaggi inutilizzati non può rilevare l'errore. Ad esempio, se la pipeline rileva un'eccezione di esaurimento della memoria (OOM), tutte le attività attive sulla VM non vanno a buon fine e viene eseguito un nuovo tentativo, senza inviare nulla alla coda delle email inutilizzate.
  • Combinazioni o altre aggregazioni. Se la pipeline esegue calcoli che richiedono che tutti gli elementi di input siano presenti ed elaborati come parte del risultato, utilizza con cautela una coda di messaggi inutilizzati prima di questo passaggio. L'utilizzo di una coda di messaggi inutilizzati esclude parte dei dati di input dal risultato. L'aggiunta di una coda di messaggi inutilizzati potrebbe comportare una minore correttezza a fronte di una maggiore tolleranza agli errori.
  • Errori nel percorso della coda dei messaggi non recapitabili. Se un elemento non va a buon fine durante l'invio alla destinazione della coda delle email inutilizzate, l'intera pipeline può non andare a buon fine. Per evitare questo errore, mantieni la logica della coda delle email inutilizzate il più semplice possibile. Puoi aggiungere un passaggio di attesa (vedi wait class) per assicurarti che l'input principale venga completato prima di scrivere gli elementi della coda delle email inutilizzate. Questa configurazione potrebbe ridurre le prestazioni e ritardare gli indicatori di errore della pipeline.
  • Elementi parzialmente trasformati. Se inserisci una coda delle email inutilizzate nel corso della pipeline, la coda potrebbe restituire l'elemento parzialmente trasformato e non avere accesso all'elemento originale. Di conseguenza, non puoi pulire l'elemento e rieseguire la pipeline al suo interno. Potresti invece dover applicare una logica diversa per correlare l'output nella coda delle email inutilizzate all'elemento originale oppure potresti dover interpretare ed elaborare l'elemento trasformato parzialmente. Potrebbero anche verificarsi risultati incoerenti. Ad esempio, se gli elementi vengono inviati lungo due rami di una pipeline e ogni ramo invia elementi che causano eccezioni a una coda di messaggi inutilizzati, un singolo elemento di input potrebbe essere inviato in uno, nell'altro, in entrambi o in nessuno dei rami.

Impostare un timeout per i record dispendiosi

Le pipeline potrebbero smettere di rispondere durante l'elaborazione di un piccolo sottoinsieme di elementi più costosi o che raggiungono una limitazione che causa la mancata risposta, ad esempio un deadlock. Per attenuare questo problema, alcune trasformazioni ti consentono di impostare un timeout e di segnalare come non riusciti gli elementi che hanno superato il timeout in tutti i DoFn di codice utente che riscontrano questo problema. Ad esempio, puoi utilizzare il metodo with_exception_handling di Python. Quando utilizzi i timeout con una coda di messaggi inutilizzati, la pipeline può continuare a elaborare gli elementi integri e a progredire, nonché a rielaborare separatamente gli elementi di costo elevato. Questa configurazione può comportare un costo in termini di prestazioni.

Per determinare quali operazioni DoFn potrebbero richiedere un timeout, esegui piccoli esperimenti prima di lanciare la pipeline completa.

Abilita la scalabilità automatica verticale

Se non sai con certezza quanta memoria è necessaria per il tuo job o ritieni che il job rischi di esaurire la memoria, abilita la scalabilità automatica verticale. Questa funzionalità consente di evitare errori di OOM quando le pipeline vengono eseguite su larga scala o quando incontrano elementi eccezionalmente grandi.

Poiché la scalabilità automatica verticale potrebbe aumentare il costo del tuo job e non impedisce tutti gli arresti anomali per memoria esaurita, devi comunque risolvere i problemi di consumo eccessivo di memoria. La scalabilità automatica verticale richiede anche Dataflow Prime, che presenta limitazioni aggiuntive e un modello di fatturazione diverso.

Soluzioni alternative per le pipeline soggette a errori

Alcune pipeline sono particolarmente soggette a errori. Sebbene sia meglio risolvere la causa di questi errori, per ridurre il costo degli errori, valuta le seguenti opzioni.

Materializzare i risultati intermedi

Le pipeline potrebbero avere una o più trasformazioni particolarmente costose che dominano il tempo di esecuzione della pipeline. Gli errori della pipeline dopo questa trasformazione possono essere particolarmente dannosi, perché tutto il lavoro già completato viene perso. Per evitare questo scenario, ti consigliamo di scrivere PCollections intermedi generati da passaggi costosi in un sink come Cloud Storage. Questa configurazione riduce il costo di un errore. Devi valutare questo vantaggio rispetto al costo dell'esecuzione della scrittura aggiuntiva. Puoi utilizzare questo risultato materializzato in uno dei seguenti modi:

  1. Dividi la pipeline originale in due pipeline: una che scrive il risultato intermedio e una che lo legge.
  2. Solo in caso di errore della pipeline, leggi e appiattisce i risultati sia dall'origine originale sia dalla raccolta intermedia materializzata.

Per assicurarti che queste materializzazioni vengano scritte prima di un'ulteriore elaborazione, aggiungi un passaggio di attesa (vedi wait class) prima di eventuali passaggi di elaborazione successivi.