056 — Modelos de consistencia y garantías de sesión
Parte 10 — Distribución, réplica y consistencia · Avanzado ·
3 horas estimadas · motores cassandra, mongodb, spanner · laboratorio
labs/07-replication · 5 fuentes.
Conceptos centrales: linealizabilidad · consistencia causal · lectura monotona · convergencia
En este caso se comparan 7 motores: 6 lo resuelven (0 con el resultado comprobado por máquina) y 1 no, con el motivo escrito.
De qué trata esta clase
El espectro entre linealizabilidad y consistencia eventual, con las garantías de sesión —leer tu propia escritura, lectura monótona— que resuelven en la práctica la mayoría de los síntomas visibles. Insiste en que la consistencia eventual solo converge si existe una regla determinista de resolución de conflictos.
flowchart LR
C["🗄️ Clase 056"]
C --> K1["linealizabilidad"]
C --> K2["consistencia causal"]
C --> K3["lectura monotona"]
C --> K4["convergencia"]
classDef raiz fill:#0b3d2e,stroke:#3fb950,color:#fff
class C raiz
Antes de empezar
Esta clase supone que ya trabajaste lo siguiente. Si algo de la última columna no te suena, vuelve a esa clase antes de seguir: aquí se usa sin volver a explicarlo.
| # | Clase previa | Lo que se da por sabido |
|---|---|---|
| 055 | CAP, PACELC y lo que realmente se elige | partición de red · disponibilidad · latencia frente a consistencia |
Vocabulario de la clase
Los términos que siguen se usan más adelante con este significado exacto. La definición completa, con sus términos relacionados, está en el glosario del programa.
Propósito
Ordenar el vocabulario de la consistencia. «Consistencia fuerte» y «eventual» son los extremos de una jerarquía con muchos escalones útiles en medio, y elegir el escalón correcto ahorra coordinación.
Resultados de aprendizaje
Al terminar podrás:
- Situar los modelos principales en su jerarquía de implicación.
- Distinguir linealizabilidad de serializabilidad.
- Aplicar las cuatro garantías de sesión.
- Explicar la consistencia causal y cómo se implementa.
- Reconocer para qué sirven los CRDT y cuáles son sus límites.
Fundamentos
La jerarquía
flowchart TD
L["Linealizabilidad<br/>(orden total + tiempo real)"] --> SEQ["Consistencia secuencial<br/>(orden total, sin tiempo real)"]
SEQ --> CAU["Consistencia causal<br/>(orden de lo relacionado)"]
CAU --> SES["Garantías de sesión<br/>(por cliente)"]
SES --> EV["Consistencia eventual<br/>(convergencia, sin plazo)"]
L -.->|"exige coordinación"| CO["Indisponible bajo partición"]
CAU -.->|"sin coordinación global"| DI["Disponible bajo partición"]
La línea que importa está entre secuencial y causal: por encima hace falta acuerdo global y el sistema deja de estar disponible durante una partición; por debajo, no.
Linealizabilidad frente a serializabilidad
Se confunden constantemente y son cosas distintas:
| Linealizabilidad | Serializabilidad | |
|---|---|---|
| Ámbito | Operaciones sobre un objeto | Transacciones sobre varios objetos |
| Garantiza | Orden total compatible con el tiempo real | Equivalencia a alguna ejecución en serie |
| Tiempo real | Sí: si A termina antes de que B empiece, A va antes | No: el orden serie puede ser cualquiera |
| Origen | Sistemas distribuidos | Bases de datos |
La combinación de ambas se llama estricta serializabilidad, y es lo que ofrecen Spanner y CockroachDB. PostgreSQL en SERIALIZABLE sobre un nodo también la cumple de hecho, porque no hay distribución que rompa el tiempo real.
Consecuencia práctica: un sistema puede ser serializable y devolver datos «viejos». Si una transacción confirma y otra empieza después y no la ve, es serializable —existe un orden serie válido— pero no linealizable. Para un usuario que acaba de guardar algo, esa distinción es la diferencia entre un sistema correcto y uno roto.
Las cuatro garantías de sesión
Definidas por conveniencia del cliente, no del sistema. Son baratas y resuelven casi todas las quejas de usuario:
| Garantía | Promete | Implementación típica |
|---|---|---|
| Lectura de tus escrituras | Ves lo que acabas de escribir | Leer del líder tras escribir, o esperar al LSN |
| Lectura monótona | Nunca ves datos que retroceden | Fijar la sesión a una réplica |
| Escrituras monótonas | Tus escrituras se aplican en tu orden | Enrutar las escrituras de la sesión al mismo nodo |
| Lectura de tus escrituras en orden | Lees los efectos en el orden causal | Marcas de versión propagadas |
Ninguna exige coordinación global. Todas sobreviven a una partición. Es el mejor retorno por unidad de esfuerzo en un sistema distribuido.
Consistencia causal
Si el evento A causó el evento B, todo observador ve A antes que B. Los eventos no relacionados pueden verse en cualquier orden.
Se apoya en la relación «ocurre antes» de Lamport (1978) y se implementa con relojes vectoriales o marcas de versión que viajan con los datos.
Ana publica una nota (A)
Luis lee A y comenta (B) B depende causalmente de A
Sara publica algo sin relación (C) C es concurrente con A y B
Todo observador debe ver A antes que B.
Ver C antes o después es indiferente.
Es el modelo más fuerte que se puede sostener con disponibilidad total, y basta para casi cualquier sistema social o colaborativo. El caso que motiva su existencia es el clásico: nadie debe ver la respuesta a un mensaje que aún no ha visto.
CRDT
Estructuras cuyo estado converge sin coordinación, porque su operación de fusión es asociativa, conmutativa e idempotente. Shapiro et al. las formalizaron.
| CRDT | Semántica | Límite |
|---|---|---|
| Contador G | Solo incrementa | No decrementa |
| Contador PN | Incrementa y decrementa | No admite cotas |
| Conjunto G | Solo añade | No elimina |
| Conjunto OR | Añade y elimina | Metadatos crecen |
| LWW-Register | Gana el de marca de tiempo mayor | Se pierden escrituras |
| RGA / secuencias | Texto colaborativo | Complejo, metadatos |
El límite fundamental: un CRDT no puede hacer cumplir una invariante global. Un contador PN converge, y no puede garantizar «nunca por debajo de cero» sin coordinación, porque dos réplicas pueden decrementar simultáneamente sin verse. Esa es exactamente la frontera de Bailis (clase 045).
LWW-Register merece una advertencia: es el CRDT más usado y el que silenciosamente descarta escrituras concurrentes. Con relojes desincronizados, la que gana puede ser la más antigua.
Ejemplo trabajado
Foro del curso, replicado entre dos regiones con replicación asíncrona.
Sin ninguna garantía:
t0 Ana (Santiago) publica "¿Alguien entiende la clase 34?" → réplica CL
t1 Luis (Fráncfort) lee la pregunta (ya replicada) y responde → réplica DE
t2 Sara (Fráncfort) carga el hilo → ve la respuesta de Luis
pero NO la pregunta de Ana
Sara ve una respuesta a nada. Es una violación de causalidad, y es lo que produce la replicación asíncrona sin control de orden.
Con consistencia causal:
# Cada publicación lleva las dependencias que su autor había visto.
publicacion = {
"id": "p2",
"autor": "luis",
"texto": "Yo la entendí, mira...",
"depende_de": ["p1"], # Luis había visto p1 al escribir
}
def mostrar(hilo, replica):
for p in hilo:
# No se muestra nada cuyas causas no estén presentes.
if not all(replica.tiene(d) for d in p["depende_de"]):
replica.solicitar(p["depende_de"])
continue
mostrar_publicacion(p)
La réplica retrasa p2 hasta tener p1. Sara nunca ve la respuesta sin la pregunta. No hizo falta coordinación global: solo propagar dependencias.
Con garantías de sesión, para el otro problema:
t0 Ana publica en la réplica CL
t1 Ana recarga → se enruta a la réplica DE (aún sin replicar)
t2 Ana no ve su propia publicación
def leer_hilo(sesion, hilo_id):
r = replica_de(sesion) # lectura monótona: siempre la misma
if r.version() < sesion.get("version_minima", 0):
r = lider # lectura de tus escrituras
return r.leer(hilo_id)
def publicar(sesion, texto):
version = lider.publicar(texto)
sesion["version_minima"] = version
Dos garantías, unas pocas líneas, ninguna coordinación global.
Contraste con un caso donde hace falta coordinación. «El foro se cierra al llegar a 1 000 mensajes»: es una invariante global sobre un contador con cota. Ningún CRDT ni garantía de sesión la sostiene. Requiere consenso (clase 047) o aceptar que se pase de 1 000 y compensar después.
Tabla de decisión resultante:
| Operación del foro | Modelo suficiente |
|---|---|
| Publicar y leer mensajes | Causal + sesión |
| Contar mensajes para mostrar | Eventual (aproximado) |
| Cerrar el hilo al llegar al límite | Linealizable |
| Editar el propio mensaje | Sesión (escrituras monótonas) |
| Edición colaborativa a varias manos | CRDT de secuencia |
Comparación
| Modelo | Coordinación | Disponible bajo partición | Coste |
|---|---|---|---|
| Linealizable | Global, por operación | No | Alto |
| Secuencial | Global | No | Alto |
| Causal | Solo dependencias | Sí | Medio (metadatos) |
| Sesión | Ninguna | Sí | Bajo |
| Eventual | Ninguna | Sí | Mínimo |
Errores frecuentes
- Confundir linealizabilidad con serializabilidad. Distinto ámbito y distinta promesa.
- Pedir consistencia fuerte por defecto. Se paga latencia en cada operación (el «else» de PACELC).
- Aceptar consistencia eventual sin garantías de sesión. Produce quejas de usuario evitables con poco esfuerzo.
- Creer que los CRDT resuelven las invariantes. Convergen; no restringen.
LWW-Registercon relojes desincronizados. Descarta escrituras válidas.- No decir cuánto dura «eventualmente». Sin un plazo medido, no es una garantía operativa.
De la clase a la operación
Casi todas las quejas de «datos que aparecen y desaparecen» se resuelven con lectura de tus escrituras y lectura monótona, sin tocar el modelo de consistencia del almacén. Es la primera intervención que hay que probar.
Reto de transferencia
- Clasifica cinco operaciones de tu sistema en la jerarquía de modelos.
- Implementa lectura de tus escrituras y lectura monótona en una de ellas.
- Reproduce una violación de causalidad y corrígela propagando dependencias.
- Identifica una invariante que ningún CRDT puede sostener y di cómo la resolverías.
Preguntas de evaluación
- Da un sistema serializable que no sea linealizable, con una traza.
- ¿Por qué la consistencia causal sobrevive a una partición y la secuencial no?
- Explica qué escrituras pierde un
LWW-Registery en qué condiciones. - Elige el modelo mínimo suficiente para tres operaciones tuyas y justifica cada elección.
🌐 El mismo problema en cada motor
Caso: Leer lo que uno acaba de escribir, y otras garantías que no son gratis
«Consistencia eventual» no dice nada útil por sí sola: dice que algún día todas las réplicas coincidirán. Lo que decide si una aplicación funciona son las garantías intermedias, y las tres que más importan tienen nombre propio:
- Leer las propias escrituras: quien acaba de guardar su perfil lo ve actualizado. Sin esta garantía, el usuario guarda, recarga y ve lo de antes: la queja de soporte más común de los sistemas replicados.
- Lecturas monótonas: nadie ve el tiempo ir hacia atrás. Sin ella, dos recargas seguidas pueden mostrar un comentario y luego no mostrarlo.
- Consistencia causal: si una respuesta se escribió después de una pregunta, nadie ve la respuesta antes que la pregunta.
Ninguna se consigue sola: cada motor las ofrece —o no— con un mecanismo distinto, y casi siempre hay que pedirlas.
Esta comparación es conceptual: la decisión no se reduce a una consulta con resultado, así que aquí no hay sello de máquina. Lo que se compara es lo que cada motor ofrece y a qué precio, con la página oficial al lado de cada afirmación.
| Motor | ¿Resuelve el caso? | Nivel de prueba | Código | Fuente |
|---|---|---|---|---|
| MongoDB | sí | conceptual | — | doc oficial |
| Apache Cassandra | sí | conceptual | — | doc oficial |
| PostgreSQL | sí | conceptual | — | doc oficial |
| MySQL | sí | conceptual | — | doc oficial |
| Google Cloud Spanner | sí | conceptual | — | doc oficial |
| Redis | sí | conceptual | — | doc oficial |
| DuckDB | no | — | — | doc oficial |
Los que resuelven el caso
MongoDB
- Cómo se hace aquí: Las tres están y se piden explícitamente. Las sesiones causalmente consistentes (
startSession({causalConsistency: true})) garantizan leer las propias escrituras y lecturas monótonas incluso leyendo de secundarios, porque el controlador lleva la cuenta del tiempo de operación y el servidor espera a alcanzarlo. - Por qué sí: Es de las implementaciones más completas y mejor documentadas: la garantía es del cliente, no del servidor, que es donde el usuario la necesita.
- Por qué no: Hay que abrir la sesión y usarla en todas las operaciones relacionadas: si una parte del código no la usa, la garantía desaparece justo ahí, sin error ni aviso.
- 📄 Documentación oficial: https://www.mongodb.com/docs/manual/core/causal-consistency-read-write-concerns/
Apache Cassandra
- Cómo se hace aquí: No hay garantías de sesión: hay una fórmula. Si el número de réplicas leídas más las escritas supera el factor de replicación (R + W > RF), la lectura ve necesariamente la última escritura. Con
RF = 3,QUORUMen ambas cumple la condición. - Por qué sí: La garantía es explícita, aritmética y comprobable: no depende de confiar en la implementación, sino de una desigualdad que se puede verificar en el código.
- Por qué no: Es responsabilidad del programador en cada consulta. Y aun cumpliendo la fórmula, escrituras concurrentes se resuelven por «la última gana» según el reloj: no hay causalidad, hay marcas de tiempo.
- 📄 Documentación oficial: https://cassandra.apache.org/doc/latest/cassandra/architecture/dynamo.html
PostgreSQL
- Cómo se hace aquí: Leyendo del primario, las tres garantías se cumplen por construcción. Al repartir lecturas entre réplicas hay que recuperarlas a mano: la aplicación lee la posición del WAL tras escribir y espera a que la réplica la haya alcanzado, o dirige al primario las lecturas posteriores a una escritura del mismo usuario.
- Por qué sí: El mecanismo está expuesto —
pg_current_wal_lsn,pg_last_wal_replay_lsn— así que la garantía se puede construir y medir, no solo esperar. - Por qué no: Nada de eso viene hecho. La forma habitual de repartir lecturas —un balanceador delante— rompe las tres garantías sin que ninguna capa avise, y el síntoma aparece en producción como «a veces no se guarda».
- 📄 Documentación oficial: https://www.postgresql.org/docs/current/hot-standby.html
MySQL
- Cómo se hace aquí: Con Group Replication existe
group_replication_consistency, que permite exigir que una lectura espere a haber aplicado todas las transacciones anteriores del grupo (BEFORE), o que una escritura espere a que todos la hayan aplicado (AFTER). - Por qué sí: Es un ajuste por sesión con nombres claros para lo que se quiere: permite pedir «leer las propias escrituras» sin implementar nada.
- Por qué no: Solo está en Group Replication, no en la réplica clásica —que es la que tiene la inmensa mayoría de las instalaciones— y cada nivel añade espera real a cada operación.
- 📄 Documentación oficial: https://dev.mysql.com/doc/refman/8.4/en/group-replication-consistency-guarantees.html
Google Cloud Spanner
- Cómo se hace aquí: Ofrece la garantía más fuerte que existe —consistencia externa, es decir, serializabilidad estricta— de modo que las tres garantías de sesión se cumplen sin pedir nada. También permite lecturas de instantánea acotadas por antigüedad, para leer más barato aceptando datos con retraso.
- Por qué sí: No hay que razonar sobre anomalías de réplica: el sistema se comporta como si fuera un solo nodo, incluso entre continentes.
- Por qué no: Se paga en latencia por escritura y en dependencia de un proveedor. Y las lecturas de instantánea, que son las baratas, vuelven a introducir el retraso: la garantía fuerte es la cara.
- 📄 Documentación oficial: https://cloud.google.com/spanner/docs/timestamp-bounds
Redis
- Cómo se hace aquí: Leyendo del primario, un cliente ve siempre sus escrituras: el hilo único las ordena. Leyendo de réplicas, no hay garantía alguna, porque la réplica es asíncrona.
WAITpermite bloquear hasta que N réplicas hayan recibido las escrituras anteriores. - Por qué sí:
WAITda un control explícito y medible sobre cuánta durabilidad y cuánta coherencia se exige, operación por operación. - Por qué no:
WAITgarantiza recepción, no aplicación ni durabilidad en disco, y añade una espera de red en el camino caliente: usarlo en cada escritura anula la razón por la que se eligió Redis. - 📄 Documentación oficial: https://redis.io/docs/latest/commands/wait/
Los que no resuelven este caso — y qué se hace en su lugar
Descartar un motor con un argumento es tan formativo como usarlo. Ninguna de estas filas dice que el motor sea peor: dice que este problema no es el suyo.
| Motor | Por qué no | Qué se hace en su lugar | Fuente |
|---|---|---|---|
| DuckDB | No hay réplicas ni sesiones distribuidas: un proceso, un archivo. Las garantías de sesión no tienen sentido donde no hay más de una copia. | La pregunta equivalente en analítica es otra —cuánto retraso tiene el almacén respecto al sistema operacional— y se trata en la parte de integración y captura de cambios. | doc |
Laboratorio
python scripts/validate_repository.py
python labs/07-replication/run_replication_lab.py
Guarda como evidencia la salida completa, la versión del motor y la semilla o los parámetros usados. Una captura sin comando no es evidencia: no se puede repetir.
Evaluación
| Criterio | Peso | Qué se comprueba |
|---|---|---|
| Comprensión conceptual | 25 % | Explica el mecanismo, no solo el resultado |
| Ejecución reproducible | 25 % | Otra persona obtiene lo mismo con las instrucciones dadas |
| Interpretación basada en evidencia | 25 % | Cada conclusión se apoya en una salida o una medición |
| Límites y riesgos declarados | 25 % | Dice qué no demuestra el ejercicio y qué faltaría en producción |
La clase se da por superada cuando la respuesta explica el mecanismo, muestra la salida que la respalda y declara al menos un límite del ejercicio.
Fuentes de esta clase
Todo lo afirmado arriba procede de estas obras. Los identificadores viven en
catalog/sources.json y el estado de los
enlaces se comprueba con python scripts/check_external_links.py.
- Kyle Kingsbury (2026). Jepsen: Consistency Models.
Mapa de modelos de consistencia y sus relaciones de implicación. - Kyle Kingsbury (2026). Jepsen: Analyses.
Informes que verifican empiricamente las garantías que cada motor afirma. - Werner Vogels (2009). Eventually Consistent. Communications of the ACM 52(1). DOI 10.1145/1435417.1435432.
Definición operativa de consistencia eventual y de sus variantes de sesión. - Leslie Lamport (1978). Time, Clocks, and the Ordering of Events in a Distributed System. Communications of the ACM 21(7). DOI 10.1145/359545.359563.
Orden causal y relojes logicos: base de la consistencia distribuida. - Marc Shapiro, Nuno Preguica, Carlos Baquero, Marek Zawirski (2011). Conflict-free Replicated Data Types. SSS.
Estructuras que convergen sin coordinación: alternativa al bloqueo distribuido.