Coerenza read-after-write su un cluster Galera
Un cluster Galera o Percona XtraDB certifica una scrittura al commit e la applica sugli altri nodi un attimo dopo, e una lettura instradata in quell'intervallo vede il passato.
In questo articolo 6 sezioni
- Su un cluster basato su certificazione, commit significa che la scrittura è stata certificata su ogni nodo, non che ogni nodo l'ha applicata. L'applicazione avviene un attimo dopo, tipicamente da decine a poche centinaia di millisecondi.
- Una lettura bilanciata su un nodo che non ha ancora applicato la scrittura restituisce la riga vecchia. In un CMS si legge come una modifica persa: l'editor salva, ricarica, vede il testo vecchio e salva di nuovo sopra la versione più recente. Nessun errore viene sollevato da nessuna parte.
- wsrep_sync_wait chiude l'intervallo facendo attendere una lettura finché il suo nodo non si è messo al passo. Costa il ritardo di applicazione sulle letture che seguono una scrittura, quindi lo si limita alle sessioni che ne hanno bisogno e lo si lascia spento sulle pagine servite da una cache edge.
Commit significa certificato, non applicato
Un cluster Galera esegue una transazione su un nodo. Al commit, quel nodo trasmette il write set al cluster, ogni nodo lo certifica rispetto ai write set già in volo, e il commit torna al client. Da quel momento la scrittura è durevole sul cluster e ordinata in modo identico ovunque.
Ciò che non è ancora avvenuto è l'applicazione. Ogni nodo prende il write set certificato e lo applica alla propria copia dei dati nei propri applier thread, poco dopo che il commit è tornato. Girano due orologi: la certificazione riguarda tutto il cluster ed è parte del commit, l'applicazione è locale e viene dopo. Su un cluster sano la distanza tra i due è di decine fino a poche centinaia di millisecondi. Cresce durante le raffiche di scritture, le transazioni grandi e le modifiche di schema, ed è visibile come un wsrep_local_recv_queue diverso da zero sul nodo che è indietro.
Dove va la lettura
Un cluster con letture bilanciate manda le letture dove decide il proxy. Una richiesta scrive sul nodo A; la richiesta successiva dello stesso utente, qualche centinaio di millisecondi dopo, legge sul nodo B. Se il nodo B ha ricevuto ma non ancora applicato il write set, risponde con la riga com'era prima della scrittura. Dal punto di vista del nodo B la risposta è corretta: quello è il suo stato committed. Nulla viene registrato, nulla allarma, e il monitor del ritardo resta zitto perché è costruito per cogliere un ritardo sostenuto, non le prime cento millisecondi dopo una singola transazione.
Ecco perché il sintomo sembra casuale. Dipende da quale nodo ha scelto il balancer, da quanto in fretta l'utente è tornato e da quanto erano occupati gli applier in quell'istante.
Come appare in un'applicazione
Lo schema è una scrittura seguita immediatamente da una lettura dello stesso utente, che atterra su un nodo diverso. In un CMS è il paragrafo sparito: salva, ricarica, testo vecchio, riscrivi, salva, e il secondo salvataggio sovrascrive il primo. In un negozio è un ordine che non esiste nella pagina di conferma. In un flusso di autenticazione è un token appena emesso che non viene trovato.
Altri due meccanismi producono una storia simile e vale la pena escluderli per primi. La back-forward cache di un browser può ripristinare una copia stantia di un form, e un nuovo salvataggio scrive poi quella copia stantia. Un plugin può fare un read-modify-write sulla stessa riga da due richieste. Entrambi sono reali e nessuno dei due è il cluster. Un loop che scrive un timestamp su un nodo e lo rilegge sugli altri li separa in un minuto.
L'impostazione: wsrep_sync_wait
wsrep_sync_wait è una maschera di bit. Con il bit 1 impostato, una SELECT attende che il nodo abbia applicato ogni write set ricevuto fino a quel momento prima di eseguire. La lettura ha allora la garanzia di vedere qualunque cosa sia stata committata ovunque nel cluster prima che iniziasse. Può essere impostata per una singola sessione, che è l'ambito giusto per un percorso di editing o di checkout, o globalmente quando l'intero carico di lavoro è sensibile al read-after-write.
-- for one session: the request path that writes and then reads
SET SESSION wsrep_sync_wait = 1;
-- for the whole node: my.cnf, under [mysqld]
wsrep_sync_wait = 1L'alternativa è il routing: mandare le letture che seguono una scrittura al nodo che ha scritto, con regole di query del proxy o nell'applicazione. Funziona per i percorsi che copre. Un solo percorso di codice che nessuno ha instradato riapre l'intervallo, ed è per questo che l'impostazione è preferita dove il costo si può pagare: affronta il meccanismo, non un singolo percorso attraverso di esso.
Cosa costa e dove pagarlo
Una lettura sotto wsrep_sync_wait attende che l'applier locale si metta al passo, quindi paga il ritardo di applicazione una volta per ogni lettura che segue una scrittura. Su un cluster sano sono decine fino a poche centinaia di millisecondi. Le sessioni autenticate, i percorsi di amministrazione e di editing e il checkout sono dove quel costo compra qualcosa. Le pagine anonime servite da una cache edge non toccano il database su un hit e non ne risentono. L'impostazione ha effetto dall'istruzione successiva e si annulla allo stesso modo, quindi può essere misurata sul posto con il carico reale invece che stimata.
Come controllare il vostro cluster
Tre letture chiudono la questione. Se le letture attendono o no; quanto resta indietro un nodo durante una raffica di scritture; e quanto spesso una lettura read-after-write tra nodi vede davvero il passato, da un loop che scrive su un nodo e legge sugli altri.
SHOW GLOBAL VARIABLES LIKE 'wsrep_sync_wait'; -- 0 means reads do not wait
SHOW GLOBAL STATUS LIKE 'wsrep_local_recv_queue'; -- received, not yet appliedGestiamo cluster Percona XtraDB con letture bilanciate dietro ProxySQL e HAProxy e possiamo rispondere a quelle tre domande per qualsiasi cluster che operiamo, compreso cosa costa l'impostazione su quel carico di lavoro specifico.
Risposte dirette
La replica Galera è sincrona?+
La certificazione lo è: la scrittura viene certificata su ogni nodo prima che il commit torni. L'applicazione su ogni nodo avviene un attimo dopo. Contano entrambe le metà, e la seconda è dove vivono i problemi di read-after-write.
Perché il mio CMS perde le modifiche su un cluster?+
Una lettura dopo un salvataggio è stata instradata su un nodo che aveva ricevuto il write set ma non l'aveva ancora applicato. L'editor ha visto il contenuto vecchio, l'ha riscritto e ha salvato sopra la versione più recente. Non viene prodotto alcun errore, ed è per questo che sembra intermittente.
Svuotare la cache aiuta?+
Di solito no. La cache ha conservato ciò che il database ha restituito, e per un attimo il database ha restituito il passato. Diagnosticate il percorso di lettura. Qui la cache è un portatore del sintomo, non la causa.
Cosa costa wsrep_sync_wait?+
Il ritardo di applicazione, pagato sulle letture che seguono una scrittura, tipicamente decine fino a poche centinaia di millisecondi su un cluster sano. Le pagine servite da una cache edge non ne risentono. Limitatelo per sessione per pagarlo solo dove conta.
Gestite un cluster con letture bilanciate?
Parlate con un ingegnere per sapere se il read-after-write è attivo e cosa costa sul vostro.
Parla con un ingegnere (shell cta.talkToEngineer)Siete arrivati alla fine.
Mettete la lettura al lavoro
Cosa fate girare?
Raccontateci il carico di lavoro, il traffico e i requisiti di ripristino. Partite dal configuratore di server, oppure discutete i dettagli con un ingegnere.
Scritto dagli ingegneri che costruiscono e gestiscono l'infrastruttura che descrive.Parla con un ingegnere
