En este tema, exploraremos los diferentes modelos de consistencia en sistemas distribuidos. La consistencia es un aspecto crucial en el diseño de sistemas distribuidos, ya que define cómo se comportan las operaciones de lectura y escritura en presencia de réplicas de datos. A continuación, desglosaremos los conceptos clave, ejemplos prácticos y ejercicios para reforzar el aprendizaje.

Conceptos Clave

  1. Consistencia Estricta

  • Definición: Un sistema es estrictamente consistente si una lectura siempre devuelve el valor más reciente escrito.
  • Características:
    • Garantiza que todas las operaciones de lectura y escritura son vistas en el mismo orden por todos los nodos.
    • Difícil de implementar en sistemas distribuidos debido a la latencia de red y la falta de un reloj global.

  1. Consistencia Linealizable

  • Definición: También conocida como consistencia secuencial fuerte, asegura que las operaciones parecen ocurrir instantáneamente en algún punto entre su inicio y finalización.
  • Características:
    • Cada operación de lectura devuelve el valor más reciente escrito.
    • Proporciona una visión global consistente del sistema.

  1. Consistencia Secuencial

  • Definición: Un sistema es secuencialmente consistente si las operaciones de todos los procesos se intercalan de manera que respeten el orden de cada proceso individual.
  • Características:
    • No garantiza que las operaciones de lectura devuelvan el valor más reciente.
    • Más fácil de implementar que la consistencia linealizable.

  1. Consistencia Causal

  • Definición: Asegura que las operaciones que son causalmente relacionadas se ven en el mismo orden por todos los nodos.
  • Características:
    • Las operaciones concurrentes pueden ser vistas en diferentes órdenes por diferentes nodos.
    • Más relajada que la consistencia secuencial.

  1. Consistencia Eventual

  • Definición: Un sistema es eventualmente consistente si, en ausencia de nuevas actualizaciones, todas las réplicas convergen al mismo valor.
  • Características:
    • No garantiza un orden específico de operaciones.
    • Utilizada en sistemas donde la disponibilidad es más crítica que la consistencia inmediata.

Ejemplos Prácticos

Ejemplo 1: Consistencia Linealizable

Imaginemos un sistema de base de datos distribuida con dos nodos, A y B. Si un cliente escribe un valor x=10 en el nodo A y luego lee el valor de x en el nodo B, la consistencia linealizable garantiza que el cliente leerá x=10.

# Nodo A
database.write('x', 10)

# Nodo B
value = database.read('x')
print(value)  # Output: 10

Ejemplo 2: Consistencia Eventual

En un sistema de almacenamiento distribuido con tres nodos, A, B y C, si un cliente escribe y=20 en el nodo A, y luego otro cliente lee y en el nodo B antes de que la actualización se propague, el cliente podría leer un valor antiguo. Sin embargo, eventualmente, todos los nodos convergerán y y=20 será visible en todos los nodos.

# Nodo A
database.write('y', 20)

# Nodo B (antes de la propagación)
value = database.read('y')
print(value)  # Output: valor antiguo

# Nodo B (después de la propagación)
value = database.read('y')
print(value)  # Output: 20

Ejercicios Prácticos

Ejercicio 1: Identificar Modelos de Consistencia

Dado el siguiente escenario, identifica qué modelo de consistencia se está utilizando:

  1. Un sistema de chat donde los mensajes enviados por un usuario se muestran en el mismo orden para todos los participantes.
  2. Un sistema de almacenamiento en caché donde las actualizaciones se propagan lentamente, pero eventualmente todos los nodos tienen el mismo valor.
  3. Un sistema de base de datos donde las lecturas siempre devuelven el valor más reciente escrito.

Soluciones:

  1. Consistencia Secuencial
  2. Consistencia Eventual
  3. Consistencia Linealizable

Ejercicio 2: Implementar Consistencia Eventual

Implementa un sistema de almacenamiento distribuido simple que garantice consistencia eventual. Usa un diccionario para simular el almacenamiento en cada nodo y una función para propagar las actualizaciones.

class Nodo:
    def __init__(self):
        self.almacenamiento = {}
        self.vecinos = []

    def escribir(self, clave, valor):
        self.almacenamiento[clave] = valor
        self.propagar_actualizacion(clave, valor)

    def leer(self, clave):
        return self.almacenamiento.get(clave, None)

    def agregar_vecino(self, vecino):
        self.vecinos.append(vecino)

    def propagar_actualizacion(self, clave, valor):
        for vecino in self.vecinos:
            vecino.recibir_actualizacion(clave, valor)

    def recibir_actualizacion(self, clave, valor):
        self.almacenamiento[clave] = valor

# Crear nodos
nodoA = Nodo()
nodoB = Nodo()
nodoC = Nodo()

# Conectar nodos
nodoA.agregar_vecino(nodoB)
nodoA.agregar_vecino(nodoC)
nodoB.agregar_vecino(nodoA)
nodoB.agregar_vecino(nodoC)
nodoC.agregar_vecino(nodoA)
nodoC.agregar_vecino(nodoB)

# Escribir en nodoA
nodoA.escribir('z', 30)

# Leer en nodoB y nodoC
print(nodoB.leer('z'))  # Output: 30
print(nodoC.leer('z'))  # Output: 30

Conclusión

En esta sección, hemos explorado varios modelos de consistencia en sistemas distribuidos, desde la consistencia estricta hasta la consistencia eventual. Cada modelo tiene sus propias características y trade-offs, y la elección del modelo adecuado depende de los requisitos específicos de la aplicación. A través de ejemplos y ejercicios prácticos, hemos visto cómo se pueden implementar y utilizar estos modelos en sistemas distribuidos. En el próximo tema, profundizaremos en los algoritmos de consenso, que son fundamentales para lograr consistencia en sistemas distribuidos.

© Copyright 2024. Todos los derechos reservados