Gestire i rischi di scalabilità

L'infrastruttura di Google è progettata per operare in modo elastico su larga scala: la maggior parte dei livelli può adattarsi all'aumento della domanda di traffico fino a una scala enorme. Un pattern di progettazione di base che lo rende possibile è costituito dai livelli adattivi, ovvero componenti dell'infrastruttura che riallocano dinamicamente il carico in base ai pattern di traffico. Tuttavia, questo adattamento richiede tempo. Poiché Cloud Tasks consente di inviare volumi molto elevati di traffico, espone a rischi di produzione in situazioni in cui il traffico può aumentare più velocemente di quanto l'infrastruttura possa adattarsi.

Panoramica

Questo documento fornisce linee guida sulle best practice per mantenere elevate le prestazioni di Cloud Tasks nelle code con traffico elevato. Una coda con un elevato TPS è una coda con almeno 500 attività create o inviate al secondo (TPS). Un gruppo di code ad alto TPS è un insieme contiguo di coda, ad esempio [queue0001, queue0002, …, queue0099], che hanno almeno 2000 attività create o inviate in totale. I TPS storici di una coda o di un gruppo di code sono visualizzabili utilizzando le metriche di Stackdriver, api/request_count per le operazioni "CreateTask" e queue/task_attempt_count per i tentativi di attività. Le code e i gruppi di code con traffico elevato sono soggetti a due diverse classi generali di errori:

Il sovracaricamento della coda si verifica quando la creazione e l'invio delle attività a una singola coda o a un gruppo di code avviene più rapidamente di quanto l'infrastruttura della coda sia in grado di adattarsi. Analogamente, il sovra carico del target si verifica quando la frequenza di invio delle attività provoca picchi di traffico nell'infrastruttura di destinazione a valle. In entrambi i casi, consigliamo di seguire uno schema 500/50/5: quando esegui la scalabilità oltre 500 TPS, aumenta il traffico non più del 50% ogni 5 minuti. Questo documento esamina diversi scenari che possono comportare rischi di scalabilità e fornisce esempi di come applicare questo pattern.

Sovraccarico della coda

Le code o i gruppi di code possono essere sovraccaricati ogni volta che il traffico aumenta improvvisamente. Di conseguenza, queste code possono presentare:

  • Aumento della latenza nella creazione delle attività
  • Aumento del tasso di errori di creazione delle attività
  • Riduzione della frequenza di invio

Per difenderti da questo problema, ti consigliamo di stabilire controlli in qualsiasi situazione in cui la frequenza di creazione o invio di una coda o di un gruppo di code può aumentare improvvisamente. Consigliamo un massimo di 500 operazioni al secondo per una coda o un gruppo di code non attive, quindi di aumentare il traffico del 50% ogni 5 minuti. In teoria, puoi aumentare fino a 740.000 operazioni al secondo dopo 90 minuti utilizzando questa pianificazione di aumento. Ciò può accadere in diverse circostanze.

Ad esempio:

  • Lancio di nuove funzionalità che fanno un uso intensivo di Cloud Tasks
  • Spostare il traffico tra le code
  • Riequilibrio del traffico tra più o meno code
  • Eseguire job batch che inseriscono un numero elevato di attività

In questi casi e in altri, segui il pattern 500/50/5.

Utilizzo della suddivisione del traffico di App Engine

Se le attività vengono create da un'app App Engine, puoi utilizzare la suddivisione del traffico di App Engine (standard/flessibile) per attenuare gli aumenti di traffico. Suddividendo il traffico tra le versioni (Standard/Flex), le richieste che devono essere gestite in base alla frequenza possono essere avviate nel tempo per proteggere la salute della coda. Ad esempio, prendiamo il caso dell'aumento del traffico verso un gruppo di code appena ampliato: supponiamo che [queue0000, queue0199] sia una sequenza di code con TPS elevato che ricevono 100.000 creazioni TPS in totale in picco.

Sia [queue0200, queue0399] una sequenza di nuove code. Dopo che tutto il traffico è stato spostato, il numero di code nella sequenza è raddoppiato e il nuovo intervallo di code riceve il 50% del traffico totale della sequenza.

Quando esegui il deployment della versione che aumenta il numero di code, incrementa gradualmente il traffico verso la nuova versione e, di conseguenza, verso le nuove code utilizzando la suddivisione del traffico:

  • Inizia a spostare l'1% del traffico verso la nuova release. Ad esempio,il 50% dell'1% di 100.000 TPS genera 500 TPS per l'insieme di nuove code.
  • Ogni 5 minuti, aumenta del 50% il traffico inviato alla nuova release, come indicato nella tabella seguente:
Minuti dall'inizio del deployment % del traffico totale passato alla nuova versione % del traffico totale verso le nuove code % del traffico totale verso le vecchie code
0 1,0 0,5 99,5
5 1,5 0,75 99,25
10 2.3 1,15 98,85
15 3.4 1,7 98,3
20 5.1 2,55 97,45
25 7,6 3,8 96,2
30 11.4 5,7 94,3
35 17,1 8,55 91,45
40 25,6 12,8 87,2
45 38,4 19,2 80,8
50 57,7 28,85 71,15
55 86,5 43,25 56,75
60 100 50 50

Picchi di traffico dovuti al rilascio

Quando viene lanciata una release che aumenta notevolmente il traffico verso una coda o un gruppo di code, l'implementazione graduale è ancora un meccanismo importante per attenuare gli aumenti. Implementa gradualmente le tue istanze in modo che il lancio iniziale non superi le 500 operazioni totali nelle nuove code, aumentando al massimo del 50% ogni 5 minuti.

Nuove code o gruppi di code con TPS elevato

Le code appena create sono particolarmente vulnerabili. I gruppi di code, ad esempio [queue0000, queue0001, …, queue0199], sono sensibili quanto le singole code durante le fasi iniziali di implementazione. Per queste code, l'implementazione graduale è una strategia importante. Lancia servizi nuovi o aggiornati, che creano code o gruppi di code con TPS elevato, in modo che il carico iniziale sia inferiore a 500 TPS e gli aumenti del 50% o meno siano pianificati a distanza di almeno 5 minuti.

Gruppi di coda appena espansi

Quando aumenti la capacità totale di un gruppo di code, ad esempio espandi [queue0000-queue0199 a queue0000-queue0399], segui il pattern 500/50/5. È importante notare che, per le procedure di implementazione, i nuovi gruppi di code non si comportano in modo diverso dalle singole code. Applica il pattern 500/50/5 al nuovo gruppo nel suo complesso, non solo alle singole code all'interno del gruppo. Per queste espansioni di gruppi di code, l'implementazione graduale è di nuovo una strategia importante. Se la sorgente del traffico è App Engine, puoi utilizzare la suddivisione del traffico (consulta Picchi di traffico dovuti al lancio). Quando esegui la migrazione del servizio per aggiungere attività al numero aumentato di code, esegui gradualmente il deployment delle istanze in modo che il lancio iniziale non superi 500 operazioni totali nelle nuove code, aumentando non più del 50% ogni 5 minuti.

Espansione del gruppo di code di emergenza

A volte potresti voler espandere un gruppo di coda esistente, ad esempio perché si prevede che le attività vengano aggiunte al gruppo di coda più velocemente di quanto il gruppo possa inviarle. Se i nomi delle nuove code sono distribuiti uniformemente tra i nomi delle code esistenti quando sono ordinati in ordine alfabetico, il traffico può essere inviato immediatamente a queste code, a condizione che non esistano più del 50% di nuove code interlacciate e che il traffico verso ogni coda sia inferiore a 500 TPS. Questo metodo è un'alternativa all'utilizzo della suddivisione del traffico e dell'implementazione graduale descritti nelle sezioni precedenti.

Questo tipo di denominazione interlacciata può essere ottenuto aggiungendo un suffisso alle code che terminano con numeri pari. Ad esempio, se hai 200 code di coda esistenti [queue0000-queue0199] e vuoi creare 100 nuove code, scegli [queue0000a, queue0002a, queue0004a, …, queue0198a] come nomi delle nuove code anziché [queue0200-queue0299].

Se hai bisogno di un ulteriore aumento, puoi comunque interlacciare fino al 50% in più di code ogni 5 minuti.

Coda di attività collettive/su larga scala

Quando è necessario aggiungere un numero elevato di attività, ad esempio milioni o miliardi, può essere utile un pattern di doppia associazione. Anziché creare attività da un singolo job, utilizza una coda di iniettori. Ogni attività aggiunta alla coda dell'iniettore si espande e aggiunge 100 attività alla coda o al gruppo di code desiderato. La coda dell'iniettore può essere accelerata nel tempo, ad esempio inizia con 5 TPS, quindi aumenta del 50% ogni 5 minuti.

Attività con nome

Quando crei una nuova attività, Cloud Tasks assegna un nome univoco per impostazione predefinita. Puoi assegnare il tuo nome a un'attività utilizzando il parametro name. Tuttavia, questo introduce un sovraccarico delle prestazioni significativo, con conseguente aumento delle latenze e potenzialmente dei tassi di errore associati alle attività con nome. Questi costi possono aumentare notevolmente se le attività vengono denominate in sequenza, ad esempio con i timestamp. Pertanto, se assegni i tuoi nomi, ti consigliamo di utilizzare un prefisso ben distribuito per i nomi delle attività, ad esempio un hash dei contenuti. Consulta la documentazione per ulteriori dettagli sull'assegnazione del nome a un'attività.

Eccesso di target

Cloud Tasks può sovraccaricare altri servizi in uso, come App Engine, Datastore e l'utilizzo della rete, se le spedizioni da una coda aumentano notevolmente in un breve periodo di tempo. Se si è accumulato un backlog di attività, la rimozione della messa in pausa di queste code può potenzialmente sovraccaricare questi servizi. La difesa consigliata è lo stesso pattern 500/50/5 suggerito per il sovraccarico della coda: se una coda invia più di 500 TPS, aumenta il traffico attivato da una coda non più del 50% ogni 5 minuti. Utilizza le metriche di Stackdriver per monitorare in modo proattivo gli aumenti del traffico. Gli avvisi Stackdriver possono essere utilizzati per rilevare situazioni potenzialmente pericolose.

Riattivare o riprendere le code con TPS elevato

Quando una coda o una serie di code viene riattivata o viene rimossa la messa in pausa, le code riprendono i dispiegamenti. Se la coda contiene molte attività, la frequenza di invio della coda appena attivata potrebbe aumentare notevolmente da 0 TPS alla piena capacità della coda. Per eseguire il ramp-up, scagliona le riprese della coda o controlla le frequenze di invio della coda utilizzando maxDispatchesPerSecond di Cloud Tasks.

Attività pianificate collettive

Anche un numero elevato di attività pianificate per l'invio contemporaneamente può comportare il rischio di un sovraccarico del target. Se devi avviare un numero elevato di attività contemporaneamente, ti consigliamo di utilizzare i controlli della frequenza della coda per aumentare gradualmente la frequenza di invio o di aumentare esplicitamente la capacità target in anticipo.

Aumento del fan-out

Quando aggiorni i servizi eseguiti tramite Cloud Tasks, l'aumento del numero di chiamate remote può creare rischi di produzione. Ad esempio, supponiamo che le attività in una coda con un elevato TPS chiamino l'handler /task-foo. Una nuova release potrebbe aumentare notevolmente il costo della chiamata a /task-foo se, ad esempio, aggiunge al gestore diverse chiamate a Datastore costose. Il risultato netto di una tale release sarebbe un aumento massiccio del traffico di Datastore immediatamente correlato alle variazioni del traffico degli utenti. Utilizza l'implementazione graduale o la suddivisione del traffico per gestire l'aumento.

Nuovi tentativi

Il codice può riprovare in caso di errore durante le chiamate all'API Cloud Tasks. Tuttavia, quando una percentuale significativa di richieste non va a buon fine con errori lato server, un tasso elevato di tentativi può sovraccaricare ulteriormente le code e causarne un recupero più lento. Pertanto, ti consigliamo di limitare la quantità di traffico in uscita se il tuo client rileva che una percentuale significativa di richieste non va a buon fine con errori lato server, ad esempio utilizzando l'algoritmo di throttling adattivo descritto nel capitolo Gestione del sovraccarico del libro Site Reliability Engineering. Le librerie client gRPC di Google implementano una variazione di questo algoritmo.