Cohérence lecture après écriture sur un cluster Galera
Un cluster Galera ou Percona XtraDB certifie une écriture au commit et l'applique sur les autres nœuds un instant plus tard, et une lecture routée dans cet intervalle voit le passé.
Dans cet article 6 sections
- Sur un cluster à certification, commit signifie que l'écriture a été certifiée sur chaque nœud, pas que chaque nœud l'a appliquée. L'application arrive un instant plus tard, typiquement de quelques dizaines à quelques centaines de millisecondes.
- Une lecture équilibrée vers un nœud qui n'a pas encore appliqué l'écriture renvoie l'ancienne ligne. Dans un CMS, cela se lit comme une modification perdue : l'éditeur enregistre, rafraîchit, voit l'ancien texte et enregistre de nouveau par-dessus la version plus récente. Aucune erreur n'est levée nulle part.
- wsrep_sync_wait ferme l'intervalle en faisant attendre une lecture jusqu'à ce que son nœud ait rattrapé le retard. Cela coûte le délai d'application sur les lectures qui suivent une écriture, donc on le limite aux sessions qui en ont besoin et on le laisse désactivé sur les pages servies par un cache en bordure.
Commit signifie certifié, pas appliqué
Un cluster Galera exécute une transaction sur un nœud. Au commit, ce nœud diffuse le write set au cluster, chaque nœud le certifie face aux write sets déjà en vol, et le commit revient au client. À partir de cet instant, l'écriture est durable sur le cluster et ordonnée identiquement partout.
Ce qui n'a pas encore eu lieu, c'est l'application. Chaque nœud prend le write set certifié et l'applique à sa propre copie des données dans ses propres threads d'application, légèrement après le retour du commit. Deux horloges tournent : la certification est à l'échelle du cluster et fait partie du commit, l'application est locale et vient après. Sur un cluster sain, la distance entre les deux est de quelques dizaines à quelques centaines de millisecondes. Elle grandit pendant les rafales d'écritures, les grosses transactions et les changements de schéma, et elle se voit comme un wsrep_local_recv_queue non nul sur le nœud en retard.
Où va la lecture
Un cluster à lectures équilibrées envoie les lectures là où le proxy le décide. Une requête écrit sur le nœud A ; la requête suivante du même utilisateur, quelques centaines de millisecondes plus tard, lit sur le nœud B. Si le nœud B a reçu mais pas encore appliqué le write set, il répond avec la ligne telle qu'elle était avant l'écriture. Du point de vue du nœud B, la réponse est correcte : c'est son état validé. Rien n'est journalisé, rien n'alerte, et le moniteur de retard reste silencieux parce qu'il est conçu pour attraper un retard soutenu, pas les cent premières millisecondes après une transaction.
C'est pourquoi le symptôme paraît aléatoire. Il dépend du nœud choisi par le répartiteur, de la vitesse à laquelle l'utilisateur est revenu, et de la charge des threads d'application à cet instant.
À quoi cela ressemble dans une application
Le motif est une écriture immédiatement suivie d'une lecture du même utilisateur, atterrissant sur un autre nœud. Dans un CMS, c'est le paragraphe disparu : enregistrer, rafraîchir, ancien texte, retaper, enregistrer, et le second enregistrement écrase le premier. Dans une boutique, c'est une commande qui n'existe pas sur la page de confirmation. Dans un flux d'authentification, c'est un jeton qui vient d'être émis et qui est introuvable.
Deux autres mécanismes produisent une histoire similaire et méritent d'être écartés d'abord. Le cache arrière-avant d'un navigateur peut restaurer une copie périmée d'un formulaire, et un nouvel enregistrement écrit alors cette copie périmée. Un plugin peut faire une lecture-modification-écriture sur la même ligne depuis deux requêtes. Les deux sont réels et aucun des deux n'est le cluster. Une boucle qui écrit un horodatage sur un nœud et le relit sur les autres les départage en une minute.
Le réglage : wsrep_sync_wait
wsrep_sync_wait est un masque de bits. Avec le bit 1 positionné, un SELECT attend que le nœud ait appliqué chaque write set reçu jusqu'à cet instant avant de s'exécuter. La lecture est alors garantie de voir tout ce qui a été validé n'importe où dans le cluster avant son démarrage. Il peut être défini pour une session, ce qui est la bonne portée pour un chemin d'édition ou de paiement, ou globalement quand toute la charge est sensible à la lecture après écriture.
-- 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'alternative est le routage : envoyer les lectures qui suivent une écriture vers le nœud qui a écrit, avec des règles de requêtes du proxy ou dans l'application. Cela fonctionne pour les chemins couverts. Un seul chemin de code que personne n'a routé rouvre l'intervalle, et c'est pourquoi le réglage est préféré là où son coût peut être payé : il traite le mécanisme, pas un chemin à travers lui.
Ce que cela coûte et où le payer
Une lecture sous wsrep_sync_wait attend que le thread d'application local rattrape le retard, donc elle paie le délai d'application une fois par lecture qui suit une écriture. Sur un cluster sain, cela fait de quelques dizaines à quelques centaines de millisecondes. Les sessions connectées, les chemins d'administration et d'édition et le paiement sont là où ce coût achète quelque chose. Les pages anonymes servies depuis un cache en bordure ne touchent pas la base en cas de hit et ne sont pas affectées. Le réglage prend effet à l'instruction suivante et se révoque de la même manière, donc il peut être mesuré en place avec la vraie charge au lieu d'être estimé.
Comment vérifier votre propre cluster
Trois lectures tranchent. Si les lectures attendent du tout ; de combien un nœud est en retard pendant une rafale d'écritures ; et à quelle fréquence une lecture après écriture entre nœuds voit réellement le passé, à partir d'une boucle qui écrit sur un nœud et lit sur les autres.
SHOW GLOBAL VARIABLES LIKE 'wsrep_sync_wait'; -- 0 means reads do not wait
SHOW GLOBAL STATUS LIKE 'wsrep_local_recv_queue'; -- received, not yet appliedNous exploitons des clusters Percona XtraDB à lectures équilibrées derrière ProxySQL et HAProxy et pouvons répondre à ces trois questions pour tout cluster que nous opérons, y compris ce que le réglage coûte sur cette charge précise.
Réponses directes
La réplication Galera est-elle synchrone ?+
La certification l'est : l'écriture est certifiée sur chaque nœud avant le retour du commit. L'application sur chaque nœud a lieu un instant plus tard. Les deux moitiés comptent, et la seconde est celle où vivent les problèmes de lecture après écriture.
Pourquoi mon CMS perd-il des modifications sur un cluster ?+
Une lecture après un enregistrement a été routée vers un nœud qui avait reçu le write set mais ne l'avait pas encore appliqué. L'éditeur a vu l'ancien contenu, l'a retapé et a enregistré par-dessus la version plus récente. Aucune erreur n'est produite, et c'est pourquoi cela paraît intermittent.
Vider le cache aide-t-il ?+
En général, non. Le cache a stocké ce que la base a renvoyé, et pendant un instant la base a renvoyé le passé. Diagnostiquez le chemin de lecture. Le cache ici est porteur du symptôme, pas la cause.
Que coûte wsrep_sync_wait ?+
Le délai d'application, payé sur les lectures qui suivent une écriture, typiquement de quelques dizaines à quelques centaines de millisecondes sur un cluster sain. Les pages servies depuis un cache en bordure ne sont pas affectées. Limitez-le par session pour ne le payer que là où cela compte.
Vous exploitez un cluster à lectures équilibrées ?
Parlez à un ingénieur pour savoir si la lecture après écriture est activée et ce qu'elle coûte sur le vôtre.
Parler à un ingénieurVous êtes arrivé à la fin.
Mettez la lecture en pratique
Que faites-vous tourner ?
Parlez-nous de la charge de travail, du trafic et des exigences de reprise. Commencez par le configurateur de serveur, ou passez en revue les détails avec un ingénieur.
Écrit par les ingénieurs qui construisent et exploitent l'infrastructure qu'il décrit.Parler à un ingénieur
