Saltar al contenido

056 — Modelos de consistencia y garantías de sesión

🗂️ parte 🎚️ nivel ⏱️ duración 📗 clase

Programa · Parte 10 · ← Anterior · Siguiente →

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.

Término Qué significa Procedencia
linealizabilidad La garantía más fuerte para un objeto: el sistema se comporta como si hubiera una sola copia y cada operación ocurriera en un instante entre su inicio y su fin. Es cara porque exige coordinación, y casi ninguna aplicación la necesita para todo. se introduce aquí
consistencia causal Si un evento pudo influir en otro, todos los observadores los ven en ese orden; los eventos sin relación causal pueden verse en cualquier orden. Es el punto dulce entre lo débil y lo caro: evita el efecto «respuesta antes que la pregunta» sin exigir coordinación global. se introduce aquí
lectura monotona Garantía de sesión que impide retroceder en el tiempo: si ya viste un valor, no volverás a ver uno anterior. Sin ella, alternar entre réplicas con distinto retraso hace que un dato aparezca y desaparezca al recargar. se introduce aquí
convergencia Que las réplicas acaben en el mismo estado si cesan las escrituras. Es la promesa de la consistencia eventual, y solo se cumple si hay una regla determinista de resolución de conflictos, como la que dan los CRDT. se introduce aquí

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:

  1. Situar los modelos principales en su jerarquía de implicación.
  2. Distinguir linealizabilidad de serializabilidad.
  3. Aplicar las cuatro garantías de sesión.
  4. Explicar la consistencia causal y cómo se implementa.
  5. 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 : 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 Medio (metadatos)
Sesión Ninguna Bajo
Eventual Ninguna Mínimo

Errores frecuentes

  1. Confundir linealizabilidad con serializabilidad. Distinto ámbito y distinta promesa.
  2. Pedir consistencia fuerte por defecto. Se paga latencia en cada operación (el «else» de PACELC).
  3. Aceptar consistencia eventual sin garantías de sesión. Produce quejas de usuario evitables con poco esfuerzo.
  4. Creer que los CRDT resuelven las invariantes. Convergen; no restringen.
  5. LWW-Register con relojes desincronizados. Descarta escrituras válidas.
  6. 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

  1. Clasifica cinco operaciones de tu sistema en la jerarquía de modelos.
  2. Implementa lectura de tus escrituras y lectura monótona en una de ellas.
  3. Reproduce una violación de causalidad y corrígela propagando dependencias.
  4. Identifica una invariante que ningún CRDT puede sostener y di cómo la resolverías.

Preguntas de evaluación

  1. Da un sistema serializable que no sea linealizable, con una traza.
  2. ¿Por qué la consistencia causal sobrevive a una partición y la secuencial no?
  3. Explica qué escrituras pierde un LWW-Register y en qué condiciones.
  4. 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:

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 conceptual doc oficial
Apache Cassandra conceptual doc oficial
PostgreSQL conceptual doc oficial
MySQL conceptual doc oficial
Google Cloud Spanner conceptual doc oficial
Redis conceptual doc oficial
DuckDB no doc oficial

Los que resuelven el caso

MongoDB

Apache Cassandra

PostgreSQL

MySQL

Google Cloud Spanner

Redis

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.


Programa · Parte 10 · ← Anterior · Siguiente →