Consistencia read-after-write en un clúster Galera
Un clúster Galera o Percona XtraDB certifica una escritura en el commit y la aplica en los demás nodos un instante después, y una lectura enrutada dentro de ese hueco ve el pasado.
En este artículo 6 secciones
- En un clúster basado en certificación, commit significa que la escritura se ha certificado en todos los nodos, no que todos los nodos la hayan aplicado. La aplicación ocurre un instante después, típicamente de decenas a unos pocos cientos de milisegundos.
- Una lectura balanceada a un nodo que aún no ha aplicado la escritura devuelve la fila antigua. En un CMS eso se lee como una edición perdida: el editor guarda, recarga, ve el texto antiguo y vuelve a guardar sobre la versión más nueva. No se produce ningún error en ninguna parte.
- wsrep_sync_wait cierra el hueco haciendo que una lectura espere hasta que su nodo se ponga al día. Cuesta el apply lag en las lecturas que siguen a una escritura, así que se limita a las sesiones que lo necesitan y se deja desactivado en las páginas que sirve una caché de edge.
Commit significa certificado, no aplicado
Un clúster Galera ejecuta una transacción en un nodo. En el commit, ese nodo difunde el write set al clúster, cada nodo lo certifica frente a los write sets ya en vuelo, y el commit vuelve al cliente. Desde ese momento la escritura es duradera en el clúster y está ordenada de forma idéntica en todas partes.
Lo que aún no ha ocurrido es la aplicación. Cada nodo toma el write set certificado y lo aplica a su propia copia de los datos en sus propios applier threads, un poco después de que el commit haya vuelto. Corren dos relojes: la certificación abarca todo el clúster y forma parte del commit, la aplicación es local y viene después. En un clúster sano la distancia entre ambos va de decenas a unos pocos cientos de milisegundos. Crece durante ráfagas de escrituras, transacciones grandes y cambios de esquema, y se ve como un wsrep_local_recv_queue distinto de cero en el nodo que va por detrás.
Adónde va la lectura
Un clúster con lecturas balanceadas envía las lecturas adonde decida el proxy. Una petición escribe en el nodo A. La siguiente petición del mismo usuario, unos cientos de milisegundos después, lee en el nodo B. Si el nodo B ha recibido pero aún no ha aplicado el write set, responde con la fila tal como estaba antes de la escritura. Desde el punto de vista del nodo B la respuesta es correcta: ese es su estado confirmado. No se registra nada, nada da la alarma, y el monitor de lag se queda callado porque está construido para detectar lag sostenido, no los primeros cien milisegundos después de una transacción.
Por eso el síntoma parece aleatorio. Depende de qué nodo eligió el balanceador, de lo rápido que volvió el usuario y de lo ocupados que estaban los appliers en ese instante.
Cómo se ve en una aplicación
El patrón es una escritura seguida inmediatamente de una lectura del mismo usuario, que aterriza en un nodo distinto. En un CMS es el párrafo desaparecido: guardar, recargar, texto antiguo, reescribir, guardar, y el segundo guardado sobrescribe el primero. En una tienda online es un pedido que no existe en la página de confirmación. En un flujo de autenticación es un token que se acaba de emitir y no se encuentra.
Otros dos mecanismos producen una historia parecida y conviene descartarlos primero. La back-forward cache de un navegador puede restaurar una copia obsoleta de un formulario, y un nuevo guardado escribe entonces esa copia obsoleta. Un plugin puede hacer un read-modify-write sobre la misma fila desde dos peticiones. Ambos son reales y ninguno es el clúster. Un bucle que escribe una marca de tiempo en un nodo y la lee en los demás los separa en un minuto.
El ajuste: wsrep_sync_wait
wsrep_sync_wait es una máscara de bits. Con el bit 1 activo, un SELECT espera hasta que el nodo haya aplicado todos los write sets recibidos hasta ese momento antes de ejecutarse. La lectura tiene entonces garantizado ver todo lo confirmado en cualquier punto del clúster antes de que empezara. Puede fijarse para una sesión, que es el ámbito correcto para una ruta de editor o de checkout, o globalmente cuando toda la carga de trabajo es sensible 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 = 1La alternativa es el enrutamiento: enviar las lecturas que siguen a una escritura al nodo que escribió, con reglas de consulta del proxy o dentro de la aplicación. Funciona para las rutas que cubre. Una sola ruta de código que nadie enrutó vuelve a abrir el hueco, y por eso se prefiere el ajuste donde el coste puede pagarse: ataca el mecanismo, no una ruta a través de él.
Qué cuesta y dónde pagarlo
Una lectura bajo wsrep_sync_wait espera a que el applier local se ponga al día, así que paga el apply lag una vez por cada lectura que sigue a una escritura. En un clúster sano eso son de decenas a unos pocos cientos de milisegundos. Las sesiones con usuario autenticado, las rutas de administración y edición y el checkout son donde ese coste compra algo. Las páginas anónimas servidas desde una caché de edge no tocan la base de datos en un acierto y no se ven afectadas. El ajuste entra en vigor en la siguiente sentencia y se revierte de la misma manera, así que puede medirse en el sitio con la carga de trabajo real en lugar de estimarse.
Cómo comprobar su propio clúster
Tres lecturas lo zanjan. Si las lecturas esperan en absoluto. Cuánto se retrasa un nodo durante una ráfaga de escrituras. Y con qué frecuencia un read-after-write entre nodos ve realmente el pasado, desde un bucle que escribe en un nodo y lee en los demás.
SHOW GLOBAL VARIABLES LIKE 'wsrep_sync_wait'; -- 0 means reads do not wait
SHOW GLOBAL STATUS LIKE 'wsrep_local_recv_queue'; -- received, not yet appliedOperamos clústeres Percona XtraDB con lecturas balanceadas detrás de ProxySQL y HAProxy y podemos responder a las tres para cualquier clúster que operemos, incluido lo que cuesta el ajuste en esa carga de trabajo concreta.
Respuestas directas
¿Es síncrona la replicación de Galera?+
La certificación lo es: la escritura se certifica en todos los nodos antes de que vuelva el commit. La aplicación en cada nodo ocurre un instante después. Las dos mitades cuentan, y la segunda es donde viven los problemas de read-after-write.
¿Por qué mi CMS pierde ediciones en un clúster?+
Una lectura tras un guardado se enrutó a un nodo que había recibido el write set pero aún no lo había aplicado. El editor vio el contenido antiguo, lo reescribió y guardó sobre la versión más nueva. No se produce ningún error, por eso parece intermitente.
¿Ayuda vaciar la caché?+
Normalmente no. La caché almacenó lo que devolvió la base de datos, y durante un instante la base de datos devolvió el pasado. Diagnostique la ruta de lectura. Aquí la caché transporta el síntoma, no es la causa.
¿Qué cuesta wsrep_sync_wait?+
El apply lag, pagado en las lecturas que siguen a una escritura, típicamente de decenas a unos pocos cientos de milisegundos en un clúster sano. Las páginas servidas desde una caché de edge no se ven afectadas. Limítelo por sesión para pagarlo solo donde importa.
¿Opera un clúster con lecturas balanceadas?
Hable con un ingeniero sobre si el read-after-write está activo y qué cuesta en el suyo.
Hable con un ingenieroHa llegado al final.
Ponga la lectura a trabajar
¿Qué está ejecutando?
Cuéntenos la carga de trabajo, el tráfico y los requisitos de recuperación. Empiece por el configurador de servidores o repase los detalles con un ingeniero.
Escrito por los ingenieros que construyen y operan la infraestructura que describe.Hable con un ingeniero
