Chord #
Chord es un sistema DHT (Distributed Hash Table) relativamente sencillo de entender, utilizado en redes p2p
Sistemas similares:
Material de lectura #
Estos apuntes estan basados en:
La sección 6.2.3 Distributed Hash tables del libro Distributed Systems, donde se describe brevemente Chord. No es necesario leer la nota avanzada, pero sí la subsección Exploiting network proximity.
En el paper original. Recomendado leer la sección 1 y la sección 4.3 (hasta el Teorema, que no es necesario leer ni entender).
Simulador #
Pueden probar como funciona Chord con este simulador
¿Pero qué es? #
Es un sistema de lookup distribuido.
En criollo: dado una clave (nombre) obtiene el nodo asociado.
Ej:
lookup(key)
retorna la IP del nodo asociado.Y lo trata de hacer de manera eficiente
El protocolo especifica:
Como realizar el lookup (obviamente).
Como nuevos nodos se unen al sistema.
Como manejar la salida de nodos (planeada o no).
Propiedades bonitas #
Balanceo de carga
Descentralizado
Escalable
Alta disponibilidad
Flexibilidad en asignación de nombres
(Estas propiedades son las que debe tener cualquier servicio de nombres)
Descripción general #
Usa un espacio de $m$ bits para asignar identificadores a nodos y claves a entidades.
El número $m$ de bits usualmente esta entre 128 y 160.
- Tiene que ser lo suficientemente grande para evitar colisiones.
Requiere hash consistente para distribuir de manera uniforme las claves en el espacio de nombres.
- Ej: SHA1
- El objetivo es todos los nodos administren (aproximadamente) la misma cantidad de claves.
Una entidad con clave $k$ es administrada por el nodo con el menor identificador $p$ tal que $p \geq k$.
- Para simplificar, vamos a decir que la entidad $k$ es administrada por el nodo $p$, tal que $p \geq k$
Dicho nodo se denomina sucesor de $k$ y se denota $succ(k)$
Los nodos se organizan en un anillo
- Cada nodo mantiene referencias a su sucesor y predecesor
Resolución de nombres #
Problema: Dado un clave $k$ ¿Cómo resolver la dirección de $succ(k)$?
Solución lineal #
Veamos primero una solución que sencilla, que no usa Chord.
Lo más simple es pasar la consulta al siguiente nodo en el anillo.
Cuando un nodo $p$ recibe una consulta por una entidad $k$
- Responde la consulta si $pred(p) < k \leq p$
- Caso contrario, pasa la consulta a $succ(p+1)$
En promedio, una consulta requerirá recorrer la mitad del anillo.
Solución exponencial #
Chord intenta resolver eficientemente la dirección de $succ(k)$
Cada nodo mantiene una tabla con $s \leq m$ entradas conocida como tabla finger
La tabla del nodo $p$ se denota $F_p$
Cada entrada $i$ contiene lo siguiente:
$$ F_p[i] = succ((p + 2^{i-1}) \quad mod \quad 2^m) $$
Por ejemplo, con $m=5$ ($2^5=32$ identificadores), la tabla del nodo 1 ($p=1$) es:
índice | sucesor |
---|---|
1 | $ succ(1 + 2^{1-1}) = 2 $ |
2 | $ succ(1 + 2^{2-1}) = 3 $ |
3 | $ succ(1 + 2^{3-1}) = 5 $ |
4 | $ succ(1 + 2^{4-1}) = 9 $ |
5 | $ succ(1 + 2^{5-1}) = 17 $ |
Cada entrada contiene el identificador del primer sucesor a una distancia de al menos $2^{i-1}$ unidades.
Cada nodo sólo guarda una cantidad reducida de información de los demás.
Notar que $F_p[1]$ es el sucesor del nodo en el anillo.
Ahora, cuando un nodo $p$ recibe una consulta por una entidad con clave $k$:
- Responde la consulta si $k \in (pred(p), p]$
- Si $p < k \leq F_p[1]$ reenvia la consulta a su sucesor
- Caso contrario, reenvia la consulta al nodo $i$ tal que $F_p[i] \leq k < F_p[i+1]$
- Osea, $k \in (,]$
El costo en general sera $O(log(N))$
Ejemplo #
Notar que no tienen que existir $2^m$ nodos: pueden haber muchos menos en el anillo.
Por ejemplo, para $F_p(1)$, $succ(2) = 4$ ya que el nodo 4 es responsable de las claves 2, 3 y 4.
Topología dinámica #
Los nodos pueden entrar o salir del anillo, voluntariamente o por un fallo
Un nodo $p$ que quiera sumarse a una anillo Chord debe:
- Consulta $succ(p+1)$ a alguno de los nodos existentes
- Luego se agrega como predcesor de $succ(p+1)$
Se tienen que actualizar las tablas finger
- Periódicamente mediante algun hilo o proceso en segundo plano
Para la primer entrada ($F_p[1]$, o sea el sucesor):
- Periodicamente consulta si sigue siendo el predecesor de su sucesor.
- Osea, el nodo $p$ verifica si $p=pred(succ(p+1))$
- Si no lo es, entonces se sumo un nuevo nodo $q$
- Repite el proceso con $q$ como sucesor.
Para el resto de las entradas, debe encontrar el $succ(p + 2^{i-1})$
De manera similar, cada nodo debe verificar su predecesor:
- Si un nodo $q$ detecta que su predecesor no esta presente, lo marca como desconocido
- Si el nodo $q$ detecta que su sucesor no conoce a su predecesor, se presenta como tal
- De la misma manera, un nuevo predecesor se presentará como tal a $q$
Proximidad #
Un problema potencial es que dos nodos lógicamente próximos pueden estar físicamente lejanos
- Nodo 19 en Madryn, nodo 20 en Tokio, nodo 21 en Madrid
Posibles soluciones:
Asignar los identificadores a nodos en base a la topología
- Nodos físicamente cercanos tendrán identificadores próximos
Ruteo por proximidad
- Se mantienen varios sucesores por cada entrada de la tabla finger
Selección del vecino más cercano
- Válida cuando puede haber varios nodos que sirvan de predecesor.
- No es el caso de Chord, pero sí de Pastry.