pt-online-schema-change selettivo può causare blocchi su Galera
Un filtro WHERE selettivo in pt-online-schema-change può causare un blocco in produzione su un cluster basato su Galera del database relazionale open-source, secondo un dettagliato post pubblicato il 28 settembre 2026 da Corrado Pandiani. Il post descrive un intervento presso un cliente con una versione clusterizzata di questo database (spesso chiamata cluster Galera) in cui una modifica di schema online di routine che copiava solo le righe più recenti ha portato a un grave controllo di flusso di Galera, forte overhead e un cluster che ha dovuto essere riavviato dopo essere rimasto bloccato per decine di minuti. La causa principale non era la modifica di schema in sé, ma l'interazione tra il ridimensionamento adattivo automatico dei chunk, la copia altamente selettiva delle righe più recenti, la replica clusterizzata e il checkpointing di InnoDB.
Lo scenario inizia con una grande tabella contenente centinaia di milioni di righe. Invece di ricostruire l'intera tabella, il team voleva copiare solo un sottoinsieme di righe e aggiungere un nuovo indice. Il comando era simile a questo: pt-online-schema-change --alter "ADD INDEX idx_status(status)" --where "created_at >= NOW() - INTERVAL 30 DAY" D=test,t=events --execute --force. In parole povere, pt-online-schema-change (pt-osc) è uno strumento che modifica la struttura di una tabella mentre la tabella originale continua a servire il traffico, e l'opzione --where limita il lavoro alle righe la cui data created_at rientra negli ultimi 30 giorni. Questo approccio è comune quando i dati storici possono essere ignorati, solo i record attivi richiedono modifiche o una migrazione viene suddivisa in fasi per ridurre il tempo totale.
Normalmente, la fase di copia di pt-osc regola la dimensione di ogni chunk di righe per mantenere il lavoro vicino a un tempo di esecuzione configurato, con un valore predefinito di 0,5 secondi. Se i chunk vengono completati troppo rapidamente, lo strumento aumenta la dimensione del chunk successivo; se i chunk rallentano, la diminuisce. Questo ridimensionamento adattivo automatico dei chunk funziona bene su tabelle con densità di righe relativamente uniforme, perché ogni chunk contiene una miscela simile di righe da copiare e righe da saltare. Il problema si presenta quando la maggior parte delle righe scansionate viene scartata da una condizione --where selettiva. Nell'esempio, la tabella aveva 1 miliardo di righe: 990 milioni non corrispondevano al filtro e solo 10 milioni sì. Le righe corrispondenti si trovavano alla fine dell'indice clusterizzato, perché i valori di created_at aumentavano di pari passo con la chiave primaria e i confini della chiave primaria erano combinati con la condizione WHERE. Per molto tempo, pt-osc ha scansionato chunk in cui quasi ogni riga veniva rifiutata, rendendo l'operazione di copia estremamente veloce. L'algoritmo adattivo interpretava quella velocità come "i chunk sono troppo piccoli" e continuava ad aumentare la dimensione dei chunk. Quando la scansione ha raggiunto le prime righe corrispondenti, la dimensione dei chunk era cresciuta enormemente, quindi invece di copiare poche migliaia di righe, pt-osc poteva improvvisamente copiare centinaia di migliaia o addirittura un milione di righe in una singola transazione.
Quando quella transazione sovradimensionata colpisce un cluster Galera, il problema si sposta da una scansione silenziosa a una tempesta di replica. Galera è una tecnologia di replica sincrona che mantiene identici più nodi del database, e replica ogni chunk copiato come un write set, ovvero il gruppo di modifiche alle righe inviate agli altri nodi. Transazioni molto grandi producono write set molto grandi, lunghe fasi di certificazione (il processo in cui i nodi verificano che le scritture non siano in conflitto), code di apply aumentate, maggiore latenza di replica e, infine, l'attivazione del controllo di flusso. Il controllo di flusso è il meccanismo integrato di Galera per mettere in pausa le scritture su un nodo veloce quando gli altri nodi restano indietro. Una volta avviato, il throughput del cluster diminuisce drasticamente, i commit delle applicazioni iniziano ad attendere e, nei casi estremi, il cluster appare quasi congelato finché ogni nodo non ha completamente applicato la transazione sovradimensionata. Il post osserva che, ironicamente, l'algoritmo adattivo, progettato per ottimizzare il throughput, crea esattamente il modello di carico che Galera gradisce di meno.
Anche InnoDB, il motore di storage predefinito del database relazionale open-source, ne risente. Non appena i primi grandi chunk hanno iniziato l'elaborazione, il log degli errori del nodo writer ha mostrato un avviso del tipo "[Warning] [MY-014084] [InnoDB] Threads are unable to reserve space in redo log ... Consider increasing innodb_redo_log_capacity." Il redo log è una registrazione delle modifiche utilizzata per il ripristino dopo un crash e, quando arriva una transazione molto grande, i thread di scrittura non riescono a trovare abbastanza spazio libero perché il consumatore log_checkpointer è in ritardo. In pratica, questo costringe il server ad attivare checkpoint più frequenti e su scala più ampia, aggiungendo overhead. Dopo che Galera ha replicato il write set sugli altri nodi, lo stesso tipo di messaggio è apparso su quei nodi durante la fase di apply. I sintomi tipici includono un rapido aumento della percentuale di controllo di flusso, una crescente wsrep_local_recv_queue (la coda di scritture in attesa di essere applicate localmente), elevato ritardo di replica, commit delle applicazioni in attesa, maggiore utilizzo della CPU, grandi picchi nel tempo di applicazione delle transazioni e checkpoint InnoDB più frequenti su tutti i nodi. La modifica di schema può comunque terminare, ma il traffico di produzione può essere gravemente compromesso; nel caso peggiore descritto, il cluster si è bloccato e ha dovuto essere riavviato.
La prima e più efficace correzione proposta è disabilitare il ridimensionamento adattivo dei chunk ogni volta che viene utilizzata una clausola --where selettiva. Invece di aumentare continuamente le dimensioni dei chunk, mantenerle fisse per tutta l'operazione includendo --chunk-time = 0 e --max-flow-ctl = 0 nel comando. Impostare --chunk-time a zero impedisce a pt-osc di regolare automaticamente la dimensione dei chunk, quindi i tempi di esecuzione delle query variano ma le dimensioni dei chunk no. Un'alternativa è specificare esplicitamente un valore per --chunk-size e omettere --chunk-time. Con l'algoritmo adattivo bypassato, l'esecuzione dei chunk diventa prevedibile, la dimensione delle transazioni rimane limitata e Galera non riceve mai write set inaspettatamente enormi. La modifica di schema complessiva potrebbe richiedere leggermente più tempo, ma la stabilità del cluster migliora notevolmente, il che di solito è il compromesso preferibile in produzione.
Un altro parametro da considerare è --max-flow-ctl. Con --max-flow-ctl=PCT, pt-osc controlla periodicamente wsrep_flow_control_paused, la percentuale di tempo che il nodo ha trascorso in pausa per il controllo di flusso, e mette automaticamente in pausa la copia delle righe ogni volta che tale percentuale supera la soglia, riprendendo una volta che il cluster scende al di sotto di essa. Impostare --max-flow-ctl a zero (o molto vicino a zero, come 1) fa sì che pt-osc smetta di copiare le righe al primo segnale che un nodo è entrato in controllo di flusso, invece di tollerare un po' di tempo di pausa prima di fare marcia indietro. L'analisi definisce questa la posizione più sicura possibile per un cluster di produzione, specialmente uno che mostra già avvisi di ritardo del checkpointer, perché qualsiasi pressione di scrittura aggiuntiva dalla copia a chunk della migrazione potrebbe far precipitare un nodo già in difficoltà in un blocco molto più grande. La soglia quasi zero scambia la velocità di migrazione con la sicurezza, consentendo allo strumento di adattarsi alla capacità in tempo reale del cluster e di avanzare solo quando non c'è davvero contesa. Per carichi di lavoro di produzione critici e sensibili alla latenza, una modifica di schema più lenta ma sicura è preferibile a una veloce che rischia un blocco delle scritture a livello di cluster.
Una leva correlata è --chunk-index. Per impostazione predefinita, pt-online-schema-change suddivide la tabella in chunk utilizzando la chiave primaria o l'indice univoco più appropriato, motivo per cui i confini dei chunk nel caso del cliente seguivano la stessa colonna monotonicamente crescente di created_at e producevano la lunga sequenza di chunk quasi vuoti. Se la tabella ha un indice secondario il cui ordinamento non è correlato con created_at, forzare lo strumento a suddividere in chunk su quell'indice con --chunk-index=idx_name può distribuire le righe corrispondenti e non corrispondenti in modo più uniforme tra i chunk, così l'algoritmo adattivo non vede mai la lunga sequenza a bassa densità che porta le dimensioni dei chunk a livelli pericolosi. Tuttavia, il post avverte che questo non sostituisce --chunk-time=0 e --max-flow-ctl, perché un indice di chunking scelto male può peggiorare le prestazioni e poche tabelle hanno un indice conveniente che sia sia abbastanza selettivo da essere usato per il chunking sia non correlato con la condizione --where. In pratica, --chunk-index è un'ottimizzazione secondaria e specifica per la tabella.
Una terza opzione è evitare del tutto la clausola --where e dividere l'attività in due fasi. Prima eseguire pt-archiver per eliminare le righe non più necessarie, poi eseguire pt-online-schema-change per modificare la tabella. Questa procedura in due fasi evita qualsiasi rischio derivante dalla distribuzione non uniforme delle righe corrispondenti. Il post conclude che pt-online-schema-change rimane uno dei migliori strumenti per le migrazioni di schema online, ma quando si utilizza --where per copiare solo un piccolo sottoinsieme di righe su un cluster basato su Galera del database relazionale open-source, il ridimensionamento adattivo dei chunk può generare involontariamente transazioni sovradimensionate che attivano un controllo di flusso eccessivo di Galera e bloccano temporaneamente il cluster. Mantenere fisse le dimensioni dei chunk elimina quel circolo vizioso, producendo un comportamento di replica più fluido e riducendo il rischio operativo. Sono disponibili anche altre correzioni e la lezione di fondo è che a volte la prevedibilità è un'ottimizzazione migliore dell'aggressività. Per i team che preferiscono esternalizzare questo onere operativo, i provider di database gestiti come AEU Data (aeu-data.com) offrono monitoraggio, backup automatici e configurazione ad alta disponibilità che possono aiutare gli operatori a rilevare e recuperare dal tipo di blocco della replica qui descritto.