Replicación y consistencia eventual
Replicación y consistencia eventual
1) Por qué consistencia eventual
Cuando el sistema está distribuido por zonas/regiones, la escritura sincrónica en todas partes da alta latencia y poca disponibilidad cuando se producen fallos en la red. La consistencia eventual (EC) permite la resincronización temporal de réplicas en aras de:- retraso de grabación bajo (recepción local),
- mejor disponibilidad en particiones de red,
- escala horizontal.
La tarea clave es la consistencia controlada y no homogénea: el usuario ve datos «bastante frescos», los invariantes de dominio persisten, los conflictos se detectan y se resuelven previsiblemente.
2) Modelos de consistencia - que prometemos al cliente
Strong: la lectura ve inmediatamente la última entrada.
Bounded stale/read-not-older-than (RNOT): leer no envejece la marca (LSN/version/time).
Causal: se mantiene la relación «causal» (A a B).
Read-Your-Writes: el cliente ve sus registros recientes.
Lecturas monotónicas: cada siguiente lectura no «retrocede».
Sesión: conjunto de garantías en una sola sesión.
Eventual: a falta de nuevos registros, todas las réplicas convergen.
Práctica: combine Session + RNOT en paths críticos y Eventual en escaparates/cachés.
3) Replicación: mecánica y anti-entropía
Sincrónico (quórum/RAFT): el registro se considera exitoso después de que los nodos confirman N; RPO mínima, por encima de p99.
Asincrónico: el líder commitita localmente, distribuye la revista más tarde; baja latencia, RPO> 0.
Físico (WAL/binlog): rápido, homogéneo.
Lógico/CDC: flujo de cambios a nivel de filas/eventos, enrutamiento flexible, filtros.
Anti-entropy: conciliaciones y reparaciones periódicas (Merkle-trees, comparación de hash, re-sync de fondo).
4) Identificadores de la versión y órdenes de la causalidad
Versiones monótonas: increment/LSN/epoch; simplemente, pero no codifican el paralelismo.
Lamport timestamp: orden parcial por reloj lógico.
Clock Vector: fija las ramas paralelas y permite detectar los updates conflictivos (concurrent).
Hybrid/TrueTime/Clock-SI: lógica de «no antes T» para el orden global.
Recomendación: para CRDT/apdates de conflicto - clock de vector; para «no envejecer» - LSN/GTID.
5) Conflictos: detección y resolución
Situaciones tipo: registro de dos regiones a un mismo objeto.
Estrategias:1. Last-Write-Wins (LWW) por reloj/sello lógico - simple, pero puede «perder» los apdates.
2. Funciones de dominio de Merge:- los campos contadores se pliegan (G-Counter/PN-Counter),
- los conjuntos se combinan con «add-wins/remove-wins»,
- importes/balances - sólo a través de registros de transacciones, no a través de un simple LWW.
- 3. CRDT (tipos convergentes): G-Counter, OR-Set, LWW-Register, RGA para listas.
- 4. Transformaciones operativas (raras veces para el DB, más frecuentes para los editores).
- 5. Resolución manual: conflicto en «inbox», el usuario elige la versión correcta.
Regla: los invariantes de dominio dictan una estrategia. Para dinero/saldos - evite LWW; utilice transacciones/eventos con compensación.
6) Garantía de registros y idempotencia
Las claves idempotentes en los comandos (payment, withdraw, create) → la repetición es segura.
Deduplicación en «entrada» (inbox) y «salida» (outbox) por clave de idempotencia/número de serie.
Exactly-once es inalcanzable sin premisas fuertes; practique at-least-once + idempotencia.
Outbox/Inbox-pattern: la entrada en la DB y la publicación del evento son atómicas (transacción local), el destinatario procesa por idempotency-key.
7) Lecturas «no envejecer X» (RNOT)
Técnicas:- Puerta LSN/GTID: el cliente transmite una versión mínima (a partir de la respuesta del registro), el router/proxy dirige a la réplica que ha alcanzado el LSN ≥ X, de lo contrario, al líder.
- Time-bound: «no envejezca 2 segundos» es un SLA simple sin versiones.
- Pinning de sesión: después de grabar N segundos, sólo leemos el líder (Read-Your-Writes).
8) Flujos de cambios y negociación de cachés
CDC → bus de eventos (Kafka/Pulsar) → consumidores (cachés, índices, escaparates).
Caché de discapacidad: topics 'invalidate: {ns}: {id}'; procesamiento idempotent.
Rebuild/Backfill: al rassincrone, reenvíe las proyecciones del registro de eventos.
9) Sagas y compensaciones (transacciones entre servicios)
En el mundo de la CE, las operaciones de larga vida se dividen en pasos con acciones compensatorias:- Orquestación: el coordinador evoca los pasos y su compensación.
- Coreografía: los pasos responden a los acontecimientos y ellos mismos publican los siguientes.
Invariantes (ejemplo): «balance ≥ 0» - verificación en los límites del paso + compensación en caso de desviación.
10) Multi-región y particiones de red
Local-write, async-replicate: registro en la región local + envío a otros (EC).
Geo-ficción: datos «pegados» a la región (baja latencia, menos conflictos).
BD de quórum (Raft) para datos CP; cachés/escaparates - AP/EC.
Plan de split-brain: cuando se pierde la comunicación, las regiones continúan operando dentro de los límites de dominio (escritura fencing, cuotas), luego - reconcile.
11) Observabilidad y SLO
Métricas:- Replica lag: tiempo/distancia LSN/offset (p50/p95/p99).
- Staleness: proporción de respuestas mayores que el umbral (por ejemplo,> 2s o LSN
- Conflict rate: frecuencia de conflictos y merge exitosos.
- Tiempo de convergencia: tiempo de convergencia de las réplicas después del pico.
- Reconcile backlog: volumen/tiempo de lotes rezagados.
- RPO/RTO por categoría de datos (CP/AP).
- Lag> objetivo, el aumento de los conflictos, «largas» ventanas de la inconsistencia.
12) Diseño del diagrama de datos bajo CE
Versión explícita/vector en cada entrada (columnas 'version', 'vc').
Append-only registros para invariantes críticos (balances, acumulaciones).
Identificadores de evento (snowflake/ULID) para el orden y el dedup.
Campos con naturaleza conmutativa (contadores, múltiples) → candidatos a CRDT.
Diseño API: PUT con if-match/etag, PATCH con precondición.
13) Patrones de almacenamiento y lectura
Modelo de lectura/CQRS: escribir en «origen», leer desde proyecciones (puede estar atrasado → mostrar «actualizado»...).
Rutas Stale-OK (directorio/cinta) vs Strict (cartera/límites).
Marcas Sticky/Bounded-stale en la solicitud (encabezado 'x-read-consistency').
14) Lista de verificación de implementación (0-45 días)
0-10 días
Categorizar los datos: CP-crítico (dinero, pedidos) vs UE/stale-OK (catálogos, índices de búsqueda).
Definir el SLO del establo (por ejemplo, "no envejece 2s'), los lags de destino.
Habilitar la versificación de objetos e idempotency-keys en la API.
11-25 días
Implementar CDC y outbox/inbox, rutas de memoria caché de discapacidad.
Agregar RNOT (puerta LSN) y pinning de sesión en paths críticos de escritura.
Implementar un mínimo de una estrategia merge (LWW/CRDT/dominio) y un registro de conflictos.
26-45 días
Automatice los informes anti-entropy (conciliaciones/reparaciones) y stale.
Pasar el día del juego: separación de la red, estallido de conflictos, recuperación.
Visualizar en los dashboards: lag, staleness, conflict rate, convergence.
15) Anti-patrones
LWW ciego para invariantes críticos (pérdida de dinero/puntos).
Ausencia de idempotencia → tomas de cirugías en retrés.
Modelo «fuerte» en todo → colas p99 excesivas y fragilidad en fallas.
No hay garantías RNOT/Session → UX «parpadea», los usuarios «no ven» sus cambios.
Russincronización oculta de caché y origen (sin CDC/discapacidad).
La ausencia de la herramienta reconcile/anti-entropy - los datos «por siglos» son divergentes.
16) Métricas de madurez
Replica lag p95 ≤ objetivo (por ejemplo, ≤ 500 ms dentro de la región, ≤ 2 s interregiones).
Staleness SLO se ejecuta ≥ el 99% de las solicitudes en rutas «estrictas».
Conflict resolution success ≥ 99. 9%, tiempo medio de resolución ≤ 1 min.
Tiempo de convergencia después de los picos - minutos, no horas.
El 100% de las operaciones «monetarias» están cubiertas por llaves idempotency y outbox/inbox.
17) Recetas (snippets)
If-Match/ETag (HTTP)
PUT /profile/42
If-Match: "v17"
Body: { "email": "new@example.com" }
Si la versión ha cambiado - '412 Precondition Failed' → el cliente resuelve el conflicto.
Solicitud «no envejecer LSN» (pseudo)
x-min-lsn: 16/B373F8D8
El router selecciona una réplica con 'replay _ lsn ≥ x-min-lsn', de lo contrario es el líder.
CRDT G-Counter (idea)
Cada región almacena su propio contador; total - suma de todos los componentes; replicación: la operación es conmutativa.
18) Conclusión
La consistencia eventual no es un compromiso de calidad, sino un contrato consciente: en algún lugar pagamos frescura por la velocidad y la disponibilidad, pero protegemos invariantes críticos con estrategias y herramientas de dominio. Introduzca las versiones, idempotency, RNOT/Session de la garantía, CDC y anti-entropy, mida lag/staleness/conflicts - y su sistema distribuido será rápido, estable y previsiblemente convergente incluso bajo fallas y cargas máximas.