Episodio 2 · día 1, 9:30

Si cae el primario

lectura · 13 mindominio · BCPS Bankoperación · transferencia interna

«Transferencia realizada»

Ana ha cobrado y le devuelve a Luis los 300 € de la cena. Pulsa Enviar en la app y, un cuarto de segundo después, lee «Transferencia realizada». Cierra la app y se va a trabajar.

Cuarenta milisegundos después de ese «hecho», en el centro de datos, el servidor del primario de Cuentasver en el mapa se apaga: una fuente de alimentación. En el episodio anterior, las réplicas servían para repartir lecturas. Hoy sirven para lo otro: una de ellas va a pasar a ser el primario. La pregunta es qué tiene esa réplica. Porque una réplica va siempre un poco por detrás, y la transferencia de Ana se confirmó hace 40 milisegundos.

Todo depende de una decisión que se toma mucho antes de que algo falle: cuándo dice el primario «hecho». Si lo dice en cuanto tiene el cambio, o si espera a que alguien más lo tenga.

Cuándo dice «hecho»

Transferenciasver en el mapa escribe sin parar y la app lee de las réplicas, como en el episodio 1. Cada panel tiene su modo de replicación, que decide cuándo responde el primario: asíncrona (sin esperar a nadie), semi-síncrona (cuando una réplica lo tiene) o síncrona (cuando lo tienen todas). La réplica 1 está en la misma ciudad; la 2, donde elijas. Las flechas de puntos son los cambios que viajan del primario a las réplicas y, en verde, las confirmaciones de vuelta.

Pulsa Arrancar y mira la latencia del commit. Luego pulsa el botón de Ana: transfiere, recibe su «hecho» y el primario se cae. Después, tira una réplica y prueba el failover manual.

Qué ha pasado

Confirmada y perdida

En asíncrona, el primario escribe el cambio en su WAL, en su propio disco, y responde «hecho».probar en el laboratorio Mandárselo a las réplicas es asunto suyo, y lo hace después: la réplica 1 va unos 0,3 s por detrás y la 2, algo más. Cuando el primario se cae 40 ms después del «hecho» de Ana, su transferencia estaba en un disco que ya no responde y en ninguna réplica. Tres segundos más tarde, el failover promueve la réplica 1, la que iba más adelantada. Para el nuevo primario, la transferencia de Ana no ha existido nunca.

No es solo la de Ana: todo lo que el primario confirmó en esos últimos 0,3 s se ha perdido: en la simulación, otras cinco a diez transferencias de otra gente. Todos recibieron «hecho». Eso es lo grave: no es un error que el cliente vea y pueda reintentar, es una promesa rota que nadie sabe que se ha roto. Si el disco del primario antiguo se recupera, esas transferencias siguen ahí, en un WAL que ya no pertenece a ninguna historia, y alguien tendrá que reconciliarlas a mano.

Síncrona: esperar a la copia

En síncrona, el primario no dice «hecho» hasta que las réplicas le confirman que tienen el cambio en su WAL. Con la misma caída, la transferencia de Ana existe en el nuevo primario: el «hecho» llegó más tarde, pero llegó cuando ya había una copia. Nada de lo confirmado se pierde.

El precio es la espera, y se paga en cada commit, haya caída o no.probar en el laboratorio Con las dos réplicas en la misma ciudad, apenas se nota. Con la réplica 2 en otro continente, cada commit espera 180 ms más de ida y vuelta. Y mientras espera, el trabajador del primario sigue ocupado con esa transacción: con el mismo tráfico, la carga del primario pasa del 20 % a más del 60 %. La distancia no la arregla nadie: es la velocidad de la luz en una fibra.

Una aclaración: «tiene el cambio» significa que la réplica lo ha guardado en su WAL, no que ya lo haya aplicado. Quien lea de ella justo después puede seguir viendo el dato anterior. Síncrona protege contra perder datos, no contra leer datos viejos; eso es el episodio 3.

El giro: la réplica que falta

Síncrona tiene un reverso.probar en el laboratorio Si el primario espera a todas las réplicas y una se cae, cada commit se queda esperando una confirmación que no llega. El primario está vivo, pero no termina ninguna escritura: los trabajadores se van quedando atrapados uno a uno y las escrituras confirmadas por segundo caen a cero. Con una sola réplica síncrona pasa lo mismo, y es la configuración más frecuente.probar en el laboratorio Hemos cambiado el riesgo de perder datos por el riesgo de no poder escribir: con dos máquinas que tienen que estar vivas a la vez, la probabilidad de que falle una se ha duplicado.

La semi-síncrona es el término medio: el primario espera a que al menos una réplica tenga el cambio, sea la que sea. Con la réplica 1 caída, sigue escribiendo gracias a la 2, algo más lento. Y como normalmente responde la más cercana, la réplica lejana no marca la latencia. Lo confirmado está al menos en dos sitios, siempre que el failover promueva la réplica que lo tiene, que es la que va más adelantada.

Failover: alguien tiene que decidir que el primario ha muerto

Promover una réplica no es instantáneo, y no lo decide nadie por sí solo.probar en el laboratorio Con failover manual, suena una alarma, alguien de guardia se despierta, comprueba que el primario no vuelve, elige la réplica más adelantada y la promueve. Mientras tanto, nadie puede hacer una transferencia; se puede leer, de réplicas cada vez más viejas. Con failover automático, un vigilante comprueba cada poco si el primario responde y, tras unos segundos sin respuesta, promueve él mismo la mejor réplica y avisa a Cuentas de su nueva dirección.

El automático es más rápido, pero tiene su propio peligro. Si el primario no está muerto, sino aislado (se ha cortado la red entre él y el vigilante), habrá dos primarios aceptando escrituras a la vez, cada uno con su versión de las cuentas. Es el split-brain, y evitarlo exige que los que votan quién es el primario se pongan de acuerdo aunque la red se parta: elección de líder y consenso, el bloque de coordinación distribuida del catálogo (el 8). Un primario antiguo que vuelve tampoco puede reincorporarse tal cual: tiene escrituras que el nuevo no tiene, y hay que rebobinarlo antes.

Criterio

Dos preguntas, con nombre propio. RPO (recovery point objective): cuántos datos confirmados puedes permitirte perder, medido en tiempo. RTO (recovery time objective): cuánto tiempo puedes estar sin servicio. El modo de replicación mueve el RPO; el failover, el RTO. Y los dos se pagan.

ModoRPO si cae el primarioQué cuesta en el día a díaSi cae una réplica
AsíncronaLo que la réplica llevara de retraso: décimas de segundo, o minutos si iba muy atrásNada: el commit no esperaNada
Semi-síncrona (1 de N)Cero, si se promueve la réplica que confirmóIda y vuelta a la réplica más cercanaSigue, mientras quede una
Síncrona (todas)CeroIda y vuelta a la más lejana, y trabajadores esperandoEl primario deja de escribir
FailoverRTORiesgo
ManualMinutos: lo que tarde la guardiaEl error humano a las cuatro de la mañana
AutomáticoSegundos: la detección más la promociónPromover sin que el primario haya muerto (split-brain), si no hay consenso
  1. ¿Perder un «hecho» es inaceptable?El libro de movimientos de Cuentas: una transferencia confirmada tiene que existir.
    semi-síncrona con al menos dos réplicas
  2. ¿Se puede reconstruir lo perdido desde otro sitio?El índice del buscador (episodio 4) se rehace desde Cuentas.
    asíncrona
  3. Asíncrona, con el retraso vigilado: el RPO es exactamente lo que la réplica vaya por detrás.

En BCPS Bank, Cuentas usa semi-síncrona con dos réplicas en la misma región y una tercera, asíncrona, en otra región para desastres: si se pierde la región entera, se asume perder unos segundos a cambio de no pagar 180 ms en cada transferencia. Síncrona pura con una sola réplica casi nunca es buena idea: convierte cualquier fallo de la réplica en un fallo del primario.

En el mundo real

PostgreSQL es asíncrona por defecto. Con synchronous_standby_names se elige a quién esperar: FIRST 2 (r1, r2) espera a las dos primeras de la lista y ANY 1 (r1, r2) a cualquiera, lo que aquí llamamos semi-síncrona. synchronous_commit afina qué significa «tener el cambio» (recibido, escrito en disco o ya aplicado) y se puede cambiar por transacción: una transferencia puede esperar y un registro de auditoría no. MySQL tiene la replicación semi-síncrona como plugin y, si ninguna réplica contesta en un plazo (10 s por defecto), vuelve a asíncrona sin avisar: elige disponibilidad antes que durabilidad.

Amazon Aurora esquiva parte del problema separando el almacenamiento: guarda seis copias en tres zonas y confirma cuando cuatro las tienen; sus réplicas leen del mismo almacenamiento y un failover tarda del orden de treinta segundos. Para el failover automático de PostgreSQL, lo habitual es Patroni, que usa etcd o Consul para que solo haya un primario aunque la red se parta; MySQL tiene Orchestrator y Group Replication.

En 2018, GitHub sufrió una partición de 43 segundos entre dos centros de datos. Su failover automático promovió primarios en la otra costa, y durante esos segundos se escribieron datos en los dos sitios. Restaurar la consistencia les llevó 24 horas de servicio degradado. Su informe posterior es una buena lectura sobre lo que cuesta un failover automático cuando la réplica promovida no tenía todo lo que el primario confirmó.

Siguiente episodio

La transferencia de Ana existe (con semi-síncrona). Ana abre la app para comprobarlo y la app lee de una réplica, como en el episodio 1. La réplica tiene el cambio en su WAL, pero todavía no lo ha aplicado. Y en la lista de movimientos de Ana, los 300 € siguen ahí.

Episodio 3 No veo mi transferencia El retraso de réplica: leer lo que acabas de escribir, lecturas que vuelven atrás y tres formas de arreglarlo.

← Todos los episodios · Cuentas en el mapa de BCPS Bank