Está en la página 1de 87

Machine Translated by Google

Sistemas distribuidos
Universidad de Cambridge
Tripos de Ciencias de la Computación, Parte IB
Período de San Miguel 2020/21

Página web del curso: https://www.cst.cam.ac.uk/teaching/2021/ConcDisSys

Vídeos de conferencias: https://www.youtube.com/playlist?list=PLeKd45zvjcDFUEv ohr HdUFe97RItdiB

Dr. Martin Kleppmann


mk428@cst.cam.ac.uk

1. Introducción
Este curso de 8 lecciones sobre sistemas distribuidos constituye la segunda mitad de Sistemas concurrentes y distribuidos. Mientras que la
primera mitad se centró en la concurrencia entre múltiples procesos o subprocesos que se ejecutan en la misma computadora, esta segunda
mitad va más allá al examinar los sistemas que consisten en múltiples computadoras que se comunican.

La simultaneidad en una sola computadora también se conoce como simultaneidad de memoria compartida, ya que varios subprocesos que
se ejecutan en el mismo proceso tienen acceso al mismo espacio de direcciones. Por lo tanto, los datos se pueden pasar fácilmente de un hilo a
otro: una variable o puntero que es válido para un hilo también es válido para otro.
Esta situación cambia cuando pasamos a sistemas distribuidos. Todavía tenemos concurrencia en un sistema distribuido, ya que diferentes
computadoras pueden ejecutar programas en paralelo. Sin embargo, normalmente no tenemos memoria compartida, ya que cada computadora
en un sistema distribuido ejecuta su propio sistema operativo con su propio espacio de direcciones, utilizando la memoria integrada en esa
computadora. Las diferentes computadoras solo pueden comunicarse enviándose mensajes a través de una red.

(Existen formas limitadas de memoria compartida distribuida en algunas supercomputadoras y sistemas de investigación, y existen
tecnologías como el acceso directo a memoria remota (RDMA) que permiten que las computadoras accedan a la memoria de otras a través de
una red. Además, las bases de datos pueden, en cierto sentido, considerarse como memoria compartida, pero con un modelo de datos diferente
en comparación con la memoria direccionable por bytes. Sin embargo, en términos generales, la mayoría de los sistemas distribuidos prácticos
se basan en el paso de mensajes).
Cada una de las computadoras en un sistema distribuido se llama nodo. Aquí, "computadora" se interpreta de manera bastante amplia: los
nodos pueden ser computadoras de escritorio, servidores en centros de datos, dispositivos móviles, automóviles conectados a Internet, sistemas
de control industrial, sensores o muchos otros tipos de dispositivos. En este curso no los distinguimos: un nodo puede ser cualquier tipo de
dispositivo informático comunicante.

Comienzo del video sección 1.1

Un sistema distribuido es. . . (descarga mp4)

yo “. . . un sistema en el que la falla de una computadora


ni siquiera sabía que existían pueden hacer que su propia computadora
quede inutilizable”. — Leslie Lamport

I . . . varios ordenadores comunicándose a través de una


I red. . . . . . tratando de lograr alguna tarea juntos I Consiste en
"nodos" (computadora, teléfono, automóvil, robot, . . . )

Diapositiva 1

Este trabajo se publica bajo una licencia Creative Commons BY-SA.


Machine Translated by Google

1.1 Acerca de los sistemas distribuidos


Estas notas y las grabaciones de la conferencia deben ser independientes, pero si desea leer más detalles, hay varios libros de texto
sugeridos:

• Maarten van Steen y Andrew S. Tanenbaum. Sistemas distribuidos. ISBN 978-1543057386. Descarga gratuita desde https://
www.distributed-systems.net/index.php/books/ds3/ (tercera edición, 2017).

Este libro ofrece una amplia visión general de una gran variedad de temas de sistemas distribuidos, con muchos ejemplos de
sistemas prácticos.

• Christian Cachin, Rachid Guerraoui y Lu´ÿs Rodrigues. Introducción a Confiable y Seguro


Programación Distribuida. Segunda edición, Springer, 2011. ISBN 978-3-642-15259-7.

Descarga de libros electrónicos para usuarios de Cambridge: https://link.springer.com/book/10.1007/978-3-642-15260-3 luego


haga clic en Iniciar sesión ÿ a través de Shibboleth ÿ escriba Universidad de Cambridge ÿ inicie sesión con Raven.

Este libro es más avanzado, profundiza en varios algoritmos distribuidos importantes y demuestra su corrección. Recomendado si
desea explorar la teoría con mayor profundidad que la que cubre este curso.

• Martín Kleppmann. Diseño de aplicaciones intensivas en datos, O'Reilly, 2017. ISBN 978-1449373320.

Este libro va más en la dirección de las bases de datos, pero también cubre una serie de temas de sistemas distribuidos. Está
diseñado para ingenieros de software en la industria que trabajan con bases de datos distribuidas.

• Jean Bacon y Tim Harris. Sistemas Operativos: Diseño de Software Concurrente y Distribuido.
Addison-Wesley, 2003. ISBN 978-0321117892.

Este libro proporciona un enlace a la mitad del curso sobre sistemas concurrentes ya los temas de sistemas operativos.

En su caso, estas notas de clase también contienen referencias a trabajos de investigación y otras lecturas de referencia útiles (estos
se dan entre corchetes, y los detalles aparecen al final de este documento). Sin embargo, solo se puede examinar el material cubierto en
las notas de clase y los videos.

Lectura recomendada

Van Steen y Tanenbaum .


“Sistemas
distribuidos” (cualquier edición),
libro electrónico gratuito disponible I Cachin, Guerraoui & Rodrigues.
“Introducción a la programación distribuida confiable y
segura” (2.ª ed.), Springer 2011 I Kleppmann.

“Diseño de aplicaciones intensivas en datos”,


O'Reilly 2017
y Bacon & Harris.
“Sistemas Operativos: Concurrentes y Distribuidos
Diseño de software”, Addison-Wesley 2003

Diapositiva 2

El programa de estudios, las diapositivas y las notas de clase de este curso se han revisado sustancialmente para 2020/21.
Esto significa que es posible que se hayan introducido nuevos errores. Si nota algo incorrecto o si algo no está claro, ¡hágamelo saber por
correo electrónico (mk428@cst.cam.ac.uk)!
En cuanto a otros cursos, las preguntas de exámenes anteriores están disponibles en https://www.cl.cam.ac.uk/teaching/exams/
pastpapers/t-ConcurrentandDistributedSystems.html . Debido a cambios en el plan de estudios, las siguientes preguntas de exámenes
anteriores ya no son aplicables: 2018 P5 Q8; 2015 P5 Q8; 2014 P5 Q9 (a); 2013 P5 Q9; 2011 P5 Q8 (b).
Estas notas también contienen ejercicios, que son material sugerido para la discusión en las supervisiones. Solu
Las notas informativas para supervisores están disponibles en la página web del curso.
Este curso está relacionado con varios otros cursos en los tripos, como se muestra en la diapositiva 3.

2
Machine Translated by Google

Relaciones con otros cursos

I Sistemas Concurrentes – Parte IB (todo


sistema distribuido también es concurrente)

I Sistemas operativos - Parte IA


(comunicación entre procesos, programación)
I Bases de datos – Parte IA

(se distribuyen muchas bases de datos modernas)

I Redes informáticas – Parte IB Cuaresma


(los sistemas distribuidos involucran comunicación en red)
I Más allá de Java - Parte IB Michaelmas

(ejercicios prácticos de programación distribuida)

I Seguridad – Término de Semana Santa de


la Parte IB (protocolos de red con encriptación y autenticación)

I Cloud Computing – Parte II (sistemas


distribuidos para procesar grandes cantidades de datos)
Diapositiva 3

Hay una serie de razones para crear sistemas distribuidos. Algunas aplicaciones están intrínsecamente distribuidas: si desea
enviar un mensaje desde su teléfono al teléfono de un amigo, esa operación inevitablemente requiere que esos teléfonos se
comuniquen a través de algún tipo de red.
Algunos sistemas distribuidos hacen cosas que en principio podría hacer una sola computadora, pero lo hacen de manera más
confiable. Una sola computadora puede fallar y es posible que deba reiniciarse de vez en cuando, pero si está utilizando varios
nodos, entonces un nodo puede continuar sirviendo a los usuarios mientras otro nodo se reinicia.
Por lo tanto, un sistema distribuido tiene el potencial de ser más confiable que una sola computadora, al menos si está bien diseñado
(¡contradiciendo un poco la cita de Lamport en la diapositiva 1)!
Otra razón para la distribución es un mejor rendimiento: si un servicio tiene usuarios en todo el mundo y todos tienen que
acceder a un solo nodo, los usuarios del Reino Unido o los usuarios de Nueva Zelanda lo encontrarán lento (o ambas cosas). Al
colocar nodos en múltiples ubicaciones alrededor del mundo, podemos sortear la lentitud de la velocidad de la luz al enrutar a cada
usuario a un nodo cercano.
Finalmente, algunas tareas informáticas o de procesamiento de datos a gran escala son simplemente demasiado grandes para
realizarlas en una sola computadora, o serían intolerablemente lentas. Por ejemplo, el Gran Colisionador de Hadrones del CERN
está respaldado por una infraestructura informática mundial con 1 millón de núcleos de CPU para el análisis de datos y 1 exabyte
(1018 bytes) de almacenamiento. Consulte https://wlcg-public.web.cern.ch/.

¿Por qué hacer un sistema distribuido?

I Se distribuye inherentemente: por


ejemplo, enviar un mensaje desde su teléfono móvil al teléfono de su
amigo I Para mayor confiabilidad:

incluso si un nodo falla, el sistema en su conjunto sigue funcionando

I Para un mejor rendimiento: obtenga


datos de un nodo cercano en lugar de uno al otro lado del mundo

I Para resolver problemas más


grandes: por ejemplo, grandes cantidades de datos, no caben en una máquina

Diapositiva 4

Sin embargo, también hay desventajas en los sistemas distribuidos, porque las cosas pueden salir mal y el sistema necesita
lidiar con tales fallas. La red puede fallar, dejando a los nodos incapaces de comunicarse.

3
Machine Translated by Google

Diapositiva 5

Otra cosa que puede salir mal es que un nodo se bloquee, funcione mucho más lento de lo normal o se comporte mal
de alguna otra manera (quizás debido a un error de software o una falla de hardware). Si queremos que un nodo se haga
cargo cuando otro nodo falla, debemos detectar que se ha producido un bloqueo; como veremos, incluso eso no es
sencillo. Las fallas de la red y las fallas de los nodos pueden ocurrir en cualquier momento, sin previo aviso.

En una sola computadora, si un componente falla (p. ej., uno de los módulos de RAM presenta una falla), normalmente
no esperamos que la computadora continúe funcionando de todos modos: probablemente se bloquee. El software no
necesita estar escrito de una manera que trate explícitamente con RAM defectuosa. Sin embargo, en un sistema distribuido
a menudo queremos tolerar que algunas partes del sistema se rompan y que el resto siga funcionando. Por ejemplo, si un
nodo se bloqueó (una falla parcial), los nodos restantes aún pueden continuar brindando el servicio.

Si un componente de un sistema deja de funcionar, lo llamamos falla, y muchos sistemas distribuidos se esfuerzan por
proporcionar tolerancia a fallas: es decir, el sistema como un todo continúa funcionando a pesar de la falla. Tratar con
fallas es lo que hace que la computación distribuida sea fundamentalmente diferente, y a menudo más difícil, en
comparación con la programación de una sola computadora. Algunos ingenieros de sistemas distribuidos creen que si
puede resolver un problema en una sola computadora, ¡es básicamente fácil! Aunque, para ser justos con nuestros colegas
en otras áreas de la informática, esto probablemente no sea cierto.

¿Por qué NO hacer un sistema distribuido?

El problema con los sistemas distribuidos:

I La comunicación puede fallar (y es posible que ni siquiera sepamos que ha fallado).

Los procesos pueden fallar (y es posible que no lo sepamos).

Todo esto puede ocurrir de forma no determinista.

Tolerancia a fallos: queremos que el sistema en su conjunto siga funcionando,


incluso cuando algunas partes estén defectuosas.

Esto es duro.

¿Escribir un programa para que se ejecute en una sola computadora


es comparativamente fácil?

Diapositiva 6

1.2 Sistemas distribuidos y redes de computadoras


Cuando estudiamos sistemas distribuidos, solemos trabajar con una abstracción de alto nivel del hardware.

4
Machine Translated by Google

Comienzo del video sección


Sistemas Distribuidos y Redes de Computadores 1.2 (descarga mp4)

Usamos una simple abstracción de comunicación:

mensaje m
nodo i nodo j

La realidad es mucho más compleja:


I Varios operadores de red: eduroam,
DSL doméstico, datos móviles, cafetería wifi, cable submarino,
satélite. . .

I Comunicación física: corriente


eléctrica, ondas de radio, láser, discos duros en una furgoneta. . .

Diapositiva 7

En este curso, simplemente asumimos que hay alguna forma de que un nodo envíe un mensaje a otro nodo.
No nos importa especialmente cómo se representa o codifica físicamente ese mensaje (los protocolos de red, conocidos
informalmente como los bytes en el cable), porque el principio básico de enviar y recibir mensajes sigue siendo el mismo,
incluso cuando las tecnologías de red particulares van y vienen. El "cable" en realidad puede ser ondas de radio, láseres,
una memoria USB en el bolsillo de alguien o incluso discos duros en una camioneta.

¿Discos duros en una furgoneta?

https://docs.aws.amazon.com/snowball/latest/ug/using-device.html

¡Alta latencia, alto ancho de banda!

Diapositiva 8

De hecho, si desea enviar un mensaje muy grande (piense en decenas de terabytes), sería lento enviar esos datos a
través de Internet y, de hecho, es más rápido escribir esos datos en un montón de discos duros, cargarlos en una furgoneta
y llevarlos a su destino. Pero desde el punto de vista de los sistemas distribuidos, el método de entrega del mensaje no es
importante: solo vemos un canal de comunicación abstracto con cierta latencia (retraso desde que se envía un mensaje
hasta que se recibe) y ancho de banda (el volumen de datos que se pueden transferir por unidad de tiempo).

El curso de redes informáticas en el período de Cuaresma se centra en los protocolos de red que permiten que los
mensajes lleguen a su destino. El estudio de los sistemas distribuidos se basa en esa instalación y, en cambio, se centra
en cómo varios nodos deben coordinarse para lograr alguna tarea compartida. El diseño de algoritmos distribuidos consiste
en decidir qué mensajes enviar y cómo procesar los mensajes cuando se reciben.

5
Machine Translated by Google

Latencia y ancho de banda

Latencia: tiempo hasta que llega el mensaje


I En el mismo edificio/centro de datos: ÿ 1 ms
I De un continente a otro: ÿ 100 ms

I Discos duros en furgoneta: ÿ 1 día

Ancho de banda: volumen de datos por unidad de tiempo

I Datos móviles 3G: ÿ 1 Mbit/s


I Banda ancha doméstica: ÿ 10 Mbit/s
I Discos duros en furgoneta: 50 TB/caja ÿ 1 Gbit/s

(Números muy aproximados, ¡varían enormemente en la práctica!)

Diapositiva 9

La web es un ejemplo de un sistema distribuido que usa todos los días.

Diapositiva 10

Ejemplo cliente-servidor: la web

El tiempo fluye de arriba hacia abajo.

cliente servidor www.cst.cam.ac.uk

OBTENER

/docencia/2021/ConcDisSys

<!DOCTYPEhtml><html>...

Diapositiva 11

En la web hay dos tipos principales de nodos: los servidores alojan sitios web y los clientes (navegadores web) los
muestran. Cuando carga una página web, su navegador web envía un mensaje de solicitud HTTP al servidor apropiado.
Al recibir esa solicitud, el servidor web envía un mensaje de respuesta que contiene la

6
Machine Translated by Google

contenido de la página al cliente que lo solicitó. Estos mensajes normalmente son invisibles, pero podemos capturar y
visualizar el tráfico de la red con una herramienta como Charles (https://www.charlesproxy.com/), se muestra en la
diapositiva 12. El video de la conferencia incluye una demostración de este software en acción.

mensaje de solicitud mensaje de respuesta


Diapositiva 12

En una URL, la parte entre // y la siguiente / es el nombre de host del servidor al que el cliente va a enviar la solicitud
(por ejemplo, www.cst.cam.ac.uk), y el resto (por ejemplo, /enseñanza /2021/ConcDisSys) es la ruta que el cliente solicita
en su mensaje de solicitud. Además de la ruta, la solicitud también contiene información adicional, como el método HTTP
(por ejemplo, GET para cargar una página o POST para enviar un formulario), la versión del software del cliente (el agente
de usuario) y una lista de formatos de archivo que el cliente entiende (el encabezado de aceptación). El mensaje de
respuesta contiene el archivo que se solicitó y un indicador de su formato de archivo (el tipo de contenido); en el caso de
una página web, puede ser un documento HTML, una imagen, un video, un documento PDF o cualquier otro tipo de archivo.

Dado que las solicitudes y las respuestas pueden ser más grandes de lo que cabe en un solo paquete de red, el
protocolo HTTP se ejecuta sobre TCP, lo que descompone una gran parte de los datos en un flujo de pequeños paquetes
de red (consulte la diapositiva 13) y coloca volver a juntarlos en el destinatario. HTTP también permite enviar múltiples
solicitudes y múltiples respuestas a través de una única conexión TCP. Sin embargo, al observar este protocolo desde el
punto de vista de los sistemas distribuidos, este detalle no es importante: tratamos la solicitud como un mensaje y la
respuesta como otro mensaje, independientemente de la cantidad de paquetes de red físicos involucrados en su
transmisión. Esto mantiene las cosas independientes de la tecnología de red subyacente.

Diapositiva 13

7
Machine Translated by Google

1.3 Ejemplo: llamadas a procedimiento remoto (RPC)


Otro ejemplo de un sistema distribuido cotidiano es cuando compra algo en línea con una tarjeta de crédito/débito. Cuando
ingresa su número de tarjeta en alguna tienda en línea, esa tienda enviará una solicitud de pago a través de Internet a un
servicio especializado en procesar pagos con tarjeta.

Comienzo del video sección


Ejemplo cliente-servidor: pagos en línea 1.3 (descarga mp4)

Tienda online servicio de pagos

Diapositiva 14

El servicio de pagos a su vez se comunica con una red de tarjetas como Visa o MasterCard, que
se comunica con el banco que emitió su tarjeta para tomar el pago.
Para los programadores que están implementando la tienda online, el código para procesar el pago
puede parecerse al código de la diapositiva 15.

Ejemplo de llamada a procedimiento remoto (RPC)

// Tienda en línea que maneja los detalles de la tarjeta del cliente


Card card = new Card(); tarjeta.establecerNúmeroTarjeta("1234
5678 8765 4321"); tarjeta.setExpiryDate("10/2024");
tarjeta.setCVC("123");

Resultado resultado = pagoServicio.procesarPago(tarjeta,


3,99, Moneda.GBP);

si (resultado.esÉxito()) {
cumplirPedido();
}

¡La implementación de esta función está en otro nodo!


Diapositiva 15

Llamar a la función procesarPago parece llamar a cualquier otra función, pero de hecho, lo que sucede detrás de
escena es que la tienda envía una solicitud al servicio de pagos, espera una respuesta y luego devuelve la respuesta que
recibió. La implementación real de processPayment, la lógica que se comunica con la red de tarjetas y los bancos, no
existe en el código de la tienda: es parte del servicio de pagos, que es otro programa que se ejecuta en otro nodo que
pertenece a una empresa diferente.

Este tipo de interacción, donde el código en un nodo parece llamar a una función en otro nodo, se denomina Llamada
a procedimiento remoto (RPC). En Java, se llama Invocación de método remoto (RMI). El software que implementa RPC
se denomina framework o middleware de RPC. (No todo el middleware se basa en RPC; también hay middleware que
utiliza diferentes modelos de comunicación).
Cuando una aplicación desea llamar a una función en otro nodo, el marco RPC proporciona un código auxiliar en su
lugar. El stub tiene el mismo tipo de firma que la función real, pero en lugar de ejecutar la función real, codifica los
argumentos de la función en un mensaje y envía ese mensaje al control remoto.

8
Machine Translated by Google

nodo, solicitando que se llame a esa función. El proceso de codificación de los argumentos de la función se conoce como
clasificación. En el ejemplo de la diapositiva 16, se usa una codificación JSON para ordenar, pero en la práctica también se
usan otros formatos.

tienda online cliente RPC Servicio de pago del servidor RPC

talón de procesoPago()
m1
argumentos del mariscal
desarmar argumentos

implementación de
esperando
procesoPago()
m2
resultado del mariscal
resultado desorganizado

función devuelve

{
"solicitud": "procesarPago", "tarjeta":
{ "número": "1234567887654321",
"fecha de caducidad": "10/2024", {
"resultado": "éxito",
"CVC": "123" "id": "XP61hHw2Rvo"
m1 = m2 = }
},
"cantidad": 3,99,
"moneda": "GBP"
}

Diapositiva 16

El envío del mensaje desde el cliente RPC al servidor RPC puede realizarse a través de HTTP (en cuyo caso, esto
también se denomina servicio web), o se puede utilizar uno de los diferentes protocolos de red. En el lado del servidor, el
marco RPC desarma (descodifica) el mensaje y llama a la función deseada con los argumentos proporcionados. Cuando la
función regresa, sucede lo mismo a la inversa: el valor de retorno de la función se clasifica, se envía como un mensaje al
cliente, el cliente lo desclasifica y el stub lo devuelve. Por lo tanto, para la persona que llama al stub, parece como si la función
se hubiera ejecutado localmente.

Llamada a procedimiento remoto (RPC)

Idealmente, RPC hace que una llamada a una función remota tenga el mismo aspecto
que una llamada a una función local.

“Transparencia de ubicación”: el
sistema oculta dónde se encuentra un recurso.

En la práctica. . .

¿Qué sucede si el servicio se bloquea durante la llamada a la función?

¿ Qué pasa si se pierde un mensaje?

¿ Qué pasa si un mensaje se retrasa?

Si algo sale mal, ¿es seguro volver a intentarlo?

Diapositiva 17

Ejercicio 1. Las redes y los nodos pueden fallar. ¿Cuáles son las implicaciones para el código que llama a otro nodo a través
de RPC? ¿En qué se diferencia RPC de una llamada de función local? ¿Se puede lograr la transparencia de la ubicación?

A lo largo de las décadas, se han desarrollado muchas variantes de RPC, con el objetivo de facilitar la programación de
sistemas distribuidos. Esto incluye middleware orientado a objetos como CORBA en la década de 1990.
Sin embargo, los desafíos subyacentes de los sistemas distribuidos siguen siendo los mismos [Waldo et al., 1994].

9
Machine Translated by Google

historia de RPC

I SunRPC/ONC RPC (década de 1980, base para NFS)


I CORBA: middleware orientado a objetos, de moda en la década de 1990

I DCOM de Microsoft y Java RMI (similar a CORBA)


I SOAP/XML-RPC: RPC usando XML y HTTP (1998)
Ahorro (Facebook, 2007 )
Yo gRPC (Google, 2015)
DESCANSO (a menudo con JSON)
Yo Ajax en navegadores web

Diapositiva 18

Hoy en día, la forma más común de RPC se implementa utilizando datos JSON enviados a través de HTTP. Un conjunto
popular de principios de diseño para dichas API basadas en HTTP se conoce como transferencia de estado representacional
o REST [Fielding, 2000], y las API que se adhieren a estos principios se denominan RESTful. Estos principios incluyen:

• la comunicación no tiene estado (cada solicitud es autónoma e independiente de otras solicitudes),

• los recursos (objetos que pueden ser inspeccionados y manipulados) están representados por URL, y

• el estado de un recurso se actualiza al realizar una solicitud HTTP con un tipo de método estándar, como
como POST o PUT, a la URL apropiada.

La popularidad de REST se debe al hecho de que el código JavaScript que se ejecuta en un navegador web puede realizar
fácilmente este tipo de solicitud HTTP, como se muestra en la diapositiva 19. En los sitios web modernos, es muy común
usar JavaScript para realizar solicitudes HTTP a un servidor sin recargar toda la página. Esta técnica a veces se conoce
como Ajax.

RPC/RESTO en JavaScript

let args = {cantidad: 3,99, moneda: 'GBP', /*...*/ }; dejar solicitud = {

método: 'POST', cuerpo:


encabezados:JSON.stringify(argumentos),
{'Content-
Type': 'application/json'} };

fetch('https://example.com/pagos', solicitud) .then((respuesta) => {

if (respuesta.ok) éxito (respuesta.json()); otra falla


(respuesta.estado); // error del servidor }) .catch((error) =>
{ falla(error); // error de red });

Diapositiva 19

El código de la diapositiva 19 toma los argumentos args, los clasifica en JSON mediante JSON.stringify() y luego los
envía a la URL https://example.com/pagos mediante una solicitud HTTP POST. Hay tres resultados posibles: o el servidor
devuelve un código de estado que indica el éxito (en cuyo caso desarmamos la respuesta usando response.json()), o el
servidor devuelve un código de estado que indica un error, o la solicitud falla porque no se recibió ninguna respuesta.
recibida del servidor (probablemente debido a una interrupción de la red). El código llama a la función Success() o Failure()
en cada uno de estos casos.
Aunque las API RESTful y el RPC basado en HTTP se originaron en la web (donde el cliente es JavaScript ejecutándose
en un navegador web), ahora también se usan comúnmente con otros tipos de clientes (por ejemplo, aplicaciones móviles)
o para servidores de servidor a servidor. comunicación.

10
Machine Translated by Google

RPC en sistemas empresariales

“Arquitectura orientada a servicios” (SOA) / “microservicios”:

dividir una gran aplicación de software en múltiples servicios (en


múltiples nodos) que se comunican a través de RPC.

Diferentes servicios implementados en diferentes idiomas:


I interoperabilidad: conversiones de tipos de datos

I Lenguaje de definición de interfaz (IDL):


especificación de API independiente del idioma

Diapositiva 20

Tal RPC de servidor a servidor es especialmente común en grandes empresas, cuyos sistemas de software son
demasiado grandes y complejos para ejecutarse en un solo proceso en una sola máquina. Para gestionar esta
complejidad, el sistema se descompone en múltiples servicios, que son desarrollados y administrados por diferentes
equipos y que incluso pueden implementarse en diferentes lenguajes de programación. Los marcos RPC facilitan la
comunicación entre estos servicios.
Cuando se utilizan diferentes lenguajes de programación, el marco RPC necesita convertir los tipos de datos de
modo que el código que se llama entienda los argumentos de la persona que llama y, de la misma manera, el valor
de retorno de la función. Una solución típica es utilizar un lenguaje de definición de interfaz (IDL) para proporcionar
firmas de tipos independientes del idioma de las funciones que están disponibles en RPC. A partir del IDL, los
desarrolladores de software pueden generar automáticamente código de clasificación/desclasificación y stubs RPC
para los respectivos lenguajes de programación de cada servicio y sus clientes. La diapositiva 21 muestra un ejemplo
del IDL utilizado por gRPC, denominados búferes de protocolo. Los detalles del idioma no son importantes para este curso.

Ejemplo de IDL de gRPC

mensaje PaymentRequest
{ mensaje Tarjeta { cadena
requerida cardNumber = 1; opcional int32
expiraciónMes = 2; opcional int32
expirationYear = 3; opcional int32 CVC = 4; }
enum Moneda { GBP = 1; USD = 2; }

tarjeta requerida
Tarjeta requerida = 1; cantidad
de int64 = 2; Moneda requerida
3; } Moneda =

mensaje estado de pago {


éxito bool requerido = 1; mensaje de error de
cadena opcional = 2;
}

servicio PaymentService { rpc


ProcessPayment(PaymentRequest) devuelve (PaymentStatus) {} }

Diapositiva 21

2 Modelos de sistemas distribuidos


Un modelo de sistema captura nuestras suposiciones sobre cómo se comportan los nodos y la red. Es una descripción
abstracta de sus propiedades, que puede ser implementada por varias tecnologías en la práctica. Para ilustrar los
modelos de sistemas comunes, comenzaremos esta sección analizando dos experimentos mentales clásicos en
sistemas distribuidos: el problema de los dos generales y el problema de los generales bizantinos.

11
Machine Translated by Google

2.1 El problema de los dos generales

En el problema de los dos generales [Gray, 1978], imaginamos dos generales, cada uno al frente de un ejército, que quieren
capturar una ciudad. (Disculpas por la analogía militarista, ¡no se me ocurrió!) Las defensas de la ciudad
son fuertes, y si solo uno de los dos ejércitos ataca, el ejército será derrotado. Sin embargo, si ambos ejércitos
ataque al mismo tiempo, capturarán con éxito la ciudad.

Comienzo del video sección 2.1


El problema de los dos generales (descarga mp4)

ciudad

¿ataque? ¿ataque?

ejercito 1 ejercito 2
mensajeros

ejercito 1 ejercito 2 Salir

no ataca no ataca no pasa nada


ataques no ataca ejercito 1 derrotado
no ataca ataques ejército 2 derrotado
ataques ataques ciudad capturada

Deseada: el ejército 1 ataca si y solo si el ejército 2 ataca

Diapositiva 22

Por lo tanto, los dos generales deben coordinar su plan de ataque. Esto se dificulta por el hecho de que
los dos ejércitos están acampados a cierta distancia uno del otro y solo pueden comunicarse por medio de mensajeros. los
los mensajeros deben atravesar el territorio controlado por la ciudad, por lo que a veces son capturados.
Así, un mensaje enviado por un general puede o no ser recibido por el otro general, y el remitente
no sabe si su mensaje llegó, excepto al recibir una respuesta explícita del otro
fiesta. Si un general no recibe ningún mensaje, es imposible saber si es porque el otro
general no envió ningún mensaje, o porque todos los mensajeros fueron capturados.

El problema de los dos generales

generales 1 generales 2

ataque el 10 de noviembre, ¿de acuerdo?

10 de noviembre de acuerdo!

Desde el punto de vista del general 1, esto es indistinguible de:

generales 1 generales 2

Diapositiva 23

¿Qué protocolo deben usar los dos generales para acordar un plan? Para cada general hay dos opciones:
o el general promete seguir adelante con el ataque en cualquier caso (incluso si no se recibe respuesta), o
el general espera un reconocimiento antes de comprometerse a atacar. En el primer caso, el general
quien promete seguir adelante corre el riesgo de quedarse solo en el ataque. En el segundo caso, el general que espera
reconocimiento traslada el problema al otro general, que ahora debe decidir si se compromete a
atacar (y correr el riesgo de quedarse solo) o esperar un reconocimiento del reconocimiento.

12
Machine Translated by Google

¿Cómo deberían decidir los generales?

1. ¿General 1 siempre ataca, incluso si no recibe respuesta?


I Enviar muchos mensajeros para aumentar la probabilidad de que
uno pase I Si todos son capturados, el general 2 no sabe sobre el
ataque, por lo que el general 1 pierde

2. ¿El general 1 solo ataca si recibe una respuesta positiva del


general 2?
Ahora el general 1 es seguro
I Pero el general 2 sabe que el general 1 solo atacará si la respuesta
del general 2 llega.
I Ahora el general 2 está en la misma situación que el general 1 en
Opción 1

Sin conocimiento común: la única manera de saber algo


es comunicándolo
Diapositiva 24

El problema es que no importa cuántos mensajes se intercambien, ninguno de los generales puede estar
seguro de que el otro ejército también aparecerá al mismo tiempo. Una secuencia repetida de acuses de recibo de
ida y vuelta puede aumentar gradualmente la confianza de que los generales están de acuerdo, pero se puede
probar que no pueden alcanzar la certeza intercambiando un número finito de mensajes.
Este experimento mental demuestra que en un sistema distribuido, no hay forma de que un nodo tenga certeza
sobre el estado de otro nodo. La única forma en que un nodo puede saber algo es comunicando ese conocimiento
en un mensaje. En una nota filosófica, esto es quizás similar a la comunicación entre humanos: no tenemos
telepatía, por lo que la única forma de que otra persona sepa lo que estás pensando es comunicándolo (a través
del habla, la escritura, el lenguaje corporal, etc.).
Como ejemplo práctico del problema de los dos generales, la diapositiva 25 adapta el modelo de la diapositiva
22 a la aplicación del pago de bienes en una tienda online. La tienda y el servicio de procesamiento de pagos con
tarjeta de crédito se comunican por RPC y algunos de estos mensajes pueden perderse. Sin embargo, la tienda
quiere asegurarse de que envía los productos solo si se pagan, y solo carga la tarjeta del cliente si se envían los
productos.

El problema de los dos generales aplicado

cliente

despachar mercancías cargar tarjeta de crédito

Tienda online servicio de pagos


RPC

Tienda online servicio de pagos Salir

no despacha no cobra despacha no pasa nada no


cobra tienda pierde dinero
no despacha cargos queja de un cliente
despachos cargos todo el mundo feliz

Deseada: la tienda en línea se despacha si y solo si se realizó el pago


Diapositiva 25

En la práctica, el ejemplo de la compra online no coincide exactamente con el problema de los dos generales:
en este escenario, es seguro que el servicio de pagos siga adelante con un pago, porque si la tienda no puede
despachar la mercancía, puede reembolsar el pago. El hecho de que un pago sea algo que se puede deshacer (a
diferencia de un ejército derrotado) hace que el problema sea solucionable. Si se interrumpe la comunicación entre
la tienda y el servicio de pago, la tienda puede esperar hasta que se restablezca la conexión y luego consultar el
servicio de pago para averiguar el estado de cualquier transacción cuyo resultado se desconociera.

13
Machine Translated by Google

2.2 El problema de los generales bizantinos El problema de

los generales bizantinos [Lamport et al., 1982] tiene un escenario similar al problema de los dos generales.
Nuevamente tenemos ejércitos queriendo capturar una ciudad, aunque en este caso pueden ser tres o más. Nuevamente
los generales se comunican por medio de mensajeros, aunque esta vez asumimos que si se envía un mensaje, siempre se
entrega correctamente.

Comienzo del video sección


El problema de los generales bizantinos 2.2 (descarga mp4)

ejercito 3

¿ataque?

mensajeros mensajeros
ciudad

¿ataque? ¿ataque?

ejercito 1 ejercito 2
mensajeros

Problema: algunos de los generales podrían ser traidores

Diapositiva 26

El desafío en el entorno bizantino es que algunos generales pueden ser "traidores": es decir, pueden tratar de engañar
y confundir deliberada y maliciosamente a los otros generales. Un ejemplo de tal comportamiento malicioso se muestra en
la diapositiva 27: aquí, el general 3 recibe dos mensajes contradictorios de los generales 1 y 2. El general 1 le dice al
general 3 que ataque, mientras que el general 2 afirma que el general 1 ordenó la retirada. Es imposible que el general 3
determine si el general 2 miente (el primer caso) o si el general 2 es honesto mientras que el general 1 emite órdenes
contradictorias (el segundo caso).

Generales que podrían mentir

generales 1 generales 2 generales 3


¡ataque!

¡ataque!
general
¡Dije retirada!

Desde el punto de vista del general 3, esto es indistinguible de:

generales 1 generales 2 generales 3


¡ataque!

¡retirada!
general
¡Dije retirada!

Diapositiva 27

Los generales honestos no saben quiénes son los generales maliciosos, pero los generales maliciosos pueden coludirse
y coordinar sus acciones en secreto. Incluso podríamos suponer que todos los generales maliciosos están controlados por
un adversario malvado. El problema de los generales bizantinos es entonces asegurarse de que todos los generales
honestos estén de acuerdo en el mismo plan (por ejemplo, si atacar o retirarse). Es imposible especificar qué van a hacer
los generales maliciosos, así que lo mejor que podemos hacer es lograr que los generales honestos estén de acuerdo.
Esto es difícil: de hecho, se puede probar que algunas variantes del problema pueden resolverse solo si estrictamente
menos de un tercio de los generales son maliciosos. Es decir, en un sistema con 3f + 1 generales, no más de f pueden ser
maliciosos. Por ejemplo, un sistema con 4 generales puede tolerar f = 1 general malicioso, y un sistema con 7 generales
puede tolerar f = 2.

14
Machine Translated by Google

El problema de los generales bizantinos

I Hasta f generales pueden comportarse maliciosamente

Los generales honestos no saben quiénes son los maliciosos.

I Los generales maliciosos pueden coludir

Sin embargo , los generales honestos deben ponerse de acuerdo sobre el plan

I Teorema: se necesitan 3f + 1 generales en total para tolerar f generales maliciosos

(es decir, < I La criptografía (firmas digitales)


1 3 puede
ayuda,
serpero
malicioso)
el problema

permanece duro

Diapositiva 28

El problema se simplifica un poco si los generales usan criptografía (firmas digitales) para probar quién dijo qué: por
ejemplo, esto permitiría al general 2 probar al general 3 cuál fue la orden del general 1 y así demostrar la honestidad del
general 2. No entraremos en los detalles de las firmas digitales en este curso, ya que se tratan en el curso de seguridad
(término de Pascua de la Parte IB). Sin embargo, incluso con las firmas, el problema de los generales bizantinos sigue
siendo un desafío.
¿Es el problema de los generales bizantinos de relevancia práctica? Los sistemas distribuidos reales a menudo
implican relaciones de confianza complejas. Por ejemplo, un cliente debe confiar en una tienda en línea para que
realmente le entregue los productos que solicitó, aunque puede disputar el pago a través de su banco si los productos
nunca llegan o si se les cobra demasiado. Pero si una tienda en línea de alguna manera permitiera a los clientes pedir
productos sin pagar por ellos, los estafadores sin duda explotarían esta debilidad, por lo que la tienda debe asumir que
los clientes son potencialmente maliciosos. Por otro lado, para RPC entre servicios pertenecientes a la tienda, que se
ejecutan en el mismo centro de datos, un servicio probablemente puede confiar en los otros servicios administrados por
la misma empresa. El servicio de pagos no confía plenamente en la tienda, ya que alguien podría configurar una tienda
fraudulenta o utilizar números de tarjetas de crédito robados, pero es probable que la tienda confíe en el servicio de pagos. Y así.
Y al final, queremos que el cliente, la tienda online y el servicio de pagos estén de acuerdo con cualquier pedido que se
realice. El problema de los generales bizantinos es una simplificación de relaciones de confianza tan complejas, pero es
un buen punto de partida para estudiar sistemas en los que algunos participantes pueden comportarse maliciosamente.

Relaciones de confianza y comportamiento malicioso

cliente

¿estar de acuerdo?

RPC RPC
pedido

¿estar de acuerdo? ¿estar de acuerdo?

Tienda online servicio de pagos


RPC

¿Quién puede confiar en quién?

Diapositiva 29

Antes de continuar, una breve digresión sobre el origen de la palabra “bizantino”. El término proviene del imperio
bizantino, llamado así por su ciudad capital, Bizancio o Constantinopla, que ahora es Estambul en Turquía. No hay
evidencia histórica de que los generales del imperio bizantino fueran más propensos a la intriga y la conspiración que
los de otros lugares. Más bien, la palabra bizantino se había usado en el sentido de "excesivamente complicado,
burocrático, tortuoso" mucho antes de que Leslie Lamport adoptara la palabra para describir el problema de los generales
bizantinos; la etimología exacta no está clara.

15
Machine Translated by Google

El imperio bizantino (650 EC)


Bizancio/Constantinopla/Estambul

Fuente: https://commons.wikimedia.org/wiki/File:Byzantiumby650AD.svg

"Bizantino" se ha utilizado durante mucho tiempo para "excesivamente complicado, burocrático,

tortuoso" (por ejemplo, "la ley fiscal bizantina")

Diapositiva 30

2.3 Descripción de los nodos y el comportamiento de la red


Al diseñar un algoritmo distribuido, un modelo de sistema es la forma en que especificamos nuestras suposiciones sobre qué fallas pueden ocurrir.

Comienzo del video sección


Modelos de sistema 2.3 (descarga mp4)

Hemos visto dos experimentos mentales:

I Problema de dos generales: un modelo de redes

I Problema de los generales bizantinos: un modelo de comportamiento de los nodos

¡En sistemas reales, tanto los nodos como las redes pueden estar defectuosos!

Capture suposiciones en un modelo de sistema que consiste en:

I Comportamiento de la red (por ejemplo, pérdida de mensajes)

Comportamiento del nodo I (por ejemplo, bloqueos)

Comportamiento de temporización (por ejemplo, latencia )

Elección de modelos para cada una de estas piezas.

Diapositiva 31

Las redes no son confiables

En el mar, los tiburones muerden cables de fibra óptica https://

slate.com/technology/2014/08/ shark-attacks-threaten-google-

s-undersea-internet-cables-video.html

En tierra, las vacas pisan los cables

https://twitter.com/uhoelzle/status/1263333283107991558

Diapositiva 32

dieciséis
Machine Translated by Google

Comencemos con la red. Ninguna red es perfectamente confiable: incluso en sistemas cuidadosamente diseñados con enlaces de
red redundantes, las cosas pueden salir mal [Bailis y Kingsbury, 2014]. Alguien podría desconectar accidentalmente el cable de red
equivocado. Se ha demostrado que tanto los tiburones como las vacas causan daños e interrupciones en las redes de larga distancia
(consulte los enlaces en la diapositiva 32). O una red puede sobrecargarse temporalmente, quizás por accidente o quizás debido a un
ataque de denegación de servicio. Cualquiera de estos puede hacer que los mensajes se pierdan.

En un modelo de sistema, adoptamos una visión más abstracta, lo que nos evita los detalles de preocuparnos por los tiburones y
las vacas. La mayoría de los algoritmos distribuidos asumen que la red proporciona un mensaje bidireccional que pasa entre un par de
nodos, también conocido como comunicación punto a punto o unidifusión. Las redes reales a veces permiten la comunicación de
difusión o multidifusión (enviar un paquete a muchos destinatarios al mismo tiempo, lo que se usa, por ejemplo, para descubrir una
impresora en una red local), pero en términos generales, asumir que solo unidifusión es un buen modelo de Internet. hoy dia. Más
adelante, en la lección 4, mostraremos cómo implementar la transmisión sobre la comunicación de unidifusión.

Luego podemos elegir qué tan confiables queremos asumir que son estos enlaces. La mayoría de los algoritmos asumen una de
las tres opciones enumeradas en la diapositiva 33.

Modelo del sistema: comportamiento de la red

Suponga una comunicación bidireccional punto a punto entre dos nodos, con uno de
los siguientes:

I Enlaces fiables (perfectos): Un


mensaje se recibe si y sólo si se envía.
Los mensajes pueden ser reordenados. reintentar +

deduplicación
Enlaces de pérdida justa :

Los mensajes pueden perderse, duplicarse o reordenarse.


Si sigue intentándolo, finalmente llegará un mensaje.
TLS
I Enlaces arbitrarios (adversario activo): un
adversario malicioso puede interferir con los mensajes (espiar, modificar,
descartar, falsificar, reproducir).

Partición de red: algunos enlaces descartan/retrasan todos los mensajes


durante un período prolongado de tiempo

Diapositiva 33

Curiosamente, es posible convertir unos tipos de enlace en otros. Por ejemplo, si tenemos un enlace de pérdida justa, podemos
convertirlo en un enlace confiable retransmitiendo continuamente los mensajes perdidos hasta que finalmente se reciben y filtrando los
mensajes duplicados en el lado del destinatario. La suposición de pérdida justa significa que cualquier partición de la red (interrupción
de la red) durará solo un período de tiempo finito, pero no para siempre, por lo que podemos garantizar que finalmente se recibirán
todos los mensajes.
Por supuesto, los mensajes enviados durante una partición de la red solo se recibirán después de que se repare la interrupción, lo
que puede llevar mucho tiempo, pero las definiciones de la diapositiva 33 no dicen nada sobre el retraso o la latencia de la red.
Llegaremos a ese tema en la diapositiva 35.
El protocolo TCP, que analizamos brevemente en la Sección 1.2, realiza este tipo de reintento y desduplicación a nivel de paquete
de red. Sin embargo, TCP generalmente se configura con un tiempo de espera, por lo que se dará por vencido y dejará de intentarlo
después de cierto tiempo, generalmente del orden de un minuto. Para superar las particiones de red que duran más de esta duración,
se debe implementar un mecanismo de deduplicación y reintento independiente además del proporcionado por TCP.

Un enlace arbitrario es un modelo preciso para la comunicación a través de Internet: cada vez que su comunicación se enruta a
través de una red (ya sea el wifi de una cafetería o una red troncal de Internet), el operador de esa red puede interferir y manipular sus
paquetes de red. de manera arbitraria. Alguien que manipula el tráfico de la red también se conoce como adversario activo.
Afortunadamente, es casi posible convertir un enlace arbitrario en un enlace de pérdida justa utilizando técnicas criptográficas. El
protocolo Transport Layer Security (TLS), que proporciona la "s" de "seguro" en https://, evita que un adversario activo espíe, modifique,
falsifique o reproduzca el tráfico (más sobre esto en el curso de Seguridad, Parte IB término de Semana Santa).

Lo único que TLS no puede evitar es que el adversario abandone (bloquee) la comunicación. Por lo tanto, un enlace arbitrario
puede convertirse en un enlace de pérdida justa solo si asumimos que el adversario no bloquea la comunicación para siempre. En
algunas redes, es posible enrutar el enlace de red interrumpido, pero no siempre es así.

17
Machine Translated by Google

Por lo tanto, la suposición de un enlace de red confiable quizás no sea tan poco realista como puede parecer a primera vista:
en general, es posible recibir todos los mensajes enviados, siempre que estemos dispuestos a esperar un período de tiempo
potencialmente arbitrario para recibirlos. reintentos durante una partición de red. Sin embargo, también debemos considerar la
posibilidad de que el remitente de un mensaje se bloquee al intentar retransmitir un mensaje, lo que puede causar que ese mensaje
se pierda de forma permanente. Esto nos lleva al tema de los bloqueos de nodos.

Modelo del sistema: comportamiento del nodo

Cada nodo ejecuta un algoritmo específico, asumiendo


uno de los siguientes: I Crash-stop (detención fallida): Un

nodo está defectuoso si se bloquea (en cualquier


momento).
Después de fallar, deja de ejecutarse para siempre.

I Crash-recovery (recuperación fallida): Un nodo


puede fallar en cualquier momento, perdiendo su estado en memoria. Puede
reanudar la ejecución en algún momento posterior.

I Byzantine (fail-arbitrary): un nodo es


defectuoso si se desvía del algoritmo.
Los nodos defectuosos pueden hacer cualquier cosa, incluido el bloqueo o
el comportamiento malicioso.

Un nodo que no es defectuoso se llama "correcto"

Diapositiva 34

En el modelo de bloqueo y detención, asumimos que después de que un nodo falla, nunca se recupera. Este es un modelo
razonable para una falla de hardware irrecuperable, o para la situación en la que una persona deja caer su teléfono en el inodoro,
después de lo cual queda permanentemente fuera de servicio. Con una caída del software, el modelo de parada por caída puede
parecer poco realista, porque simplemente podemos reiniciar el nodo, después de lo cual se recuperará. Sin embargo, algunos
algoritmos asumen un modelo de parada de choque, ya que eso hace que el algoritmo sea más simple. En este caso, un nodo que
falla y se recupera tendría que volver a unirse al sistema como un nuevo nodo. Alternativamente, el modelo de recuperación tras un
bloqueo permite explícitamente que los nodos se reinicien y reanuden el procesamiento después de un bloqueo.
Finalmente, el modelo bizantino es el modelo más general de comportamiento de los nodos: como en el problema de los
generales bizantinos, un nodo defectuoso no solo puede bloquearse, sino también desviarse del algoritmo especificado de manera
arbitraria, incluida la exhibición de un comportamiento malicioso. Un error en la implementación de un nodo también podría
clasificarse como una falla bizantina. Sin embargo, si todos los nodos ejecutan el mismo software, todos tendrán el mismo error, por
lo que cualquier algoritmo que se base en menos de un tercio de los nodos con fallas bizantinas no podrá tolerar dicho error. En
principio, podríamos intentar usar varias implementaciones diferentes del mismo algoritmo, pero rara vez es una opción práctica.

En el caso de la red, era posible convertir un modelo a otro utilizando protocolos genéricos.
Este no es el caso con los diferentes modelos de comportamiento de los nodos. Por ejemplo, un algoritmo diseñado para un modelo
de sistema de recuperación de fallas puede verse muy diferente de un algoritmo bizantino.

Modelo del sistema: suposiciones de sincronía (tiempo)

Suponga uno de los siguientes para la red y los nodos:

I Síncrono: latencia del


mensaje no superior a un límite superior conocido.
Los nodos ejecutan el algoritmo a una velocidad conocida.

I Parcialmente sincrónico: el sistema


es asíncrono durante algunos períodos de tiempo finitos (pero
desconocidos), sincrónico de lo contrario.

Yo Asíncrono:
Los mensajes se pueden retrasar arbitrariamente.
Los nodos pueden pausar la ejecución arbitrariamente.
No hay garantías de tiempo en absoluto.

Nota: otras partes de la informática utilizan los términos "sincrónico" y


"asincrónico" de manera diferente.

Diapositiva 35

18
Machine Translated by Google

La tercera parte de un modelo de sistema es la suposición de sincronía, que tiene que ver con el tiempo. Las tres elecciones
que podemos hacer aquí son sincrónicas, asincrónicas o parcialmente sincrónicas [Dwork et al., 1988].
(Tenga en cuenta que las definiciones de estos términos difieren un poco en diferentes partes de la informática; nuestras definiciones
aquí son estándar en el campo de la computación distribuida).
Un sistema síncrono es lo que nos encantaría tener: un mensaje enviado a través de la red nunca toma más tiempo que una
latencia máxima conocida, y los nodos siempre ejecutan su algoritmo a una velocidad predecible. Muchos problemas en la
computación distribuida son mucho más fáciles si asume un sistema síncrono.
Y es tentador suponer sincronía, porque las redes y los nodos se comportan bien la mayor parte del tiempo, por lo que esta
suposición suele ser cierta.
Desafortunadamente, la mayor parte del tiempo no es lo mismo de siempre, y los algoritmos diseñados para un modelo síncrono
a menudo fallan catastróficamente si se violan los supuestos de latencia limitada y velocidad de ejecución limitada, aunque sea por
un corto tiempo, e incluso si esto sucede raramente. Y en los sistemas prácticos, hay muchas razones por las que la latencia de la
red o la velocidad de ejecución a veces pueden variar enormemente, consulte la diapositiva 36.
El otro extremo es un modelo asíncrono, en el que no hacemos ninguna suposición de tiempo: permitimos que los mensajes se
retrasen arbitrariamente en la red y permitimos diferencias arbitrarias en las velocidades de procesamiento de los nodos (por ejemplo,
permitimos que un nodo pause la ejecución mientras otros nodos continúan funcionando normalmente). Los algoritmos que están
diseñados para un modelo asincrónico suelen ser muy sólidos, ya que no se ven afectados por interrupciones temporales de la red o
picos de latencia.
Desafortunadamente, algunos problemas en la computación distribuida son imposibles de resolver en un modelo asíncrono y,
por lo tanto, tenemos el modelo parcialmente síncrono como compromiso. En este modelo, asumimos que nuestro sistema es
sincrónico y se comporta bien la mayor parte del tiempo, pero ocasionalmente puede pasar al modo asíncrono en el que todas las
garantías de tiempo están desactivadas, y esto puede suceder de manera impredecible. El modelo parcialmente síncrono es bueno
para muchos sistemas prácticos, pero usarlo correctamente requiere cuidado.

Violaciones de sincronía en la práctica

Las redes suelen tener una latencia bastante predecible, que en ocasiones
puede aumentar:

Pérdida de mensaje que requiere reintento

I Congestión/contención que provoca colas

I Reconfiguración de red/ruta

Los nodos generalmente ejecutan código a una velocidad predecible, con


pausas ocasionales: Problemas de programación del sistema operativo, por

ejemplo, inversión de prioridad Pausas de recolección de basura para detener el

mundo Errores de página , intercambio, hiperpaginación

Los sistemas operativos en tiempo real (RTOS) brindan garantías de


programación, pero la mayoría de los sistemas distribuidos no usan RTOS

Diapositiva 36

Hay muchas razones por las que un sistema puede violar los supuestos de sincronía. Ya hemos hablado sobre el aumento
ilimitado de la latencia si los mensajes se pierden y se retransmiten, especialmente si tenemos que esperar a que se repare una
partición de la red antes de que los mensajes puedan pasar. Otra razón por la que aumenta la latencia en una red es la congestión
que provoca la puesta en cola de los paquetes en los búferes del conmutador. La reconfiguración de la red también puede causar
grandes retrasos: incluso dentro de un solo centro de datos, se han documentado casos de paquetes que se retrasan por más de un
minuto [Imbriaco, 2012].
Podríamos esperar que la velocidad a la que los nodos ejecutan sus algoritmos sea constante: después de todo, una instrucción
generalmente toma un número fijo de ciclos de reloj de la CPU y la velocidad del reloj no varía mucho.
Sin embargo, incluso en un solo nodo, hay muchas razones por las que un programa en ejecución puede pausarse inesperadamente
durante períodos de tiempo significativos. La programación en el sistema operativo puede adelantarse a un subproceso en ejecución
y dejarlo en pausa mientras se ejecutan otros programas, especialmente en una máquina con mucha carga. Un problema real en los
lenguajes administrados por memoria como Java es que cuando se ejecuta el recolector de elementos no utilizados, debe pausar
todos los subprocesos en ejecución de vez en cuando (esto se conoce como una pausa de recolección de elementos no utilizados
para detener el mundo). ¡ En pilas grandes, estas pausas pueden durar varios minutos [Thompson, 2013]! Los errores de página son
otra razón por la que un subproceso puede suspenderse, especialmente cuando no queda mucha memoria libre.
Como sabe por la mitad de los sistemas concurrentes de este curso, los subprocesos pueden y serán reemplazados incluso en
los momentos más inconvenientes, en cualquier parte de un programa. En un sistema distribuido, esto es particularmente

19
Machine Translated by Google

problemático, porque para un nodo, el tiempo parece "detenerse" mientras está en pausa, y durante este tiempo todos los
demás nodos continúan ejecutando sus algoritmos normalmente. Otros nodos pueden incluso notar que el nodo en pausa
no responde y suponer que se ha bloqueado. Después de un tiempo, el nodo en pausa reanuda el procesamiento, sin
siquiera darse cuenta de que estuvo en pausa durante un período de tiempo significativo.
Combinado con las muchas razones para la latencia de red variable, esto significa que en los sistemas prácticos, rara
vez es seguro asumir un modelo de sistema síncrono. La mayoría de los algoritmos distribuidos deben diseñarse para el
modelo asíncrono o parcialmente síncrono.

Resumen de modelos de sistema

Para cada una de las tres partes, elija una:

Red I : confiable,
de pérdida justa o arbitraria

Nodos I :
parada de choque, recuperación de choque o bizantino

Temporización:
síncrona , parcialmente síncrona o asíncrona

Esta es la base de cualquier algoritmo distribuido.


Si sus suposiciones son incorrectas, ¡todas las apuestas están canceladas!

Diapositiva 37

2.4 Tolerancia a fallos y alta disponibilidad


Como se destaca en la Diapositiva 4, una de las razones para construir sistemas distribuidos es lograr una mayor
confiabilidad que la que es posible con una sola computadora. Ahora exploraremos más esta idea a la luz de los modelos
de sistema que hemos discutido.
Desde un punto de vista comercial, lo que suele importar más es la disponibilidad de un servicio, como un sitio web.
Por ejemplo, una tienda online quiere poder vender productos a cualquier hora del día o de la noche: cualquier interrupción
del sitio web significa una oportunidad perdida de ganar dinero. Para otros servicios, incluso puede haber acuerdos
contractuales con clientes que requieran que el servicio esté disponible. Si un servicio no está disponible, esto también
puede dañar la reputación del proveedor del servicio.
La disponibilidad de un servicio normalmente se mide en términos de su capacidad para responder correctamente a las
solicitudes dentro de un tiempo determinado. La definición de si un servicio está "disponible" o "no disponible" puede ser un
tanto arbitraria: por ejemplo, si una página tarda 5 segundos en cargarse, ¿consideramos que ese sitio web todavía está
disponible? ¿Y si tarda 30 segundos? ¿Una hora?

Comienzo del video sección


Disponibilidad 2.4 (descarga mp4)
¡La tienda en línea quiere vender cosas 24/7!
Falta de disponibilidad del servicio = tiempo de inactividad = pérdida de dinero

Disponibilidad = tiempo de actividad = fracción de tiempo que un servicio


funciona correctamente

I “Dos nueves” = 99% arriba = abajo 3,7 días/año I “Tres nueves”

= 99,9% arriba = abajo 8,8 horas/año I “Cuatro nueves” = 99,99%

arriba = abajo 53 minutos/año I “Cinco nueves” = 99,999% arriba =

abajo 5,3 minutos/año

Objetivo de nivel de servicio (SLO): por


ejemplo, "99.9% de las solicitudes en un día obtienen una respuesta en 200 ms"

Acuerdo de nivel de servicio (SLA): contrato


que especifica algunos SLO, sanciones por incumplimiento
Diapositiva 38

20
Machine Translated by Google

Por lo general, las expectativas de disponibilidad de un servicio se formalizan como un objetivo de nivel de servicio (SLO),
que generalmente especifica el porcentaje de solicitudes que deben devolver una respuesta correcta dentro de un tiempo de
espera especificado, medido por un determinado cliente durante un cierto período de tiempo. hora. Un acuerdo de nivel de
servicio (SLA) es un contrato que especifica algún SLO, así como las consecuencias si no se cumple el SLO (por ejemplo, es
posible que el proveedor de servicios deba ofrecer un reembolso a sus clientes).
Las fallas (como bloqueos de nodos o interrupciones de la red) son una causa común de falta de disponibilidad. Para
aumentar la disponibilidad, podemos reducir la frecuencia de fallas, o podemos diseñar sistemas para que sigan funcionando
a pesar de que algunos de sus componentes estén defectuosos; el último enfoque se llama tolerancia a fallas. Es posible
reducir la frecuencia de las fallas comprando hardware de mayor calidad e introduciendo redundancia, pero este enfoque
nunca puede reducir la probabilidad de fallas a cero. En cambio, la tolerancia a fallas es el enfoque adoptado por muchos
sistemas distribuidos.

Lograr una alta disponibilidad: tolerancia a fallas

Fallo: el sistema en su conjunto no funciona

Fallo: alguna parte del sistema no funciona

I Falla de nodo: accidente (crash-stop/crash-recovery),


desviación del algoritmo (Bizantino)
I Falla de red: caída o retraso significativo de mensajes

Tolerancia a fallos:
el sistema como un todo continúa funcionando, a pesar de las
fallas (se asume un número máximo de fallas)

Punto único de falla (SPOF): nodo/enlace


de red cuya falla conduce a la falla

Diapositiva 39

Si todos los nodos fallan y no se recuperan, ningún algoritmo podrá realizar ningún trabajo, por lo que no tiene sentido
tolerar un número arbitrario de fallas. Más bien, un algoritmo está diseñado para tolerar un número específico de fallas: por
ejemplo, algunos algoritmos distribuidos pueden progresar siempre que menos de la mitad de los nodos se hayan bloqueado.

En los sistemas tolerantes a fallas, queremos evitar los puntos únicos de falla (SPOF), es decir, los componentes que
causarían una interrupción si fallaran. Por ejemplo, Internet está diseñado para no tener SPOF: no hay un servidor o enrutador
cuya destrucción provocaría la caída de Internet por completo (aunque la pérdida de algunos componentes, como los enlaces
de fibra intercontinentales clave, causa una interrupción notable).
El primer paso para tolerar fallas es detectar fallas, lo que a menudo se hace con un detector de fallas.
("Detector de fallas" sería más lógico, pero "detector de fallas" es el término convencional.) Un detector de fallas generalmente
detecta fallas por colisión. Las fallas bizantinas no siempre son detectables, aunque en algunos casos el comportamiento
bizantino deja evidencia que puede usarse para identificar y excluir nodos maliciosos.

Detectores de fallos

Detector de fallos:
algoritmo que detecta si otro nodo es defectuoso

Detector de fallos perfecto:


etiqueta un nodo como defectuoso si y solo si se ha bloqueado

Implementación típica para detención/recuperación de fallas: enviar


mensaje, esperar respuesta, etiquetar el nodo como fallado si no hay
respuesta dentro de un tiempo de espera

Problema:
no se puede diferenciar entre un nodo bloqueado, un nodo que no responde
temporalmente, un mensaje perdido y un mensaje retrasado

Diapositiva 40

21
Machine Translated by Google

En la mayoría de los casos, un detector de fallas funciona enviando mensajes periódicamente a otros nodos y etiquetando
un nodo como fallado si no se recibe una respuesta dentro del tiempo esperado. Idealmente, nos gustaría que se agotara el
tiempo de espera si y solo si el nodo realmente se bloqueó (esto se denomina detector de falla perfecto). Sin embargo, el
problema de los dos generales nos dice que esta no es una forma totalmente precisa de detectar un bloqueo, porque la
ausencia de una respuesta también podría deberse a la pérdida o retraso del mensaje.
Un detector de fallas perfecto basado en el tiempo de espera solo existe en un sistema sincrónico de parada por choque
con enlaces confiables; en un sistema parcialmente síncrono, no existe un detector de fallas perfecto. Además, en un sistema
asíncrono, no existe una falla basada en el tiempo de espera, ya que los tiempos de espera no tienen sentido en el modelo
asíncrono. Sin embargo, existe un útil detector de fallas en los sistemas parcialmente síncronos: el detector de fallas
eventualmente perfecto [Chandra y Toueg, 1996].

Detección de fallos en sistemas parcialmente síncronos

El detector de fallas perfecto basado en el tiempo de espera solo existe


en un sistema sincrónico de parada por choque con enlaces confiables.

Eventualmente perfecto detector de fallas:

Puedo etiquetar temporalmente un nodo como bloqueado,


aunque sea correcto

Puedo etiquetar temporalmente un nodo como correcto,


aunque se haya bloqueado

I Pero eventualmente, etiqueta un nodo como fallado si y


solo si ha fallado

Refleja el hecho de que la detección no es instantánea y es posible que tengamos


tiempos de espera falsos

Diapositiva 41

Más adelante veremos cómo usar un detector de fallas de este tipo para diseñar mecanismos de tolerancia a fallas y para
recuperarse automáticamente de fallas de nodos. Usando tales algoritmos, es posible construir sistemas que sean altamente
disponibles. Tolerar fallas también facilita las operaciones diarias: por ejemplo, si un servicio puede tolerar que uno de cada
tres nodos no esté disponible, entonces se puede implementar una actualización de software instalándolo y reiniciando un
nodo a la vez, mientras que el resto dos nodos continúan ejecutando el servicio. Poder implementar actualizaciones de
software de esta manera, sin que los clientes noten ninguna interrupción, es importante para muchas organizaciones que
trabajan continuamente en su software.
Para aplicaciones críticas para la seguridad, como los sistemas de control de tráfico aéreo, sin duda es importante invertir
en buenos mecanismos de tolerancia a fallas. Sin embargo, no es el caso que una mayor disponibilidad sea siempre mejor.
Alcanzar una disponibilidad extremadamente alta requiere un esfuerzo de ingeniería muy centrado y, a menudo, elecciones
de diseño conservadoras. Por ejemplo, la antigua red telefónica de línea fija está diseñada para una disponibilidad de "cinco
nueves", pero la desventaja de este enfoque en la disponibilidad es que ha evolucionado muy lentamente. La mayoría de los
servicios de Internet ni siquiera llegan a cuatro nueves debido a los rendimientos decrecientes: más allá de algún punto, el
costo adicional de lograr una mayor disponibilidad supera el costo del tiempo de inactividad ocasional, por lo que es
económicamente racional aceptar una cierta cantidad de tiempo de inactividad.

Ejercicio 2. Los enlaces de red confiables permiten reordenar los mensajes. Proporcione un pseudocódigo para un algoritmo
que refuerce las propiedades de un enlace punto a punto confiable, de modo que los mensajes se reciban en el orden en que
fueron enviados (esto se denomina enlace FIFO), asumiendo un modelo de sistema asíncrono de parada por choque.

Ejercicio 3. ¿Cómo necesitamos cambiar el algoritmo del ejercicio 2 si asumimos un modelo de recuperación de fallas en lugar
de un modelo de parada de fallas?

22
Machine Translated by Google

3 Tiempo, relojes y orden de eventos


Comencemos con un acertijo, que se resolverá más adelante en esta lección.

Comienzo del video sección


Una historia de detectives 3.1 (descarga mp4)

En la noche del 30 de junio al 1 de julio de 2012 (hora del Reino Unido), muchos
servicios y sistemas en línea de todo el mundo colapsaron simultáneamente.

Los servidores se bloquearon y dejaron de responder.

Algunas aerolíneas no pudieron procesar ninguna reserva o check-in durante varias


horas.

¿Qué sucedió?

Diapositiva 42

En esta lección veremos el concepto de tiempo en sistemas distribuidos. Ya hemos visto que nuestras suposiciones
sobre el tiempo forman una parte clave del modelo del sistema en el que se basan los algoritmos distribuidos.
Por ejemplo, los detectores de fallas basados en el tiempo de espera deben medir el tiempo para determinar cuándo ha
transcurrido un tiempo de espera. Los sistemas operativos se basan en gran medida en temporizadores y mediciones de
tiempo para programar tareas, realizar un seguimiento del uso de la CPU y muchos otros fines. Las aplicaciones a menudo
desean registrar la hora y la fecha en que ocurrieron los eventos: por ejemplo, al depurar un error en un sistema distribuido,
las marcas de tiempo son útiles para la depuración, ya que nos permiten reconstruir qué cosas sucedieron aproximadamente
al mismo tiempo en diferentes nodos. Todos estos requieren mediciones de tiempo más o menos precisas.

Relojes y tiempo en sistemas distribuidos

Los sistemas distribuidos a menudo necesitan medir el tiempo, por ejemplo:

I Programadores, tiempos de espera, detectores de fallas, temporizadores

de reintento I Mediciones de rendimiento, estadísticas, creación de perfiles I

Archivos de registro y bases de datos: registre cuándo ocurrió un evento I Datos

con validez limitada en el tiempo (por ejemplo, entradas de caché)

I Determinar el orden de los eventos en varios nodos

Distinguimos dos tipos de reloj:

I relojes físicos: cuentan el número de segundos transcurridos I relojes

lógicos: cuentan eventos, por ejemplo, mensajes enviados

NÓTESE BIEN. Reloj en electrónica digital (oscilador) 6=


reloj en sistemas distribuidos (fuente de sellos de tiempo)

Diapositiva 43

3.1 Relojes físicos


Comenzaremos discutiendo los relojes físicos, que son los tipos de relojes con los que está familiarizado por el uso diario.
Los relojes físicos incluyen relojes analógicos/mecánicos basados en péndulos o mecanismos similares, y relojes digitales
basados, por ejemplo, en un cristal de cuarzo vibrante. Los relojes de cuarzo se encuentran en la mayoría de los relojes de
pulsera, en todas las computadoras y teléfonos móviles, en los hornos de microondas que muestran la hora y en muchos
otros objetos cotidianos.

23
Machine Translated by Google

relojes de cuarzo

Yo cristal de cuarzo
recortado con láser

para resonar mecánicamente a una


frecuencia específica
I Efecto piezoeléctrico:
fuerza mecánica ÿ
campo eléctrico

El circuito del oscilador produce una


señal a una frecuencia resonante

Cuento el número de ciclos para medir


el tiempo transcurrido

Diapositiva 44

Los relojes de cuarzo son baratos, pero no son totalmente precisos. Debido a imperfecciones de fabricación, algunos relojes
funcionan un poco más rápido que otros. Además, la frecuencia de oscilación varía con la temperatura.
Los relojes de cuarzo típicos están ajustados para ser bastante estables a temperatura ambiente, pero las temperaturas
significativamente más altas o más bajas ralentizan el reloj. La velocidad a la que un reloj se adelanta o atrasa se denomina deriva.

Error de reloj de cuarzo: deriva

I Un reloj va un poco más rápido, otro un poco más lento

I Deriva medida en partes por millón (ppm)

I 1 ppm = 1 microsegundo/segundo = 86 ms/día = 32 s/año

I La mayoría de los relojes de computadora son correctos dentro de ÿ 50 ppm

La temperatura
afecta
significativamente la deriva

Diapositiva 45

relojes atómicos

I El cesio-133 tiene un

resonancia (“transición
hiperfina”) a ÿ 9 GHz
Sintonizo un oscilador
electrónico a esa frecuencia

resonante I 1 segundo =

9,192,631,770 períodos de esa señal I


Precisión ÿ 1 en 10ÿ14 (1 segundo

en 3 millones de años)
https: //www.microsemi.com/product-
directory/cesium-frequency-references/
I Precio ÿ £ 20,000 (?) (puede 4115-5071a-cesium-primary-frequency-standard
obtener relojes de rubidio más
baratos por ÿ £ 1,000)

Diapositiva 46

Cuando se requiere mayor precisión, se utilizan relojes atómicos. Estos relojes se basan en la cuántica.

24
Machine Translated by Google

propiedades mecánicas de ciertos átomos, como el cesio o el rubidio. De hecho, la unidad de tiempo de un segundo en el
Sistema Internacional de Unidades (SI) se define como exactamente 9.192.631.770 períodos de una frecuencia resonante
particular del átomo de cesio-133.

GPS como fuente de tiempo

I 31 satélites, cada uno con un reloj


atómico
I transmisiones satelitales
hora y ubicación actual

Calculo la posición a partir del


retraso de la velocidad de la
luz entre el satélite y el
receptor
Yo correcciones para
efectos atmosféricos,
relatividad, etc.
https://commons.wikimedia.org/wiki/Archivo:
Yo en centros de datos, Gps-atmospheric-efects.png
necesito antena en el techo

Diapositiva 47

Otro método de alta precisión para obtener la hora es confiar en el sistema de posicionamiento por satélite GPS, o
sistemas similares como Galileo o GLONASS. Estos sistemas funcionan al tener varios satélites orbitando la Tierra y
transmitiendo la hora actual a muy alta resolución. Los receptores miden el tiempo que tardó la señal de cada satélite en
llegar a ellos y lo utilizan para calcular su distancia de cada satélite y, por lo tanto, su ubicación. Al conectar un receptor
GPS a una computadora, es posible obtener un reloj con una precisión de una fracción de microsegundo, siempre que el
receptor pueda obtener una señal clara de los satélites. En un centro de datos, generalmente hay demasiada interferencia
electromagnética para obtener una buena señal, por lo que un receptor GPS requiere una antena en el techo del edificio
del centro de datos.
Ahora tenemos un problema: tenemos dos definiciones diferentes de tiempo, una basada en la mecánica cuántica y la
otra basada en la astronomía, y esas dos definiciones no coinciden con precisión. Una rotación del planeta Tierra alrededor
de su propio eje no toma exactamente 24 × 60 × 60 × 9,192,631,770 períodos de frecuencia de resonancia de cesio-133.
De hecho, la velocidad de rotación del planeta ni siquiera es constante: fluctúa debido a los efectos de las mareas, los
terremotos, el derretimiento de los glaciares y algunos factores inexplicables.

Tiempo Universal Coordinado (UTC)

Hora media de Greenwich (GMT, hora solar):


es mediodía cuando el sol está en el sur, visto
desde el meridiano de Greenwich

Tiempo atómico internacional (TAI): 1 día es 24 ×


60 × 60 × 9,192,631,770 períodos de frecuencia de
resonancia de cesio-133

Problema: la velocidad de rotación de la Tierra no es


constante

Compromiso: UTC es TAI con correcciones para


tener en cuenta la rotación de la Tierra

Las zonas horarias y el horario de verano están


compensados con UTC

Diapositiva 48

La solución es el Tiempo Universal Coordinado (UTC), que se basa en el tiempo atómico, pero incluye correcciones
para tener en cuenta las variaciones en la rotación de la Tierra. En la vida cotidiana, usamos nuestra zona horaria local,
que se especifica como un desplazamiento de UTC.
La zona horaria local del Reino Unido se llama Greenwich Mean Time (GMT) en invierno y British Summer Time (BST)
en verano, donde GMT se define como UTC y BST se define como UTC + 1 hora.
De manera confusa, el término hora media de Greenwich se usó originalmente para referirse a la hora solar media en el

25
Machine Translated by Google

Meridiano de Greenwich, es decir, antes se definía en términos de astronomía, mientras que ahora se define en términos de relojes
atómicos. Hoy en día, el término UT1 se usa para referirse al tiempo solar medio a 0° de longitud.
La diferencia entre UTC y TAI es que UTC incluye segundos bisiestos, que se agregan según sea necesario
para mantener el UTC más o menos sincronizado con la rotación de la Tierra.

Segundos bisiestos

Todos los años, el 30 de junio y el 31 de diciembre a las 23:59:59 UTC, sucede


una de estas tres cosas:

I El reloj salta inmediatamente hacia adelante a las 00:00:00, saltándose


un segundo (segundo bisiesto negativo)
I El reloj se mueve a 00:00:00 después de un segundo, como de costumbre

I El reloj se mueve a las 23:59:60 después de un segundo y luego


se mueve a 00:00:00 después de un segundo más

(segundo intercalar positivo)


Esto se anuncia con varios meses de antelación.

http://leapsecond.com/notes/leap-watch.htm

Diapositiva 49

Debido a los segundos bisiestos, no es cierto que una hora siempre tenga 3600 segundos y un día siempre tenga 86 400
segundos. En la escala de tiempo UTC, un día puede tener 86 399 segundos, 86 400 segundos o 86 401 segundos debido a un
segundo bisiesto. Esto complica el software que necesita trabajar con fechas y horas.
En informática, una marca de tiempo es una representación de un punto particular en el tiempo. Normalmente se utilizan dos
representaciones de marcas de tiempo: tiempo Unix e ISO 8601. Para el tiempo Unix, cero corresponde a la fecha elegida
arbitrariamente del 1 de enero de 1970, conocida como la época. Hay variaciones menores: por ejemplo, System.currentTimeMillis()
de Java es como el tiempo de Unix, pero usa milisegundos en lugar de segundos.

Cómo representan las marcas de tiempo las computadoras

Dos representaciones más comunes:

I tiempo Unix: número de segundos desde el 1 de enero de 1970


00:00:00 UTC (la “época”), sin contar los segundos bisiestos

I ISO 8601: año, mes, día, hora, minuto, segundo y


desplazamiento de la zona horaria en

relación con el ejemplo de UTC: 2020-11-09T09: 50: 17 + 00: 00

La conversión entre los dos requiere:

I Calendario gregoriano: 365 días en un año, excepto años bisiestos (año % 4


== 0 && (año % 100 != 0 || año % 400 == 0))

I Conocimiento de los segundos intercalares pasados y futuros. . . ?!

Diapositiva 50

Para ser correcto, el software que funciona con marcas de tiempo necesita conocer los segundos intercalares. Por ejemplo, si
desea calcular cuántos segundos transcurrieron entre dos marcas de tiempo, necesita saber cuántos segundos intercalares se
insertaron entre esas dos fechas. Para fechas que están a más de seis meses en el futuro, esto es imposible de saber, ¡porque la
rotación de la Tierra aún no ha ocurrido!
El enfoque más común en el software es simplemente ignorar los segundos intercalares, fingir que no existen y esperar que el
problema desaparezca de alguna manera. Este enfoque lo adoptan las marcas de tiempo de Unix y el estándar POSIX. Para el
software que solo necesita tiempos de grano grueso (por ejemplo, redondeado al día más cercano), esto está bien, ya que la
diferencia de unos pocos segundos no es significativa.
Sin embargo, los sistemas operativos y los sistemas distribuidos a menudo se basan en marcas de tiempo de alta resolución
para mediciones precisas del tiempo, donde una diferencia de un segundo es muy notable. En tales entornos, ignorar los segundos
bisiestos puede ser peligroso. Por ejemplo, supongamos que tiene un programa Java que llama dos veces

26
Machine Translated by Google

System.currentTimeMillis(), con 500 ms de diferencia, dentro de un segundo bisiesto positivo (es decir, mientras el reloj marca las 23:59:60).
¿Cuál será la diferencia entre esas dos marcas de tiempo? No puede ser 500, ya que el reloj currentTimeMillis() no tiene en cuenta los
segundos bisiestos. ¿Se detiene el reloj, por lo que la diferencia entre las dos marcas de tiempo es cero? ¿O podría la diferencia incluso
ser negativa, por lo que el reloj retrocede por un breve momento? La documentación guarda silencio sobre esta pregunta. (Probablemente,
la mejor solución sea usar un reloj monótono, que presentamos en la diapositiva 58).

El mal manejo del segundo intercalar el 30 de junio de 2012 es lo que causó las fallas simultáneas de muchos servicios ese día
(Diapositiva 42). Debido a un error en el kernel de Linux, el segundo intercalar tenía una alta probabilidad de desencadenar una condición
de bloqueo activo cuando se ejecutaba un proceso de subprocesos múltiples [Allen, 2013, Minar, 2012]. Incluso un reinicio no solucionó el
problema, pero configurar el reloj del sistema restableció el mal estado en el kernel.

Cómo la mayoría del software trata los segundos bisiestos

¡Ignorándolos!

Sin embargo, OS y DistSys a menudo necesitan


tiempos con una precisión de subsegundos.

30 de junio de 2012: un error en el kernel de Linux provocó


un bloqueo en el segundo bisiesto, lo que provocó muchos
Servicios de Internet a bajar

Solución pragmática: "difuminar" (esparcir) el segundo


bisiesto en el transcurso de un día
https://www.flickr.com/
photos/ruboff/
37915499055/

Diapositiva 51

Hoy en día, algunos programas manejan los segundos bisiestos explícitamente, mientras que otros programas continúan ignorándolos.
Una solución pragmática que se usa ampliamente hoy en día es que cuando ocurre un segundo bisiesto positivo, en lugar de insertarlo
entre las 23:59:59 y las 00:00:00, el segundo adicional se distribuye durante varias horas antes y después de esa hora deliberadamente
ralentizando los relojes durante ese tiempo (o acelerándolos en el caso de un segundo bisiesto negativo). Este enfoque se llama manchar
el segundo bisiesto y no está exento de problemas.
Sin embargo, es una alternativa pragmática a hacer que todo el software sea consciente y resistente a los segundos intercalares, lo que
bien puede ser inviable.

Ejercicio 4. Describa algunos problemas que pueden surgir de la mancha de segundo bisiesto.

3.2 Sincronización de relojes y relojes monotónicos

Comienzo del video sección 3.2


Sincronización de reloj (descarga mp4)

Las computadoras rastrean la hora física/UTC con un reloj de cuarzo (con


batería, continúa funcionando cuando se apaga)

Debido a la deriva del reloj, el error del reloj aumenta gradualmente

Sesgo de reloj: diferencia entre dos relojes en un punto en el tiempo

Solución: obtenga periódicamente la hora actual de un servidor que tenga una


fuente de tiempo más precisa (reloj atómico o receptor GPS)

Protocolos: Protocolo de tiempo de red (NTP),


Protocolo de tiempo de precisión (PTP)

Diapositiva 52

27
Machine Translated by Google

Los relojes atómicos son demasiado costosos y voluminosos para integrarlos en todas las computadoras y teléfonos, por lo que
se utilizan relojes de cuarzo. Como se discutió en la diapositiva 45, estos relojes se desvían y necesitan ajustes de vez en cuando.
El enfoque más común es utilizar el Protocolo de tiempo de red (NTP). Todos los sistemas operativos principales tienen clientes NTP
incorporados; por ejemplo, la diapositiva 53 muestra el cuadro de diálogo de configuración de NTP en macOS.

Diapositiva 53

Protocolo de tiempo de red (NTP)


Muchos proveedores de sistemas operativos ejecutan servidores NTP, configure

el sistema operativo para usarlos de forma predeterminada

Jerarquía de servidores de reloj dispuestos en estratos:


I Estrato 0: reloj atómico o receptor GPS

I Estrato 1: sincronizado directamente con el dispositivo del


estrato 0 I Estrato 2: servidores que sincronizan con el estrato 1, etc.

Puede ponerse en contacto con múltiples servidores, descartar valores atípicos, promedio de descanso

Realiza múltiples solicitudes al mismo servidor, usa estadísticas para


reducir errores aleatorios debido a variaciones en la latencia de la red

Reduce el sesgo del reloj a unos pocos milisegundos en buenas


condiciones de red, ¡pero puede ser mucho peor!

Diapositiva 54

La sincronización horaria en una red se ve dificultada por una latencia impredecible. Como se discutió en la diapositiva 36, tanto
la latencia de la red como la velocidad de procesamiento de los nodos pueden variar considerablemente. Para reducir los efectos de
las variaciones aleatorias, NTP toma varias muestras de medidas de tiempo y aplica filtros estadísticos para eliminar los valores
atípicos.
La diapositiva 55 muestra cómo NTP estima el sesgo de reloj entre el cliente y el servidor. Cuando el cliente envía un mensaje
de solicitud, incluye la marca de tiempo actual t1 según el reloj del cliente. Cuando el servidor recibe la solicitud, y antes de procesarla,
el servidor registra la marca de tiempo actual t2 de acuerdo con el reloj del servidor. Cuando el servidor envía su respuesta, repite el
valor t1 de la solicitud y también incluye la marca de tiempo de recepción del servidor t2 y la marca de tiempo de respuesta del
servidor t3 en la respuesta. Finalmente, cuando el cliente recibe la respuesta, registra la marca de tiempo actual t4 de acuerdo con el
reloj del cliente.
Podemos determinar el tiempo que los mensajes tardaron en viajar a través de la red calculando el tiempo de ida y vuelta desde
el punto de vista del cliente (t4 ÿ t1) y restando el tiempo de procesamiento en el servidor (t3 ÿ t2). Luego estimamos que la latencia
de la red unidireccional es la mitad del retraso total de la red. Por lo tanto, cuando la respuesta llega al cliente, podemos estimar que
el reloj del servidor se habrá movido a t3 más la latencia de la red unidireccional. Luego restamos la hora actual del cliente t4 de la
hora estimada del servidor para obtener el desfase estimado entre los dos relojes.

Esta estimación depende de la suposición de que la latencia de la red es aproximadamente la misma en ambas direcciones.
Esta suposición es probablemente cierta si la latencia está dominada por la distancia geográfica entre

28
Machine Translated by Google

cliente y servidor. Sin embargo, si el tiempo de espera en la red es un factor significativo en la latencia (por ejemplo, si el
enlace de red de un nodo está muy cargado mientras que el enlace del otro nodo tiene suficiente capacidad), entonces
podría haber una gran diferencia entre la latencia de solicitud y respuesta. Desafortunadamente, la mayoría de las redes no
dan a los nodos ninguna indicación de la latencia real que ha experimentado un paquete en particular.

Estimación del tiempo en una red

cliente NTP servidor NTP

t1 solicitud: t1
t2
respuesta: (t1, t2, t3) t3
t4

Retardo de red de ida y vuelta: ÿ = (t4 ÿ t1) ÿ (t3 ÿ t2)

D
Tiempo estimado del servidor cuando el cliente recibe la respuesta: t3 + 2

ÿ t2 - t1 + t3 - t4
Sesgo de reloj estimado: ÿ = t3 + ÿ t4 = 2
2

Diapositiva 55

Ejercicio 5. ¿Cuál es el error máximo posible en la estimación de sesgo del cliente NTP con respecto a un servidor en
particular, suponiendo que ambos nodos siguen correctamente el protocolo?

Una vez que NTP ha estimado el sesgo de reloj entre el cliente y el servidor, el siguiente paso es ajustar el reloj del
cliente para alinearlo con el servidor. El método utilizado para esto depende de la cantidad de sesgo. El cliente corrige las
pequeñas diferencias suavemente ajustando la velocidad del reloj para que funcione un poco más rápido o más lento según
sea necesario, lo que reduce gradualmente el sesgo en el transcurso de unos minutos. Este proceso se llama girar el reloj.

La diapositiva 57 muestra un ejemplo de rotación, en el que la frecuencia del reloj del cliente converge a la misma
velocidad que la del servidor, manteniendo a los dos sincronizados en unos pocos milisegundos. Por supuesto, la precisión
exacta lograda en un sistema en particular depende de las propiedades de tiempo de la red entre el cliente y el servidor.
Sin embargo, si el sesgo es mayor, la rotación tomaría demasiado tiempo, por lo que el cliente NTP establece su reloj
a la fuerza en la hora correcta estimada en función de la marca de tiempo del servidor. Esto se llama pisar el reloj. Cualquier
aplicación que esté mirando el reloj en el cliente verá que el tiempo salta repentinamente hacia adelante o hacia atrás.
Y finalmente, si el sesgo es muy grande (por defecto, más de unos 15 minutos), el cliente NTP puede decidir que algo
debe estar mal y negarse a ajustar el reloj, dejando el problema para que lo corrija un usuario u operador. Por esta razón,
cualquier sistema que dependa de la sincronización del reloj debe ser monitoreado cuidadosamente para detectar sesgos
de reloj: solo porque un nodo esté ejecutando NTP, eso no garantiza que su reloj sea correcto, ya que podría quedarse
atascado en un estado de pánico en el que se niega a ajustar el reloj.

Corrección del sesgo del reloj

Una vez que el cliente ha estimado el sesgo de reloj ÿ, necesita aplicar esa
corrección a su reloj.

Si |ÿ| < 125 ms, giró el reloj: acelere ligeramente


o disminuya la velocidad hasta 500 ppm (sincroniza los relojes en ÿ 5
minutos)

I Si 125 ms ÿ |ÿ| < 1,000 s, paso el reloj: reinicie


repentinamente el reloj del cliente a la marca de tiempo estimada del servidor

Si |ÿ| ÿ 1,000 s, entrar en pánico y no hacer nada (deje


que un operador humano resuelva el problema)

¡Los sistemas que dependen de la sincronización del reloj necesitan monitorear el sesgo del reloj!

Diapositiva 56

29
Machine Translated by Google

http://www.ntp.org/ntpfaq/NTP-s-algo.htm

Diapositiva 57

El hecho de que los relojes puedan ser acelerados por NTP, es decir, movidos repentinamente hacia adelante o hacia atrás, tiene una
implicación importante para cualquier software que necesite medir el tiempo transcurrido. La diapositiva 58 muestra un ejemplo en Java, en el
que queremos medir el tiempo de ejecución de una función hacerAlgo(). Java tiene dos funciones principales para obtener la marca de tiempo
actual del reloj local del sistema operativo: currentTimeMillis() y nanoTime(). Además de la diferente resolución (milisegundos versus
nanosegundos), la diferencia clave entre los dos es cómo se comportan frente a los ajustes de reloj de NTP u otras fuentes.

currentTimeMillis() es un reloj de hora del día (también conocido como reloj de tiempo real) que devuelve el tiempo transcurrido desde un
punto de referencia fijo (en este caso, la época de Unix del 1 de enero de 1970). Cuando el cliente NTP avanza el reloj local, un reloj de hora
del día puede saltar. Por lo tanto, si usa un reloj de este tipo para medir el tiempo transcurrido, la diferencia resultante entre la marca de tiempo
final y la marca de tiempo de inicio puede ser mucho mayor que el tiempo transcurrido real (si el reloj se adelantó), o incluso puede ser negativa
(si el reloj se dio un paso atrás). Por lo tanto, este tipo de reloj no es adecuado para medir el tiempo transcurrido.

Por otro lado, nanoTime() es un reloj monótono, que no se ve afectado por los pasos de NTP: sigue contando los segundos transcurridos,
pero siempre avanza. Solo la velocidad a la que se mueve hacia adelante puede ajustarse mediante la rotación de NTP. Esto hace que un reloj
monótono sea mucho más robusto para medir el tiempo transcurrido.
La desventaja es que una marca de tiempo de un reloj monótono no tiene sentido por sí misma: mide el tiempo desde algún punto de referencia
arbitrario, como el tiempo desde que se inició esta computadora. Cuando se usa un reloj monótono, solo tiene sentido la diferencia entre dos
marcas de tiempo del mismo nodo. No tiene sentido comparar marcas de tiempo de reloj monótonas en diferentes nodos.

Relojes monotónicos y de hora del día.

// MALO:
larga startTime = System.currentTimeMillis(); hacer algo();
long endTime = System.currentTimeMillis(); largo
transcurridoMillones = horafinalización - horainicial ; //
¡Millones transcurridos pueden ser negativos!

El cliente NTP avanza el reloj durante este


// BUENO:
larga startTime = System.nanoTime(); hacer
algo(); long endTime = System.nanoTime();
nanos transcurridos largos = horafinal -
horainicial ; // nanos transcurridos siempre es >= 0

Diapositiva 58

La mayoría de los sistemas operativos y lenguajes de programación proporcionan tanto un reloj de la hora del día como un reloj monótono.
reloj, ya que ambos son útiles para diferentes propósitos.

30
Machine Translated by Google

Relojes monotónicos y de hora del día.


Reloj de hora del día: I

Tiempo desde una fecha fija (por ejemplo, época del 1 de enero de 1970)

I Puede moverse repentinamente hacia adelante o hacia atrás


(paso NTP), sujeto a ajustes de segundos intercalares I Las

marcas de tiempo se pueden comparar entre nodos (si están sincronizados)

Yo Java: System.currentTimeMillis()

Yo Linux: clock_gettime(CLOCK_REALTIME)

Reloj monotónico:

I Tiempo desde un punto arbitrario (por ejemplo, cuando la máquina arrancó)


I Siempre avanza a una velocidad casi constante I Bueno

para medir el tiempo transcurrido en un solo nodo I Java:

System.nanoTime()

Yo Linux: clock_gettime(CLOCK_MONOTONIC)
Diapositiva 59

3.3 Causalidad y sucesos anteriores


Pasaremos ahora al problema de ordenar eventos en un sistema distribuido, que está íntimamente relacionado con el
concepto de tiempo. Considere el escenario de la diapositiva 60, en el que el usuario A hace una declaración m1 y la envía
como un mensaje a los otros dos usuarios, B y C. Al recibir m1, el usuario B reacciona enviando una respuesta m2 a los
otros dos usuarios, A y C. Sin embargo, incluso si asumimos que los enlaces de la red son confiables, permiten el
reordenamiento (Diapositiva 33), por lo que C podría recibir m2 antes que m1 si m1 se retrasa un poco en la red.
Desde el punto de vista de C, el resultado es confuso: C primero ve la respuesta y luego el mensaje al que está
respondiendo. Casi parece como si B pudiera ver el futuro y anticipar la declaración de A incluso antes de que A la dijera.
En la vida real, este tipo de reordenamiento de las palabras habladas no ocurre, por lo que intuitivamente tampoco
esperamos que suceda en los sistemas informáticos.
Como ejemplo más técnico, considere m1 como una instrucción que crea un objeto en una base de datos
y m2 como una instrucción que actualiza este mismo objeto. Si un nodo procesa m2 antes que m1 , primero
intentará actualizar un objeto inexistente y luego creará un objeto que no se actualizará posteriormente. Las
instrucciones de la base de datos solo tienen sentido si m1 se procesa antes que m2.

Comienzo del video sección 3.3

Ordenamiento de mensajes (descarga mp4)

usuario A usuario B usuario c

m1

m1
metro2 m2

m1 = “A dice: ¡La luna está hecha de queso!” m2 = “B dice:


¡Oh, no, no lo es!”

C ve m2 primero, m1 segundo,
aunque lógicamente m1 sucedió antes que m2.

Diapositiva 60

¿Cómo puede C determinar el orden correcto en el que debe poner los mensajes? Un reloj monótono no funcionará
ya que sus marcas de tiempo no son comparables entre nodos. Un primer intento podría ser obtener una marca de tiempo
de un reloj de hora del día cada vez que un usuario desee enviar un mensaje y adjuntar esa marca de tiempo al mensaje.
En este escenario, podríamos esperar razonablemente que m2 tenga una marca de tiempo posterior a m1, ya que m2 es
una respuesta a m1 y, por lo tanto , m2 debe haber ocurrido después de m1.
Desafortunadamente, en un modelo de sistema parcialmente síncrono, esto no funciona de manera confiable. La
sincronización del reloj realizada por NTP y protocolos similares siempre deja cierta incertidumbre residual sobre

31
Machine Translated by Google

el sesgo exacto entre dos relojes, especialmente si la latencia de la red en las dos direcciones es asimétrica.
Por lo tanto, no podemos descartar el siguiente escenario: A envía m1 con la marca de tiempo t1 de acuerdo con el reloj de A.
Cuando B recibe m1, la marca de tiempo según el reloj de B es t2, donde t2 < t1, porque el reloj de A está ligeramente adelantado
al reloj de B. Por lo tanto, si ordenamos los mensajes en función de las marcas de tiempo de los relojes que marcan la hora del
día, es posible que nuevamente terminemos con un orden incorrecto.

Marcas de tiempo físicas inconsistentes con la causalidad

usuario A usuario B usuario c

t1 m1

m1
t2
metro2 m2

m1 = (t1, “A dice: ¡La luna está hecha de queso!”) m2


= (t2, “B dice: ¡Oh, no, no lo es!”)

Problema: incluso con relojes sincronizados, t2 < t1 es posible.


¡El orden de la marca de tiempo no coincide con el orden esperado!

Diapositiva 61

Para formalizar lo que queremos decir con el orden "correcto" en este tipo de escenario, usamos la relación sucede antes
como se define en la diapositiva 62. Esta definición asume que cada nodo tiene solo un único hilo de ejecución, por lo que para
cualquier dos pasos de ejecución de un nodo, está claro cuál ocurrió primero. Más formalmente, asumimos que existe un orden
total estricto en los eventos que ocurren en el mismo nodo. Un proceso de subprocesos múltiples se puede modelar utilizando un
nodo separado para representar cada subproceso.
Luego, extendemos este orden a través de los nodos definiendo que un mensaje se envía antes de que se reciba ese mismo
mensaje (en otras palabras, descartamos el viaje en el tiempo: no es posible recibir un mensaje que aún no se ha enviado). Para
mayor comodidad, asumimos que cada mensaje enviado es único, por lo que cuando se recibe un mensaje, siempre sabemos
sin ambigüedades dónde y cuándo se envió ese mensaje. En la práctica, pueden existir mensajes duplicados, pero podemos
hacerlos únicos, por ejemplo, al incluir la ID del nodo emisor y un número de secuencia en cada mensaje.

Finalmente, tomamos la clausura transitiva, y el resultado es la relación sucede antes. Este es un orden parcial, lo que
significa que es posible que para algunos eventos a y b, ni a suceda antes de b, ni b suceda antes de a. En ese caso, llamamos
a y b concurrentes. Tenga en cuenta que aquí, "concurrente" no significa literalmente "al mismo tiempo", sino que a y b son
independientes en el sentido de que no hay una secuencia de mensajes que conduzcan de uno a otro.

La relación pasa antes


Un evento es algo que sucede en un nodo (enviar o recibir un mensaje, o
un paso de ejecución local).

Decimos que el evento a ocurre antes que el evento b (escrito a ÿ b) si y si:


I a y b ocurrieron en el mismo nodo, y a ocurrió antes que b en el
orden de ejecución local de ese nodo; o

I el evento a es el envío de algún mensaje m, y el evento b es la recepción


de ese mismo mensaje m (asumiendo que los mensajes enviados son
únicos); o
I existe un evento c tal que a ÿ c y c ÿ b.

La relación sucede antes es un orden parcial: es posible que ni a ÿ b ni b ÿ


a. En ese caso, a y b son concurrentes (escrito akb).

Diapositiva 62

32
Machine Translated by Google

Ejemplo de relación sucede antes

A B C
a
Y
B m1
C

D m2
F

I a ÿ b, c ÿ d y e ÿ f debido al orden de ejecución del nodo


I b ÿ c y d ÿ f debido a los mensajes m1 y m2
I a ÿ c, a ÿ d, a ÿ f, b ÿ d, b ÿ f, y c ÿ f debido a
transitividad
ake , bke, cke y dke

Diapositiva 63

Ejercicio 6. Una relación R es de orden parcial estricto si es irreflexiva (@a. (a, a) ÿ R) y transitiva (ÿa, b, c. (a, b) ÿ R ÿ (b, c) ÿ R
=ÿ (a, c) ÿ R). (Estas dos condiciones también implican que R es asimétrica, es decir, que ÿa, b. (a, b) ÿ R =ÿ (b, a) ÿ/ R.)
Demuestre que la relación que ocurre antes es un orden parcial estricto.
Puede suponer que dos nodos cualesquiera están separados por una distancia distinta de cero, así como el principio físico de que
la información no puede viajar más rápido que la velocidad de la luz.

Ejercicio 7. Muestre que para dos eventos cualesquiera a y b, exactamente una de las tres afirmaciones siguientes debe ser
verdadera: ya sea a ÿ b, o b ÿ a, o ak b.

La relación sucede antes es una forma de razonar sobre la causalidad en sistemas distribuidos. La causalidad considera si la
información pudo haber fluido de un evento a otro y, por lo tanto, si un evento pudo haber influido en otro. En el ejemplo de la
diapositiva 60, m2 (“¡Oh, no, no lo es!”) es una respuesta a m1 (“¡La luna está hecha de queso!”), por lo que m1 influyó en m2. Si
un evento realmente "causó" otro es una pregunta filosófica que no necesitamos responder ahora; lo que importa para nuestros
propósitos es que el remitente de m2 ya había recibido m1 en el momento de enviar m2.

Causalidad
Tomado de la física (relatividad).
I Cuando a ÿ b, entonces a podría haber causado b.
I Cuando akb, sabemos que a no puede haber causado b.
La relación sucede antes de codificar la causalidad potencial.

distancia en el espacio
a B hora

luz de un luz de b
C

Sea ÿ un orden total estricto de eventos.


Si (a ÿ b) =ÿ (a ÿ b) entonces ÿ es un orden causal (o: ÿ
es “consistente con la causalidad”).
NÓTESE BIEN. “causal” 6= “casual”!
Diapositiva 64

La noción de causalidad se toma prestada de la física, donde generalmente se cree que no es posible que la información viaje
más rápido que la velocidad de la luz. Por lo tanto, si tiene dos eventos a y b que ocurren lo suficientemente separados en el
espacio, pero juntos en el tiempo, entonces es imposible que una señal enviada desde a llegue a la ubicación de b antes del
evento b, y viceversa. Por lo tanto, a y b deben estar causalmente no relacionados.
Un evento c que esté lo suficientemente cerca en el espacio de a, y lo suficientemente largo después de a en el tiempo, estará
dentro del cono de luz de a: es decir, es posible que una señal de a llegue a c, y por lo tanto a podría influir en c. En los sistemas
distribuidos, normalmente trabajamos con mensajes en una red en lugar de haces de luz, pero el principio es muy similar.

33
Machine Translated by Google

4 Protocolos de transmisión y tiempo lógico


En esta lección examinaremos los protocolos de difusión (también conocidos como protocolos de multidifusión), es decir,
algoritmos para entregar un mensaje a múltiples destinatarios. Estos son bloques de construcción útiles para algoritmos
distribuidos de alto nivel, como veremos en la Lección 5.
En la práctica, se utilizan varios protocolos de transmisión diferentes, y su principal diferencia es el orden en que entregan
los mensajes. Como vimos en la última lección, el concepto de ordenar está estrechamente relacionado con los relojes y el
tiempo. Por lo tanto, comenzaremos esta lección examinando más de cerca cómo los relojes pueden ayudarnos a realizar un
seguimiento de los pedidos dentro de un sistema distribuido.

4.1 Tiempo lógico


Los relojes físicos, que analizamos en la Sección 3.1, miden el número de segundos transcurridos. Sin embargo, recuerde que
en la diapositiva 61 vimos que las marcas de tiempo de los relojes físicos pueden ser inconsistentes con la causalidad, incluso
si esos relojes están sincronizados usando algo como NTP. Es decir, si enviar(m) es el evento de enviar el mensaje m, y si la
relación que ocurre antes indica que enviar(m1) ÿ enviar(m2), entonces podría darse el caso de que la marca de tiempo física
de enviar( m1) (según el reloj del remitente de m1) es menor que la marca de tiempo física de envío (m2) (según el reloj del
remitente de m2).
En cambio, los relojes lógicos se enfocan en capturar correctamente el orden de los eventos en un sistema distribuido.

Comienzo del video sección


Relojes lógicos vs. físicos 4.1 (descarga mp4)

I Reloj físico: cuenta el número de segundos transcurridos


I Reloj lógico: cuenta el número de eventos ocurridos

Marcas de tiempo físicas: útiles para muchas cosas, pero pueden ser
inconsistentes con la causalidad.

Relojes lógicos: diseñados para capturar dependencias causales.

(e1 ÿ e2) =ÿ (T(e1) < T(e2))

Veremos dos tipos de relojes lógicos:


I Lamport relojes
I Relojes vectoriales

Diapositiva 65

El primer tipo de reloj lógico que examinaremos es el reloj de Lamport, introducido por Lamport [1978] en
uno de los artículos seminales de la computación distribuida.

Algoritmo de relojes Lamport


en la inicialización do
t := 0 end on . cada nodo tiene su propia variable local t

en cualquier evento que ocurra en el nodo local do


t := t + 1 end on

a petición de enviar mensaje m do t := t +


1; enviar (t, m) a través del extremo del enlace de red subyacente
en

al recibir (t 0 , m) a través del enlace de red subyacente do t :=


max(t, t0) + 1 entregar m al final de la aplicación el

Diapositiva 66

34
Machine Translated by Google

Relojes Lamport en palabras

I Cada nodo mantiene un contador t,


incrementado en cada evento local e
Sea L(e) el valor de t después de ese incremento
Adjunto la t actual a los mensajes enviados a través de la red
I El destinatario adelanta su reloj a la marca de tiempo en el
mensaje (si es mayor que el contador local), luego incrementa

Propiedades de este esquema:


I Si a ÿ b entonces L(a) < L(b)
Sin embargo , L(a) < L(b) no implica a ÿ b
I Posible que L(a) = L(b) para a 6= b

Diapositiva 67

Una marca de tiempo de Lamport es esencialmente un número entero que cuenta el número de eventos que han ocurrido.
Como tal, no tiene relación directa con el tiempo físico. En cada nodo, el tiempo aumenta porque el número entero se incrementa
en cada evento. El algoritmo asume un modelo de parada de bloqueo (o un modelo de recuperación de bloqueo si la marca de
tiempo se mantiene en un almacenamiento estable, es decir, en el disco).
Cuando se envía un mensaje a través de la red, el remitente adjunta su marca de tiempo actual de Lamport a ese mensaje.
En el ejemplo de la diapositiva 68, t = 2 está unido a m1 y t = 4 está unido a m2. Cuando el destinatario recibe un mensaje,
adelanta su reloj Lamport local a la marca de tiempo del mensaje más uno; si el reloj del destinatario ya está adelantado con
respecto a la marca de tiempo del mensaje, solo se incrementa.
Las marcas de tiempo de Lamport tienen la propiedad de que si a sucedió antes de b, entonces b siempre tiene una marca
de tiempo mayor que a; en otras palabras, las marcas de tiempo son consistentes con la causalidad. Sin embargo, lo contrario
no es cierto: en general, si b tiene una marca de tiempo mayor que a, sabemos que b 6ÿ a, pero no sabemos si es el caso de
que a ÿ b o de que ak b.
También es posible que dos eventos diferentes tengan la misma marca de tiempo. En el ejemplo de la diapositiva 68, el
tercer evento en el nodo A y el primer evento en el nodo B tienen una marca de tiempo de 3. Si necesitamos una marca de
tiempo única para cada evento, cada marca de tiempo puede extenderse con el nombre o identificador del nodo. en que se
produjo dicho hecho. Dentro del alcance de un solo nodo, a cada evento se le asigna una marca de tiempo única; por lo tanto,
asumiendo que cada nodo tiene un nombre único, la combinación de marca de tiempo y nombre de nodo es globalmente única
(en todos los nodos).

Ejemplo de relojes Lamport

A B C

(1, A)
(1, C)
(2, A) (2, m1)
(3, B)
(3, A) (4, B)
(4, m2) (5, C)

Sea N(e) el nodo en el que ocurrió el evento e.


Entonces el par (L(e), N(e)) identifica de manera única el evento e.

Defina un pedido total ÿ utilizando las marcas de tiempo de Lamport:

(a ÿ b) ÿÿ (L(a) < L(b) ÿ (L(a) = L(b) ÿ N(a) < N(b)))

Este orden es causal: (a ÿ b) =ÿ (a ÿ b)


Diapositiva 68

Recuerde que la relación sucede antes es un orden parcial (Diapositiva 62). Usando las marcas de tiempo de Lamport
podemos extender este pedido parcial a un pedido total. Usamos el orden lexicográfico sobre pares (marca de tiempo, nombre
de nodo): es decir, primero comparamos las marcas de tiempo y, si son iguales, rompemos los vínculos comparando los nombres
de los nodos.
Esta relación ÿ pone todos los eventos en un orden lineal: para dos eventos cualesquiera a 6= b tenemos a ÿ b o

35
Machine Translated by Google

b ÿ a. Es un orden causal: es decir, siempre que a ÿ b tenemos a ÿ b. En otras palabras, ÿ es una extensión lineal del orden
parcial ÿ. Sin embargo, si akb podríamos tener a ÿ b o b ÿ a, por lo que el algoritmo determina arbitrariamente el orden de los
dos eventos.

Ejercicio 8. Dada la secuencia de mensajes en la siguiente ejecución, muestre las marcas de tiempo de Lamport en cada evento
de envío o recepción.

A B C D
m1
m2
m3

m4
m5

m6
m7

m8
m9

Ejercicio 9. Demostrar que el orden total ÿ utilizando las marcas de tiempo de Lamport es un orden causal.

Dadas las marcas de tiempo de Lamport de dos eventos, en general no es posible saber si esos eventos son concurrentes
o si uno sucedió antes que el otro. Si queremos detectar cuándo los eventos son concurrentes, necesitamos un tipo diferente
de tiempo lógico: un reloj vectorial.
Mientras que las marcas de tiempo de Lamport son solo un número entero (posiblemente con un nombre de nodo adjunto),
las marcas de tiempo de vector son una lista de números enteros, uno para cada nodo en el sistema. Por convención, si
ponemos los n nodos en un vector hN1, N2, . . . , Nni, entonces un vector timestamp es un vector similar ht1, t2, . . . , tni donde
ti es la entrada correspondiente al nodo Ni . Concretamente, ti es el número de eventos que se sabe que han ocurrido en el
nodo Ni .
En un vector T = ht1, t2, . . . , tni nos referimos al elemento ti como T[i], como un índice en una matriz.

Relojes vectoriales

Dadas las marcas de tiempo de Lamport L(a) y L(b) con L(a) < L(b) no
podemos decir si a ÿ b o ak b.

Si queremos detectar qué eventos son concurrentes, necesitamos


relojes vectoriales:

Suponga n nodos en el sistema, N = hN1 , N2, . . . , Nni I La marca


de tiempo del vector del evento a es V (a) = ht1, t2, . . . , tni I ti es
el número de eventos observados por el nodo Ni I Cada nodo tiene
una marca de tiempo del vector actual T I En el evento en el nodo
Ni, incrementa el elemento del vector T[i]
I Adjuntar la marca de tiempo del vector actual a cada mensaje
I El destinatario fusiona el vector del mensaje en su vector local

Diapositiva 69

Aparte de la diferencia entre un escalar y un vector, el algoritmo del reloj vectorial es muy similar a un reloj Lamport
(compare la diapositiva 66 y la diapositiva 70). Un nodo inicializa su reloj vectorial para que contenga un cero, incrementa la i-
el sistema. Cada vez que ocurre un evento en la propia entrada del nodo Ni ) en su reloj , ésima
vectorial.
entrada
(En la(es
práctica,
para cada
estenodo
vector
ena
menudo se implementa como un mapa de ID de nodo a números enteros en lugar de una matriz de números enteros). Cuando
se envía un mensaje a través de la red, la marca de tiempo del vector actual del remitente se adjunta al mensaje. Finalmente,
cuando se recibe un mensaje, el destinatario fusiona la marca de tiempo del vector en el mensaje con su marca de tiempo local
tomando el máximo de elementos de los dos vectores, y luego el destinatario incrementa su propia entrada.

36
Machine Translated by Google

Algoritmo de relojes vectoriales


en la inicialización en el nodo Ni do
T := h0, 0, . . . , 0i termino en . variable local en el nodo Ni

en cualquier evento que ocurra en el nodo Ni do


T[i] := T[i] + 1 fin
en

a petición de enviar el mensaje m en el nodo Ni do


T[i] := T[i] + 1; enviar (T, m) a través de la red
finalizará el

al recibir (T 0 , m) en el nodo Ni a través de la red hacer T[j] :=


max(T[j], T0 [j]) para cada j ÿ {1, . . . n}
T[i] := T[i] + 1; entregar m al final de la aplicación en

Diapositiva 70

La diapositiva 71 muestra un ejemplo de este algoritmo en acción. Tenga en cuenta que cuando C recibe el mensaje m2 de B, la
entrada del vector para A también se actualiza a 2 porque este evento tiene una dependencia causal indirecta de los dos eventos que
ocurrieron en A. De esta manera, las marcas de tiempo del vector reflejan la transitividad de los eventos. -antes de la relación.

Ejemplo de relojes vectoriales

Suponiendo que el vector de nodos es N = hA, B, Ci:

A B C

h1, 0, 0i
h0, 0, 1i
h2, 0, 0i (h2, 0, 0i, m1)
h2, 1, 0i
h3, 0, 0i h2, 2, 0i
(h2, 2, 0i, m2) h2, 2, 2i

El vector timestamp de un evento e representa un conjunto de eventos, e y


sus dependencias causales: {e} ÿ {a | a ÿ e}

Por ejemplo, h2, 2, 0i representa los dos primeros eventos de A, los dos
primeros eventos de B y ningún evento de C.
Diapositiva 71

Pedidos de relojes vectoriales

Defina el siguiente orden en las marcas de tiempo


vectoriales (en un sistema con n nodos):
yo T = T 0
si y si T[i] = T 0 [i] para todo i ÿ {1, . . . , n}
0
yo T ÿ T si y solo T[i] ÿ n}
T 0y [i]
T 6=para
T todo i ÿ {1, . . . ,
0 0
I T < T0 si T ÿ T

si y si T 6ÿ T y T
0 0 0
yo t k t 6ÿT

V (a) ÿ V (b) si y solo si ({a} ÿ {e | e ÿ a}) ÿ ({b} ÿ {e | e ÿ b})

Propiedades de este orden:


Yo (V (a) < V (b)) ÿÿ (a ÿ b)
Yo (V (a) = V (b)) ÿÿ (a = b)
Yo (V (a) k V (b)) ÿÿ (akb)

Diapositiva 72

Luego definimos un orden parcial sobre las marcas de tiempo del vector como se muestra en la diapositiva 72. Decimos que un vector

37
Machine Translated by Google

es menor o igual a otro vector si cada elemento del primer vector es menor o igual al elemento correspondiente del segundo
vector. Un vector es estrictamente menor que otro vector si son menores o iguales y si difieren en al menos un elemento.
Sin embargo, dos vectores son incomparables si un vector tiene un valor mayor en un elemento y el otro tiene un valor mayor
en un elemento diferente.
0
Por ejemplo, T = h2, 2, 0i y T = h0, 0, 1i son incomparables porque T[1] > T0 [1] pero T[3] < T0 [3].
El orden parcial sobre marcas de tiempo vectoriales corresponde exactamente al orden parcial definido por la relación
antes de que suceda. Por lo tanto, el algoritmo de reloj vectorial nos proporciona un mecanismo para calcular la relación
antes de que ocurra en la práctica.

Ejercicio 10. Dada la misma secuencia de mensajes que en el ejercicio 8, muestre los relojes vectoriales en cada evento de
envío o recepción.

Ejercicio 11. Utilizando el Lamport y las marcas de tiempo vectoriales calculadas en los Ejercicios 8 y 10, indique si se puede
determinar que los siguientes eventos tienen o no una relación de ocurrencia anterior.

Eventos Vector de lamport


enviar(m2) enviar(m3)
enviar(m3) enviar(m5)
enviar(m5) enviar(m9)

Ejercicio 12. Hemos visto varios tipos de relojes físicos (relojes horarios con NTP, relojes monotónicos) y relojes lógicos.
Para cada uno de los siguientes usos del tiempo, explique qué tipo de reloj es el más adecuado: programación de procesos;
E/S; consistencia del sistema de archivos distribuido; validez del certificado criptográfico; actualizaciones simultáneas de la
base de datos.

Esto completa nuestra discusión sobre el tiempo lógico. Hemos visto dos algoritmos clave: los relojes Lamport y los
relojes vectoriales, uno que proporciona un orden total y el otro que captura el orden parcial de lo que sucede antes.
Se han propuesto varias otras construcciones: por ejemplo, hay relojes híbridos que combinan algunas de las propiedades
de los relojes lógicos y físicos [Kulkarni et al., 2014].

4.2 Orden de entrega en protocolos de difusión


Muchas redes proporcionan mensajería punto a punto (unidifusión), en la que un mensaje tiene un destinatario específico.
Ahora veremos los protocolos de transmisión, que generalizan la creación de redes de modo que se envía un mensaje a
todos los nodos de algún grupo. La pertenencia al grupo puede ser fija, o el sistema puede proporcionar mecanismos para
que los nodos se unan y abandonen el grupo.
Algunas redes de área local proporcionan multidifusión o difusión a nivel de hardware (por ejemplo, multidifusión IP),
pero la comunicación a través de Internet normalmente solo permite la unidifusión. Además, la multidifusión a nivel de
hardware generalmente se proporciona según el mejor esfuerzo, lo que permite descartar mensajes; hacerlo confiable
requiere protocolos de retransmisión similares a los discutidos aquí.
Las suposiciones del modelo de sistema sobre el comportamiento de los nodos (Diapositiva 34) y la sincronía (Diapositiva 35) se transfieren
directamente a los grupos de transmisión.

Comienzo del video sección


Protocolos de transmisión 4.2 (descarga mp4)

Broadcast (multicast) es comunicación grupal:

I Un nodo envía el mensaje, todos los nodos del grupo lo entregan

I El conjunto de miembros del grupo puede ser fijo (estático) o dinámico

I Si un nodo está defectuoso, los miembros restantes del grupo continúan

Nota: el concepto es más general que la multidifusión IP (nos


basamos en la mensajería punto a punto)

Construya sobre los modelos de sistema de la lección 2:

I Puede ser el mejor esfuerzo (puede dejar mensajes) o


confiable (nodos no defectuosos entregan cada mensaje,
retransmitiendo mensajes perdidos)

I Modelo de temporización asincrónico/parcialmente sincrónico =ÿ sin


límite superior en la latencia del mensaje

Diapositiva 73

38
Machine Translated by Google

Antes de entrar en detalles, debemos aclarar algunos términos. Cuando una aplicación quiere enviar un mensaje a todos
los nodos del grupo, utiliza un algoritmo para transmitirlo. Para que esto suceda, el algoritmo de transmisión envía algunos
mensajes a otros nodos a través de enlaces punto a punto, y otro nodo recibe dicho mensaje cuando llega a través del enlace
punto a punto. Finalmente, el algoritmo de difusión puede entregar el mensaje a la aplicación. Como veremos en breve, a veces
hay un retraso entre el momento en que se recibe un mensaje y cuando se entrega.

Recibir versus entregar


Nodo A: Nodo B:

Solicitud Solicitud

transmisión entregar

Algoritmo de difusión Algoritmo de difusión


(middleware) (middleware)

enviar recibir enviar recibir

La red

Suponga que la red proporciona envío/recepción punto a punto

Después de que el algoritmo de transmisión recibe el mensaje de la red, puede


almacenarlo en un búfer o ponerlo en cola antes de enviarlo a la aplicación.

Diapositiva 74

Examinaremos tres formas diferentes de transmisión. Todos estos son confiables: cada mensaje finalmente se entrega a
cada nodo sin fallas, sin garantías de tiempo. Sin embargo, difieren en cuanto al orden en que se pueden entregar los mensajes
en cada nodo. Resulta que esta diferencia en el orden tiene consecuencias muy fundamentales para los algoritmos que
implementan la transmisión.

Formas de transmisión confiable

Transmisión FIFO:
Si m1 y m2 son transmitidos por el mismo nodo, y broadcast(m1) ÿ
broadcast(m2), entonces m1 debe entregarse antes que m2

Transmisión causal:

Si broadcast (m1) ÿ broadcast (m2) , entonces m1 debe entregarse antes que m2

Emisión total del pedido:


Si m1 se entrega antes que m2 en un nodo, entonces m1 debe entregarse
antes que m2 en todos los nodos

Emisión de pedido total FIFO:


Combinación de transmisión FIFO y transmisión de orden total

Diapositiva 75

El tipo de transmisión más débil se llama transmisión FIFO, que está estrechamente relacionada con los enlaces FIFO
(consulte el Ejercicio 2). En este modelo, los mensajes enviados por el mismo nodo se entregan en el orden en que fueron enviados.
Por ejemplo, en la diapositiva 76, m1 debe entregarse antes que m3, ya que ambos fueron enviados por A. Sin embargo, m2
puede entregarse en cualquier momento antes, entre o después de m1 y m3.
Otro detalle sobre estos protocolos de transmisión: asumimos que cada vez que un nodo transmite un mensaje, también
se entrega ese mensaje a sí mismo (representado como una flecha de bucle invertido en la diapositiva 76). Esto puede parecer
innecesario al principio; después de todo, ¡un nodo sabe qué mensajes ha transmitido él mismo! – pero necesitaremos esto
para la transmisión de orden total.

39
Machine Translated by Google

transmisión FIFO

A B C

m1 m1

m1

m2 m2 m2

m3 m3

m3

Los mensajes enviados por el mismo nodo deben entregarse en el orden en


que fueron enviados.
Los mensajes enviados por diferentes nodos se pueden entregar en cualquier orden.
Pedidos válidos: (m2, m1, m3) o (m1, m2, m3) o (m1, m3, m2)
Diapositiva 76

La ejecución de ejemplo en la diapositiva 76 es una transmisión FIFO válida, pero viola la causalidad: el nodo C entrega m2
antes que m1, aunque B transmite m2 después de entregar m1. La transmisión causal proporciona una propiedad de ordenación
más estricta que la transmisión FIFO. Como sugiere el nombre, garantiza que los mensajes se entreguen en orden causal: es
decir, si la transmisión de un mensaje ocurrió antes de la transmisión de otro mensaje, todos los nodos deben entregar esos
dos mensajes en ese orden. Si se transmiten dos mensajes al mismo tiempo, un nodo puede entregarlos en cualquier orden.

En el ejemplo de la diapositiva 76, si el nodo C recibe m2 antes que m1, el algoritmo de transmisión en C tendrá que retener
(retrasar o amortiguar) m2 hasta que se haya entregado m1 , para garantizar que los mensajes se entreguen en orden causal.
En el ejemplo de la diapositiva 77, los mensajes m2 y m3 se transmiten simultáneamente. Los nodos A y C entregan mensajes
en el orden m1, m3, m2, mientras que el nodo B los entrega en el orden m1, m2, m3.
Cualquiera de estas órdenes de entrega es aceptable, ya que ambas son consistentes con la causalidad.

Transmisión causal

A B C

m1 m1

m1

m3 m2 m2
metro3

m3

Los mensajes relacionados causalmente deben entregarse en orden causal.


Los mensajes simultáneos se pueden entregar en cualquier orden.

Aquí: emisión(m1) ÿ emisión(m2) y emisión(m1) ÿ


emisión(m3) =ÿ órdenes válidas son: (m1, m2, m3) o (m1,
m3, m2)
Diapositiva 77

El tercer tipo de transmisión es la transmisión de orden total, a veces también conocida como transmisión atómica.
Si bien FIFO y la transmisión causal permiten que diferentes nodos entreguen mensajes en diferentes órdenes, la transmisión
de orden total impone la coherencia entre los nodos, lo que garantiza que todos los nodos entreguen mensajes en el mismo
orden. El orden de entrega preciso no está definido, siempre que sea el mismo en todos los nodos.
Las diapositivas 78 y 79 muestran dos ejecuciones de ejemplo de transmisión de orden total. En la diapositiva 78, los tres
nodos entregan los mensajes en el orden m1, m2, m3, mientras que en la diapositiva 79, los tres nodos entregan los mensajes
en el orden m1, m3, m2. Cualquiera de estas ejecuciones es válida, siempre que los nodos estén de acuerdo.
Al igual que con la transmisión causal, es posible que los nodos deban retener mensajes, esperando otros mensajes que
deben entregarse primero. Por ejemplo, el nodo C podría recibir los mensajes m2 y m3 en cualquier orden. Si el algoritmo ha
determinado que m3 debe entregarse antes que m2, pero si el nodo C recibe m2 primero, entonces C deberá retener m2 hasta
que se haya recibido m3 .
Otro detalle importante se puede ver en estos diagramas: en el caso de FIFO y transmisión causal,

40
Machine Translated by Google

cuando un nodo transmite un mensaje, puede entregar ese mensaje a sí mismo de inmediato, sin tener que
esperar la comunicación con cualquier otro nodo. Esto ya no es cierto en la transmisión de orden total: por ejemplo,
en la diapositiva 78, es necesario entregar m2 antes que m3, por lo que la entrega de m3 del nodo A a sí mismo debe esperar hasta después
A ha recibido m2 de B. Del mismo modo, en la diapositiva 79, la entrega de m2 del nodo B a sí mismo debe esperar por m3.

Emisión de pedido total (1)

A B C

m1 m1

m1

m3 m2 m2
m3

m3

Todos los nodos deben entregar mensajes en el mismo orden


(aquí: m1, m2, m3)

¡Esto incluye las entregas de un nodo a sí mismo!


Diapositiva 78

Emisión de pedido total (2)

A B C

m1 m1

m1

m2 m2
m3
m3
m3

m2

Todos los nodos deben entregar mensajes en el mismo orden


(aquí: m1, m3, m2)

¡Esto incluye las entregas de un nodo a sí mismo!


Diapositiva 79

Relaciones entre modelos de transmisión

Emisión de orden FIFO-total

Orden total
Transmisión causal
transmisión

transmisión FIFO

Transmisión confiable

Mejor esfuerzo
transmisión
= más fuerte que

Diapositiva 80

41
Machine Translated by Google

Finalmente, la transmisión de orden total FIFO es como la transmisión de orden total, pero con el requisito FIFO
adicional de que todos los mensajes transmitidos por el mismo nodo se entregan en el orden en que fueron enviados. Los
ejemplos de las diapositivas 78 y 79 son, de hecho, ejecuciones de transmisión de orden total FIFO válidas, ya que m1 se
entrega antes que m3 en ambos.

Ejercicio 13. Demostrar que la transmisión causal también satisface los requisitos de la transmisión FIFO, y que la transmisión
de orden total FIFO también satisface los requisitos de la transmisión causal.

4.3 Algoritmos de transmisión


Ahora pasaremos a los algoritmos para implementar la transmisión. En términos generales, esto implica dos pasos: primero,
asegurarse de que todos los nodos reciban todos los mensajes; y segundo, entregar esos mensajes en el orden correcto.
Primero veremos cómo diseminar los mensajes de manera confiable.
El primer algoritmo que podríamos intentar es: cuando un nodo quiere transmitir un mensaje, envía ese mensaje
individualmente a todos los demás nodos, utilizando enlaces confiables como se explica en la diapositiva 33 (es decir,
retransmitiendo mensajes perdidos). Sin embargo, podría suceder que se pierda un mensaje y el remitente se bloquee antes
de retransmitirlo. En esta situación, uno de los nodos nunca recibirá ese mensaje.

Comienzo del video sección


Algoritmos de transmisión 4.3 (descarga mp4)
Descomponerse en dos capas:

1. Haga que la transmisión de mejor esfuerzo sea confiable al retransmitir


mensajes caídos 2.

Hacer cumplir el orden de entrega además de una transmisión confiable

Primer intento: el nodo de transmisión envía un mensaje directamente a todos


los demás nodos

Usar enlaces confiables (reintentar + deduplicar )

I Problema: el nodo puede bloquearse antes de que se entreguen todos los mensajes

A B C

m1
m1

Diapositiva 81

Para mejorar la confiabilidad, podemos contar con la ayuda de los otros nodos. Por ejemplo, podríamos decir que la
primera vez que un nodo recibe un mensaje en particular, lo reenvía a todos los demás nodos (esto se denomina transmisión
confiable ansiosa). Este algoritmo garantiza que incluso si algunos nodos fallan, todos los nodos restantes (no defectuosos)
recibirán todos los mensajes. Sin embargo, este algoritmo es bastante ineficiente: en ausencia de fallas, cada mensaje se
envía O (n veces). Esto significa2 )que
veces
utiliza
en un
unagrupo
gran de
cantidad
n nodos,
de tráfico
ya quedecada
rednodo
redundante.
recibirá todos los mensajes n ÿ 1

Transmisión confiable ansiosa

Idea: la primera vez que un nodo recibe un mensaje en particular, lo vuelve a


transmitir entre sí (a través de enlaces confiables).

A B C

m1

m1
m1 m1
m1

m1

Confiable, pero. . . ¡hasta O(n 2) mensajes para n nodos!

Diapositiva 82

42
Machine Translated by Google

Se han desarrollado muchas variantes de este algoritmo, optimizando en varias dimensiones, como la tolerancia a fallas, el
tiempo hasta que todos los nodos reciben un mensaje y el ancho de banda de red utilizado. Una familia particularmente común de
algoritmos de transmisión son los protocolos de chismes (también conocidos como protocolos epidémicos). En estos protocolos, un
nodo que desea emitir un mensaje lo envía a un pequeño número fijo de nodos que se eligen aleatoriamente. Al recibir un mensaje
por primera vez, un nodo lo reenvía a un número fijo de nodos elegidos al azar. Esto se asemeja a la forma en que los chismes, los
rumores o una enfermedad infecciosa se pueden propagar a través de una población.

Los protocolos de chismes no garantizan estrictamente que todos los nodos recibirán un mensaje: es posible que en la
selección aleatoria de nodos, siempre se omita algún nodo. Sin embargo, si los parámetros del algoritmo se eligen adecuadamente,
la probabilidad de que un mensaje no se entregue puede ser muy pequeña. Los protocolos de chismes son atractivos porque, con
los parámetros correctos, son muy resistentes a la pérdida de mensajes y los bloqueos de nodos, a la vez que siguen siendo
eficientes.

Protocolos de chismes

Útil cuando se transmite a una gran cantidad de nodos.


Idea: cuando un nodo reciba un mensaje por primera vez,
reenviarlo a otros 3 nodos, elegidos al azar.

Eventualmente llega a todos los nodos (con alta probabilidad).

Diapositiva 83

Ahora que tenemos una transmisión confiable (usando una transmisión confiable ansiosa o un protocolo de chismes), podemos
construya transmisiones FIFO, causales o de orden total sobre él. Comencemos con la transmisión FIFO.

Algoritmo de transmisión FIFO

en la inicialización
hacer sendSeq := 0; entregado := h0, 0, . . . , 0i; búfer: = {} final en

a pedido para transmitir m en el nodo Ni do


enviar (i, sendSeq, m) a través de una transmisión
confiable sendSeq := sendSeq + 1 end on

al recibir un mensaje de una transmisión confiable en el nodo Ni do


buffer := buffer ÿ {msg}
mientras ÿremitente, metro. (remitente, entregado[remitente], m) ÿ buffer do
entregar m a la aplicación
entregado[remitente] := entregado[remitente] + 1 final
mientras finaliza el

Diapositiva 84

Cada mensaje de difusión FIFO enviado por el nodo Ni se etiqueta con el número de nodo emisor i y un número de secuencia
que es 0 para el primer mensaje enviado por Ni 1 para el segundo mensaje,,y consiste
así sucesivamente.
en el número
El de
estado
secuencia
local en
sendSeq
cada nodo
(contando la cantidad de mensajes transmitidos por este nodo), entregado (un vector con una entrada por nodo, contando la
cantidad de mensajes de cada remitente que este nodo ha entregado), y búfer (un búfer para retener mensajes hasta que estén
listos para ser entregados). El algoritmo busca mensajes de cualquier remitente que coincidan con el siguiente número de secuencia
esperado y luego incrementa ese número, asegurándose de que los mensajes de cada remitente en particular se entreguen en
orden creciente de número de secuencia.

43
Machine Translated by Google

Algoritmo de transmisión causal


en la inicialización
hacer sendSeq := 0; entregado := h0, 0, . . . , 0i; búfer: = {}
final en

a pedido para transmitir m en el nodo Ni do


deps := entregado; deps[i] := sendSeq
envía (i, deps, m) a través de una transmisión
confiable sendSeq := sendSeq + 1 finaliza

al recibir un mensaje de una transmisión confiable en el nodo


Ni do buffer := buffer ÿ {msg} while ÿ(sender , deps, m) ÿ
buffer . deps ÿ entregado do
entregar m al búfer de la
aplicación := búfer \ {(remitente, deps, m)}
entregado[remitente] := entregado[remitente] +
1 final mientras finaliza el

Diapositiva 85

El algoritmo de transmisión causal es algo similar a la transmisión FIFO; en lugar de adjuntar un número de secuencia
a cada mensaje que se transmite, adjuntamos un vector de números enteros. Este algoritmo a veces se denomina algoritmo
de reloj vectorial, aunque es bastante diferente del algoritmo de la diapositiva 70. En el algoritmo de reloj vectorial de la
diapositiva 70 , los elementos vectoriales cuentan el número de eventos que han ocurrido en cada nodo, mientras que en el
algoritmo causal algoritmo de difusión, los elementos del vector cuentan el número de mensajes de cada remitente que se
han entregado.
El estado local en cada nodo consta de sendSeq, entregado y búfer, que tienen el mismo significado que en el algoritmo
de transmisión FIFO. Cuando un nodo quiere transmitir un mensaje, adjuntamos el número de nodo emisor i y deps, un
vector que indica las dependencias causales de ese mensaje. Construimos deps tomando una copia de delivered, el vector
que cuenta cuántos mensajes de cada remitente se han entregado en este nodo. Esto indica que todos los mensajes que
se han entregado localmente antes de esta transmisión deben aparecer antes del mensaje de transmisión en el orden
causal. Luego, actualizamos el elemento propio del nodo de envío de este vector para que sea igual a sendSeq, lo que
garantiza que cada mensaje transmitido por este nodo tenga una dependencia causal con el mensaje anterior transmitido
por el mismo nodo.
Al recibir un mensaje, el algoritmo primero lo agrega al búfer como en la transmisión FIFO y luego busca en el búfer
cualquier mensaje que esté listo para ser entregado. La comparación deps ÿ entregado utiliza el operador ÿ en los vectores
definidos en la diapositiva 72. Esta comparación es verdadera si este nodo ya entregó todos los mensajes que deben
preceder a este mensaje en el orden causal. Cualquier mensaje que esté causalmente listo se entrega a la aplicación y se
elimina del búfer, y se incrementa el elemento apropiado del vector entregado.

Algoritmos de transmisión de orden total

Enfoque de líder único :


I Un nodo se designa como líder (secuenciador)
I Para transmitir el mensaje, envíelo al líder; líder lo
transmite a través de la transmisión FIFO.
I Problema: el líder falla =ÿ no se entregaron más mensajes
Cambiar el líder de manera segura es difícil

Enfoque de relojes Lamport :


Adjunto la marca de tiempo de Lamport a cada
mensaje Entrego los mensajes en el orden total de
las marcas de tiempo Problema: ¿ cómo sabe si ha visto todos los
mensajes con marca de tiempo < T? Necesita usar enlaces
FIFO y esperar el mensaje con marca de tiempo ÿ T de cada nodo

Diapositiva 86

Finalmente, la transmisión de orden total (y la transmisión de orden total FIFO) son más complicadas. En la diapositiva
86 se describen dos enfoques simples , uno basado en un nodo líder designado y un algoritmo sin líder que utiliza marcas
de tiempo de Lamport. Sin embargo, ninguno de estos enfoques es tolerante a fallas: en ambos casos, el bloqueo de un solo

44
Machine Translated by Google

node puede impedir que todos los demás nodos puedan entregar mensajes. En el enfoque de líder único, el líder es un único
punto de falla. Volveremos al problema de la transmisión de orden total tolerante a fallas en la lección 6.

Ejercicio 14. Proporcione un pseudocódigo para un algoritmo que implemente la transmisión de orden total FIFO utilizando
relojes de puerto Lam. Puede suponer que cada nodo tiene una ID única y que se conoce el conjunto de todas las ID de nodo.
Suponga además que la red subyacente proporciona una transmisión FIFO confiable. [2020 Documento 5 Pregunta 8]

5 replicación
Ahora abordaremos el problema de la replicación, lo que significa mantener una copia de los mismos datos en varios nodos,
cada uno de los cuales se denomina réplica. La replicación es una característica estándar de muchas bases de datos
distribuidas, sistemas de archivos y otros sistemas de almacenamiento. Es uno de los principales mecanismos que tenemos
para lograr la tolerancia a fallas: si una réplica falla, podemos continuar accediendo a las copias de los datos en otras réplicas.

Comienzo del video sección


Replicación 5.1 (descarga mp4)

I Conservar una copia de los mismos datos en varios nodos I Bases de datos,

sistemas de archivos, cachés, . . .

I Un nodo que tiene una copia de los datos se denomina réplica . I Si algunas réplicas

son defectuosas, otras siguen estando accesibles . I Distribuya la carga entre muchas

réplicas . I Fácil si los datos no cambian: simplemente cópielos. I Nos centraremos

en los datos. cambios

Comparar con RAID (matriz redundante de discos independientes): replicación dentro de


una sola computadora

I RAID tiene un solo controlador; En un sistema distribuido, cada nodo actúa de forma
independiente.

I Las réplicas se pueden distribuir en todo el mundo, cerca de los usuarios.

Diapositiva 87

5.1 Manipulación del estado remoto


Si los datos no cambian, la replicación es fácil, ya que solo requiere hacer una copia única de los datos.
Por lo tanto, el principal problema de la replicación es gestionar los cambios en los datos. Antes de entrar en los detalles de
la replicación, veamos cómo ocurren los cambios de datos en un sistema distribuido.

Reintentando actualizaciones de estado


Usuario A: ¡La luna en realidad no está hecha de queso!

Me gusta A 12.300 personas les gusta esto.

cliente

incrementar post.me gusta


12,301
Pobre de mí

incrementar post.me gusta


12,302
Pobre de mí

La deduplicación de solicitudes requiere que la base de datos realice un seguimiento de las

solicitudes que ya ha visto (en almacenamiento estable)


Diapositiva 88

45
Machine Translated by Google

Consideremos como ejemplo el acto de "gustar" una actualización de estado en una red social. Cuando hace clic en el botón
"me gusta", el hecho de que le haya gustado y la cantidad de personas a las que les ha gustado deben almacenarse en algún lugar
para que pueda mostrárselo a usted y a otros usuarios. Esto suele ocurrir en una base de datos en los servidores de la red social.
Podemos considerar los datos almacenados en una base de datos como su estado.
Una solicitud para actualizar la base de datos puede perderse en la red, o puede perderse un reconocimiento de que se ha
realizado una actualización. Como de costumbre, podemos mejorar la confiabilidad volviendo a intentar la solicitud. Sin embargo,
si no tenemos cuidado, el reintento podría provocar que la solicitud se procese varias veces, lo que provocaría un estado incorrecto
en la base de datos.

Diapositiva 89

Para que no piense que se trata de un problema puramente hipotético, considere la diapositiva 89, una captura de pantalla
genuina (¡lo prometo!) de Twitter que hice en 2014, que muestra el perfil de un usuario que aparentemente sigue a un número
negativo de personas. No tengo una idea de las funciones internas de Twitter para saber exactamente qué sucedió aquí, pero
supongo que esta persona solía seguir a varias personas, luego las dejó de seguir y, debido a un problema de red durante el
proceso de dejar de seguir, la disminución del contador de seguimiento se volvió a intentar, lo que resultó en más decrementos de
los que el usuario estaba siguiendo originalmente.
Si seguir a un número negativo de personas parece un problema trivial, entonces, en lugar de disminuir un contador de
seguimiento, considere el acto de deducir £ 1,000 del saldo de su cuenta bancaria. La operación de la base de datos es
esencialmente la misma, pero realizar esta operación demasiadas veces tiene el potencial de hacerte sentir bastante infeliz.

Una forma de evitar que una actualización surta efecto varias veces es desduplicar las solicitudes. Sin embargo, en un modelo
de sistema de recuperación de fallas, esto requiere almacenar solicitudes (o algunos metadatos sobre solicitudes, como un reloj
vectorial) en un almacenamiento estable, para que los duplicados puedan detectarse con precisión incluso después de una falla.
Una alternativa al registro de solicitudes de deduplicación es hacer que las solicitudes sean idempotentes.

idempotencia

Una función f es idempotente si f(x) = f(f(x)).

I No idempotente: f(likeCount) = likeCount + 1 I Idempotente: f(likeSet) =

likeSet ÿ {userID}

Las solicitudes idempotentes se pueden volver a intentar sin desduplicación.

Elección del comportamiento de reintento:

I A lo sumo una vez semántica:

enviar solicitud, no volver a intentarlo, es posible que no se actualice


I Al menos una vez semántica:

reintentar la solicitud hasta que se reconozca, puede repetir la actualización

I Semántica exactamente una vez :


reintento + idempotencia o deduplicación

Diapositiva 90

46
Machine Translated by Google

Incrementar un contador no es idempotente, pero agregar un elemento a un conjunto sí lo es. Por lo tanto, si se requiere un
contador (como en el número de Me gusta), podría ser mejor mantener el conjunto de elementos en la base de datos y derivar el
valor del contador del conjunto calculando su cardinalidad.
Se puede volver a intentar una actualización idempotente de forma segura, porque realizarla varias veces tiene el mismo efecto
que realizarla una vez. La idempotencia permite que una actualización tenga una semántica exactamente una vez: es decir, la
actualización se puede aplicar varias veces, pero el efecto es el mismo que si se hubiera aplicado exactamente una vez.
La idempotencia es una propiedad muy útil en los sistemas prácticos y, a menudo, se encuentra en el contexto de RPC (Sección
1.3), donde los reintentos suelen ser inevitables.
Sin embargo, la idempotencia tiene una limitación que se hace evidente cuando hay varias actualizaciones en curso. En la
diapositiva 91, el cliente 1 agrega una identificación de usuario al conjunto de Me gusta de una publicación, pero se pierde el
reconocimiento. El cliente 2 lee el conjunto de Me gusta de la base de datos (incluida la ID de usuario agregada por el cliente 1) y
luego realiza una solicitud para eliminar la ID de usuario nuevamente. Mientras tanto, el cliente 1 vuelve a intentar su solicitud, sin
darse cuenta de la actualización realizada por el cliente 2. Por lo tanto, el reintento tiene el efecto de agregar nuevamente la ID de usuario al conjunto.
Esto es inesperado ya que el cliente 2 observó el cambio del cliente 1, por lo que la eliminación ocurrió causalmente después de
agregar el elemento del conjunto y, por lo tanto, podríamos esperar que en el estado final, la ID de usuario no esté presente en el
conjunto. En este caso, el hecho de que agregar un elemento a un conjunto sea idempotente no es suficiente para que el reintento
sea seguro.

Agregando y luego eliminando de nuevo

cliente 1 cliente 2
f: agregar como

Pobre de mí

conjunto de me gusta

g: a diferencia

f: agregar como Pobre de mí

Pobre de mí

f(me gusta) = me gusta ÿ {ID de usuario}


g(me gusta) = me gusta \ {ID de usuario}
¿Idempotente? f(f(x)) = f(x) pero f(g(f(x)) 6= g(f(x))

Diapositiva 91

Un problema similar ocurre en la diapositiva 92, en la que tenemos dos réplicas. En el primer escenario, un cliente primero
agrega x a ambas réplicas de la base de datos, luego intenta eliminar x nuevamente de ambas. Sin embargo, la solicitud de
eliminación a la réplica B se pierde y el cliente se bloquea antes de que pueda volver a intentarlo. En el segundo escenario, un
cliente intenta agregar x a ambas réplicas, pero la solicitud a la réplica A se pierde y nuevamente el cliente falla.

Otro problema con agregar y quitar


cliente
agregar
A B
(x)

agregar (x)

eliminar (x) eliminar (x)

El estado final (x /ÿ A, x ÿ B) es el mismo que en este caso:

cliente
agregar
A B
(x)

sumar (x)

Diapositiva 92

En ambos escenarios, el resultado es el mismo: x está presente en la réplica B y está ausente en la réplica A.

47
Machine Translated by Google

Sin embargo, el efecto deseado es diferente: en el primer escenario, el cliente quería que x se eliminara de ambas réplicas, mientras
que en el segundo escenario, el cliente quería que x estuviera presente en ambas réplicas. Cuando las dos réplicas reconcilien sus
estados inconsistentes, queremos que ambas terminen en el estado que el cliente pretendía. Sin embargo, esto no es posible si las
réplicas no pueden distinguir entre estos dos escenarios.
Para solucionar este problema, podemos hacer dos cosas. Primero, adjuntamos una marca de tiempo lógica a cada operación
de actualización y almacenamos esa marca de tiempo en la base de datos como parte de los datos escritos por la actualización. En
segundo lugar, cuando se nos pide que eliminemos un registro de la base de datos, en realidad no lo eliminamos, sino que escribimos
un tipo especial de actualización (llamada lápida) que lo marca como eliminado. En la diapositiva 93, los registros que contienen
falsos son lápidas.

Marcas de tiempo y lápidas

cliente
A B

(t1,
t1
sumar(x)) {x 7ÿ (t1, verdadero)}
{x 7ÿ (t1, verdadero)}
(t1, sumar(x)) (t2,
t2
quitar(x)) (t2, quitar(x))
{x 7ÿ (t2, falso)}

"eliminar (x)" en realidad no elimina x: etiqueta x con "falso" para


indicar que es invisible (una lápida)

Cada registro tiene una marca de tiempo lógica de la última escritura

Diapositiva 93

En muchos sistemas replicados, las réplicas ejecutan un protocolo para detectar y reconciliar cualquier diferencia (esto se
denomina anti-entropía), de modo que las réplicas eventualmente contengan copias consistentes de los mismos datos. Gracias a las
lápidas, el proceso de antientropía puede diferenciar entre un registro que se ha eliminado y un registro que aún no se ha creado. Y
gracias a las marcas de tiempo, podemos saber qué versión de un registro es más antigua y cuál es más nueva. El proceso de anti-
entropía luego mantiene el registro más nuevo y descarta el más antiguo.
Este enfoque también ayuda a abordar el problema de la diapositiva 91: una solicitud reintentada tiene la misma marca de
tiempo que la solicitud original, por lo que un reintento no sobrescribirá un valor escrito por una solicitud causalmente posterior con
una marca de tiempo mayor.

Reconciliación de réplicas

Las réplicas se comunican periódicamente entre sí para comprobar


si hay incoherencias.

A B

reconciliar estado
{x 7ÿ (t2, falso)} {x 7ÿ (t1, verdadero)}
(anti-entropía)

{x 7ÿ (t2, falso)} t1 < t2 {x 7ÿ (t2, falso)}

Propaga el registro con la marca de tiempo más reciente,


descarta los registros con marcas de tiempo anteriores
(para una clave dada).

Diapositiva 94

La técnica de adjuntar una marca de tiempo a cada actualización también es útil para manejar actualizaciones simultáneas.
En la diapositiva 95, el cliente 1 quiere establecer la clave x en el valor v1 (con marca de tiempo t1), mientras que el cliente 2 quiere
establecer la misma clave x en el valor v2 (con marca de tiempo t2). La réplica A recibe v2 primero y v1 segundo, mientras que la
réplica B recibe las actualizaciones en el orden opuesto. Para garantizar que ambas réplicas terminen en el mismo estado, no nos
basamos en el orden en que reciben las solicitudes, sino en el orden de sus marcas de tiempo.

48
Machine Translated by Google

Escrituras simultáneas de diferentes clientes

cliente 1 cliente 2
A B

t1 (t1, conjunto (x, v1)) (t2, conjunto (x, v2)) t2

Dos enfoques comunes:


I El último escritor gana (LWW):
use marcas de tiempo con orden total (por ejemplo, reloj Lamport)
Mantenga v2 y descarte v1 si t2 > t1. Nota: ¡pérdida de datos!
I Registro multivalor:
Usar marcas de tiempo con orden parcial (por ejemplo, reloj
vectorial) v2 reemplaza a v1 si t2 > t1; preservar ambos {v1, v2} si t1 k t2

Diapositiva 95

Los detalles de este enfoque dependen del tipo de marcas de tiempo utilizadas. Si usamos relojes Lamport (con el orden
total definido en la diapositiva 68), se ordenarán arbitrariamente dos actualizaciones simultáneas, dependiendo de cómo se
asignen las marcas de tiempo. En este caso, obtenemos lo que se conoce como la semántica del último escritor gana (LWW): la
actualización con la marca de tiempo más grande entra en vigor y se descartan las actualizaciones simultáneas con marcas de
tiempo más bajas para la misma clave. Es fácil trabajar con este enfoque, pero implica la pérdida de datos cuando se realizan
varias actualizaciones al mismo tiempo. Si esto es un problema o no, depende de la aplicación: en algunos sistemas, descartar
actualizaciones concurrentes está bien.
Cuando descartar actualizaciones concurrentes no es aceptable, necesitamos usar un tipo de marca de tiempo que nos
permita detectar cuándo ocurren actualizaciones concurrentes, como relojes vectoriales. Con tales marcas de tiempo parcialmente
ordenadas, podemos saber cuándo un nuevo valor debe sobrescribir un valor anterior (cuando la actualización anterior ocurrió
antes de la nueva actualización), y cuando varias actualizaciones son simultáneas, podemos mantener todos los valores escritos
simultáneamente. Estos valores escritos simultáneamente se denominan conflictos o, a veces, hermanos. La aplicación puede
luego fusionar los conflictos en un solo valor, como se discutió en la Lección 8.
Una desventaja de los relojes vectoriales es que pueden volverse costosos: cada cliente necesita una entrada en el vector,
y en sistemas con una gran cantidad de clientes (o donde los clientes asumen una nueva identidad cada vez que se reinician),
estos vectores pueden llegar a ser grandes. potencialmente ocupando más espacio que los propios datos.
Otros tipos de relojes lógicos, como los vectores de versión punteada [Pregui¸ca et al., 2010], se han desarrollado para optimizar
este tipo de sistema.

Ejercicio 15. Apache Cassandra, una base de datos distribuida ampliamente utilizada, utiliza un enfoque de replicación similar al
que se describe aquí. Sin embargo, utiliza marcas de tiempo físicas en lugar de marcas de tiempo lógicas, como se explica aquí:
https://www.datastax.com/ blog/ 2013/ 09/why-cassandra-doesnt-need-vector-clocks. Escriba una crítica de esta publicación de
blog. ¿Qué opinas de sus argumentos y por qué? ¿Qué hechos faltan en él? ¿Qué recomendación le harías a alguien que esté
considerando usar Cassandra?

5.2 Quórumes
Como se discutió al comienzo de esta lección, la replicación es útil ya que nos permite mejorar la confiabilidad de un sistema:
cuando una réplica no está disponible, las réplicas restantes pueden continuar procesando solicitudes.
La falta de disponibilidad puede deberse a un nodo defectuoso (p. ej., un bloqueo o una falla de hardware), a una partición de la
red (incapacidad para acceder a un nodo a través de la red) o a un mantenimiento planificado (p. ej., reiniciar un nodo para
instalar actualizaciones de software).
Sin embargo, los detalles de cómo se realiza exactamente la replicación tienen un gran impacto en la confiabilidad del
sistema. Sin tolerancia a fallas, tener varias réplicas empeoraría la confiabilidad: cuantas más réplicas tenga, mayor será la
probabilidad de que alguna de las réplicas sea defectuosa en un momento dado (suponiendo que las fallas no estén perfectamente
correlacionadas). Sin embargo, si el sistema continúa funcionando a pesar de algunas réplicas defectuosas, la confiabilidad
mejora: la probabilidad de que todas las réplicas sean defectuosas al mismo tiempo es mucho menor que la probabilidad de que
una réplica sea defectuosa.

49
Machine Translated by Google

Comienzo del video sección


Probabilidad de fallas 5.2 (descarga mp4)
Es posible que una réplica no esté disponible debido a una partición de la red
oa un error del nodo (p. ej., bloqueo, problema de hardware).

Suponga que cada réplica tiene una probabilidad p de ser defectuosa


o no disponible en un momento dado, y que las fallas son independientes.
(¡En realidad no es cierto! Pero está bien la aproximación por ahora).

norte

Probabilidad de que todas las n réplicas sean defectuosas: p


Probabilidad de que ÿ 1 de n réplicas sea defectuosa: 1 ÿ (1 ÿ p) n

Ejemplo con p = 0,01:


n+1
réplicas n P(ÿ 1 defectuoso) P(ÿ 0,01 2 defectuoso) P(todos los n
0,03 defectuosos) 0,01 0,01 3 1· 10ÿ4
· 10ÿ510ÿ6
13 10ÿ10 6 · 10ÿ74 10ÿ200
5 0.049
100 0,63
Diapositiva 96

Ahora exploraremos cómo lograr la tolerancia a fallas en la replicación. Para comenzar, considere el ejemplo de la
diapositiva 97. Suponga que tenemos dos réplicas, A y B, que inicialmente asocian la clave x con un valor v0 (y la marca de
tiempo t0). Un cliente intenta actualizar el valor de x a v1 (con marca de tiempo t1). Logra actualizar B, pero la actualización
de A falla porque A no está disponible temporalmente. Posteriormente, el cliente intenta volver a leer el valor que ha escrito;
la lectura tiene éxito en A pero falla en B. Como resultado, la lectura no devuelve el valor v1 previamente escrito por el
mismo cliente, sino el valor inicial v0.

Coherencia de lectura después de escritura

cliente
A B

(t1, conjunto (x, v1))


t1

obtener (x)

(t0, v0)

Escribiendo en una réplica, leyendo de otra: el cliente no


leer de nuevo el valor que ha escrito

Requerir escritura/lectura de ambas réplicas =ÿ no se puede escribir/


leer si una réplica no está disponible
Diapositiva 97

Este escenario es problemático, ya que desde el punto de vista del cliente parece que se ha perdido el valor que ha
escrito. Imagine que publica una actualización en una red social, luego actualiza la página y no ve la actualización que acaba
de publicar. Como este comportamiento es confuso para los usuarios, muchos sistemas requieren coherencia de lectura
después de escritura (también conocida como coherencia de lectura de sus escrituras), en la que nos aseguramos de que
después de que un cliente escriba un valor, el mismo cliente podrá volver a leer el mismo valor de la base de datos.
Estrictamente hablando, con consistencia de lectura después de escritura, después de escribir, es posible que un cliente
no lea el valor que escribió porque, al mismo tiempo, otro cliente puede haber sobrescrito el valor. Por lo tanto, decimos que
la consistencia de lectura después de escritura requiere leer el último valor escrito o un valor posterior.
En la diapositiva 97 , podríamos garantizar la coherencia de lectura tras escritura asegurándonos de que siempre
escribimos en ambas réplicas y/o leemos de ambas réplicas. Sin embargo, esto significaría que las lecturas y/o escrituras
ya no son tolerantes a fallas: si una réplica no está disponible, una escritura o lectura que requiera respuestas de ambas
réplicas no podrá completarse.
Podemos resolver este enigma usando tres réplicas, como se muestra en la diapositiva 98. Enviamos cada solicitud de
lectura y escritura a las tres réplicas, pero consideramos que la solicitud fue exitosa siempre que recibamos ÿ 2 respuestas.
En el ejemplo, la escritura se realiza correctamente en las réplicas B y C, mientras que la lectura se realiza correctamente
en las réplicas A y B. Con una política de "2 de 3" tanto para lecturas como para escrituras, se garantiza que al menos una
de las respuestas a una read es de una réplica que vio la escritura más reciente (en el ejemplo, esta es la réplica B).

50
Machine Translated by Google

Quórum (2 de 3)
cliente
A B C

(t1, conjunto (x, v1))


t1

OK OK

obtener (x)

(t0, v0) (t1, v1)

La escritura tiene éxito en B y C; la lectura tiene éxito en A y B


Elija entre (t0, v0) y (t1, v1) según la marca de tiempo Diapositiva 98

Diferentes réplicas pueden devolver diferentes respuestas a la misma solicitud de lectura: en la diapositiva 98, la lectura en A
devuelve el valor inicial (t0, v0), mientras que la lectura en B devuelve el valor (t1, v1) escrito previamente por este cliente. Usando
las marcas de tiempo, el cliente puede saber qué respuesta es la más reciente y devolver v1 a la aplicación.

En este ejemplo, el conjunto de réplicas {B, C} que respondió a la solicitud de escritura es un quórum de escritura y el conjunto
{A, B} que respondió a la lectura es un quórum de lectura. En general, un quórum es un conjunto mínimo de nodos que deben
responder a alguna solicitud para que tenga éxito. (El término proviene de la política, donde el quórum se refiere al número mínimo
de votos necesarios para tomar una decisión válida, por ejemplo, en un parlamento o comité). el quórum para la lectura debe tener
una intersección no vacía: en otras palabras, el quórum de lectura debe contener al menos un nodo que haya reconocido la escritura.

Una opción común de quórum en los sistemas distribuidos es un quórum mayoritario, que es cualquier subconjunto de nodos
que comprende estrictamente más de la mitad de los nodos. En un sistema con tres nodos {A, B, C}, los quórumes mayoritarios son
{A, B}, {A, C} y {B, C}. En general, en un sistema con un número impar de nodos n, cualquier subconjunto de tamaño n+1 es un
quórum mayoritario (2 de 3, o2 3 de
un5,quórum
. . . ). Con un número par de nodos n, esto debe redondearse a 2 . Por ejemplo, 3 de 4 forman
mayoritario.
n+1 = n+2
2
Los quórumes mayoritarios tienen la propiedad de que dos quórumes cualesquiera siempre tienen al menos un elemento en común.
Sin embargo, también son posibles otras construcciones de quórum además de las mayorías.

Cuórums de lectura y escritura


En un sistema con n réplicas:
I Si una escritura es reconocida por w réplicas (escribe quórum),
I y posteriormente leemos de r réplicas (leer quórum),
yo y r + w > n,
I . . . luego, la lectura verá el valor escrito previamente
(o un valor que posteriormente lo sobrescribió)
I Quórum de lectura y quórum de escritura participación ÿ 1
n+1
réplica I Típico: r = w = I Las para npueden
2 lecturas = 3, 5,tolerar
7, . . .nÿr
(mayoria)
réplicas no disponibles, las escrituras nÿw

A B C D Y

leer su escribir quórum


Diapositiva 99

Un sistema que requiere reconocimientos w para escrituras (es decir, un quórum de escritura de tamaño w) puede continuar
procesando actualizaciones siempre que no haya más de n ÿ w réplicas disponibles, y un sistema que requiere respuestas r para
lecturas puede continuar leyendo siempre que no haya más de n ÿ w réplicas disponibles. más de n ÿ r réplicas no están disponibles.
Con quórumes mayoritarios, esto significa que un sistema de tres réplicas puede tolerar que una réplica no esté disponible, un
sistema de cinco réplicas puede tolerar que dos no estén disponibles, y así sucesivamente.

51
Machine Translated by Google

En este enfoque de quórum para la replicación, es posible que falten algunas actualizaciones de algunas réplicas en un
momento dado: por ejemplo, en la diapositiva 98, falta la actualización (t1, v1) de la réplica A, ya que se eliminó esa solicitud
de escritura. Para volver a sincronizar las réplicas entre sí, un enfoque es confiar en un proceso anti-entropía, como se explica
en la diapositiva 94.

Leer reparación

cliente
A B C

obtener (x)

(t0, v0) (t1, v1)

(t1, conjunto (x, v1))

La actualización (t1, v1) es más reciente que (t0, v0) ya que t0 < t1.
El cliente ayuda a propagar (t1, v1) a otras réplicas.
Diapositiva 100

Otra opción es conseguir que los clientes ayuden con el proceso de difusión de actualizaciones. Por ejemplo, en la
diapositiva 100, el cliente lee (t1, v1) de B, pero recibe un valor anterior (t0, v0) de A y no responde de C. Dado que el cliente
ahora sabe que la actualización (t1, v1 ) debe propagarse a A, puede enviar esa actualización a A (utilizando la marca de
tiempo original t1, ya que no se trata de una nueva actualización, solo un reintento de una actualización anterior). El cliente
también puede enviar la actualización a C, aunque no sepa si C la necesita (si resulta que C ya tiene esta actualización, solo
se desperdicia una pequeña cantidad de ancho de banda de red). Este proceso se denomina reparación de lectura. El cliente
puede realizar una reparación de lectura en cualquier solicitud de lectura que realice, independientemente de si fue el cliente
el que realizó originalmente la actualización en cuestión.
Las bases de datos que utilizan este modelo de replicación a menudo se denominan estilo Dynamo, en honor a la base
de datos Dynamo de Amazon [DeCandia et al., 2007], que la popularizó. Sin embargo, el enfoque en realidad es anterior a
Dynamo [Attiya et al., 1995].

5.3 Replicación mediante difusión


El enfoque de quórum de la Sección 5.2 utiliza esencialmente la transmisión de mejor esfuerzo: un cliente transmite cada
solicitud de lectura o escritura a todas las réplicas, pero el protocolo no es confiable (las solicitudes pueden perderse) y no
ofrece garantías de orden.
Un enfoque alternativo para la replicación es usar los protocolos de transmisión de la lección 4. Primero
Considere la transmisión de orden total FIFO, la forma más fuerte de transmisión que hemos visto.

Comienzo del video sección


Replicación de máquinas de estado 5.3 (descarga mp4)

Hasta ahora, hemos utilizado la transmisión de mejor esfuerzo para la replicación.


¿Qué pasa con los modelos de transmisión más fuertes?

Difusión de orden total: cada nodo entrega los mismos mensajes


en el mismo orden

Replicación de máquinas de estado (SMR):

I FIFO-pedido total transmite cada actualización a todas las réplicas

I Replica entrega un mensaje de actualización: aplíquelo a su propio estado

Aplicar una actualización es determinista

I La réplica es una máquina de estado: comienza en un estado inicial fijo,


pasa por la misma secuencia de transiciones de estado en el mismo
orden =ÿ todas las réplicas terminan en el mismo estado

Diapositiva 101

52
Machine Translated by Google

Al usar la transmisión de orden total FIFO, es fácil construir un sistema replicado: transmitimos cada solicitud de
actualización a las réplicas, que actualizan su estado en función de cada mensaje a medida que se entrega. Esto se
denomina replicación de máquina de estado (SMR), porque una réplica actúa como una máquina de estado cuyas
entradas son entregas de mensajes. Solo requerimos que la lógica de actualización sea determinista: dos réplicas
cualesquiera que estén en el mismo estado y reciban la misma entrada, deben terminar en el mismo estado siguiente.
Incluso los errores deben ser deterministas: si una actualización tiene éxito en una réplica pero falla en otra, se volverían inconsistentes.
Una característica excelente de SMR es que la lógica para pasar de un estado al siguiente puede ser arbitrariamente
compleja, siempre que sea determinista. Por ejemplo, se puede ejecutar una transacción de base de datos completa con
lógica empresarial arbitraria, y esta lógica puede depender tanto del mensaje de difusión como del estado actual de la
base de datos. Algunas bases de datos distribuidas realizan la replicación de esta manera, y cada réplica ejecuta de forma
independiente el mismo código de transacción determinista (esto se conoce como replicación activa). Este principio
también es la base de las cadenas de bloques, las criptomonedas y los registros distribuidos: la "cadena de bloques" en
una cadena de bloques no es más que la secuencia de mensajes entregados por un protocolo de transmisión de orden
total (más sobre esto en la Lección 6), y cada réplica se ejecuta de forma determinista. las transacciones descritas en
esos bloques para determinar el estado del libro mayor (por ejemplo, quién posee qué dinero). Un "contrato inteligente" es
solo un programa determinista que una réplica ejecuta cuando se entrega un mensaje en particular.

Replicación de máquinas de estado

a pedido para realizar la actualización que haces


enviar u a través de FIFO-fin de transmisión de
orden total el

al entregar u a través de la transmisión de orden total FIFO,


¡ actualice el estado usando una lógica determinista arbitraria!
finalizará el

Ideas estrechamente relacionadas:

I Transacciones serializables (ejecutar en orden de entrega)


I Blockchains, registros distribuidos, contratos inteligentes
Limitaciones:

No puedo actualizar el estado inmediatamente, tengo que esperar


entrega a través de transmisión
Necesito transmisión de orden total tolerante a fallas: vea la lección 6
Diapositiva 102

Las desventajas de la replicación de máquinas de estado son las limitaciones de la transmisión de orden total. Como
se discutió en la Sección 4.2, cuando un nodo quiere transmitir un mensaje a través de una transmisión de orden total, no
puede enviarse ese mensaje inmediatamente. Por esta razón, cuando se utiliza la replicación de máquinas de estado, una
réplica que desea actualizar su estado no puede hacerlo de inmediato, sino que debe pasar por el proceso de transmisión,
coordinarse con otros nodos y esperar a que se le devuelva la actualización. La tolerancia a fallas de la replicación de la
máquina de estado depende de la tolerancia a fallas de la transmisión de orden total subyacente, que discutiremos en la
lección 6. Sin embargo, la replicación basada en la transmisión de orden total es ampliamente utilizada.

Réplica líder de la base de datos

La réplica L de la base de datos líder garantiza la transmisión total de pedidos

cliente 1 cliente 2
I F
T1

T1
T2

T2

OK cometer

OK
cometer

El seguidor F aplica el registro de transacciones en el orden de confirmación

Diapositiva 103

53
Machine Translated by Google

Recuerde de la diapositiva 86 que una forma de implementar la transmisión de orden total es designar un nodo como líder y enrutar
todos los mensajes de transmisión a través de él para imponer una orden de entrega. Este principio también se usa ampliamente para la
replicación de bases de datos: muchos sistemas de bases de datos designan una réplica como líder, primaria o maestra. Cualquier
transacción que desee modificar la base de datos debe ejecutarse en la réplica líder. Como se muestra en la diapositiva 103, el líder
puede ejecutar varias transacciones al mismo tiempo; sin embargo, compromete esas transacciones en una orden total. Cuando se
confirma una transacción, la réplica líder transmite los cambios de datos de esa transacción a todas las réplicas de seguidores, y los
seguidores aplican esos cambios en el orden de confirmación. Este enfoque se conoce como replicación pasiva y podemos ver que es
equivalente a la transmisión de orden total de los registros de confirmación de transacciones.

Tanto sobre el uso de la transmisión de orden total para la replicación. ¿Qué pasa con los otros modelos de transmisión de la Clase
4 ? ¿Podemos usarlos también para la replicación? La respuesta es sí, como se muestra en la diapositiva 104; sin embargo, se requiere
más cuidado para garantizar que las réplicas permanezcan consistentes. No basta con asegurarse de que la actualización de estado sea
determinista.
Por ejemplo, podemos usar la transmisión causal, que garantiza el mismo orden de entrega en las réplicas cuando una actualización
se produjo antes que otra, pero que puede entregar actualizaciones simultáneas en cualquier orden. Si queremos asegurarnos de que
las réplicas terminen en el mismo estado, sin importar en qué orden se entreguen las actualizaciones simultáneas, debemos hacer que
esas actualizaciones sean conmutativas: es decir, debemos asegurarnos de que el resultado final sea el mismo, sin importar en qué en
qué orden se aplican esas actualizaciones. Esto se puede hacer, y veremos algunas técnicas para la conmutatividad en la lección 8.

Replicación usando transmisión causal (y más débil)


La replicación de máquinas de estado utiliza (FIFO-) transmisión de orden total.
¿Podemos usar formas de transmisión más débiles también?

Si las actualizaciones de estado de réplica son conmutativas, las réplicas pueden


procesar actualizaciones en diferentes órdenes y aún terminar en el mismo estado.

Las actualizaciones f y g son conmutativas si f(g(x)) = g(f(x))

suposiciones de transmisión sobre la función de actualización de estado

determinista de orden total (SMR)


causal Actualizaciones deterministas y concurrentes conmutan

de confianza determinista, todas las actualizaciones viajan

mejor esfuerzo determinista, conmutativo, idempotente, tolera la pérdida


de mensajes

Diapositiva 104

6 Consenso
En esta lección volvemos al problema de la transmisión de orden total. Vimos en la Sección 5.3 que la transmisión de orden total es muy
útil para permitir la replicación de máquinas de estado. Como se discutió en la diapositiva 86, una forma de implementar la transmisión
de orden total es designar un nodo como líder y enrutar todos los mensajes a través de él. Luego, el líder solo necesita distribuir los
mensajes a través de la transmisión FIFO, y esto es suficiente para garantizar que todos los nodos entreguen la misma secuencia de
mensajes en el mismo orden.
Sin embargo, el gran problema con este enfoque es que el líder es un único punto de falla: si deja de estar disponible, todo el
sistema se detiene. Una forma de superar esto es a través de la intervención manual: se puede notificar a un operador humano si el líder
no está disponible, y esta persona luego reconfigura todos los nodos para usar un nodo diferente como su líder. Este proceso se
denomina conmutación por error y, de hecho, se utiliza en muchos sistemas de bases de datos.

La conmutación por error funciona bien en situaciones en las que la indisponibilidad del líder se planifica con anticipación, por
ejemplo, cuando es necesario reiniciar el líder para instalar actualizaciones de software. Sin embargo, para interrupciones repentinas e
inesperadas del líder (por ejemplo, un bloqueo, una falla de hardware o un problema de red), la conmutación por error se ve afectada por
el hecho de que los humanos están limitados en cuanto a la rapidez con la que pueden realizar este procedimiento. Incluso en el mejor
de los casos, un operador tardará varios minutos en responder, durante los cuales el sistema no podrá procesar ninguna actualización.
Esto plantea la pregunta: ¿podemos transferir automáticamente el liderazgo de un nodo a otro en caso de que el antiguo líder no
esté disponible? La respuesta es sí, y esto es exactamente lo que hacen los algoritmos de consenso.

54
Machine Translated by Google

Comienzo del video sección


Transmisión de orden total tolerante a fallas 6.1 (descarga mp4)

La transmisión de orden total es muy útil para la replicación de


máquinas de estado.

Puede implementar la transmisión de orden total enviando todos los mensajes a


través de un solo líder.

Problema: ¿qué pasa si el líder falla/no está disponible?

I Conmutación por error manual: un operador humano elige un nuevo


líder y reconfigura cada nodo para usar un nuevo líder

Utilizado en muchas bases de datos! Multa por mantenimiento planificado.

¿Apagón no planificado? Los humanos son lentos, puede tomar mucho


tiempo hasta que el sistema se recupere. . .

¿ Podemos elegir automáticamente un nuevo líder?

Diapositiva 105

6.1 Introducción al consenso


El problema del consenso se formula tradicionalmente de la siguiente manera: varios nodos quieren llegar a un acuerdo
sobre un valor. Uno o más nodos pueden proponer un valor, y luego el algoritmo de consenso decidirá sobre uno de esos
valores. El algoritmo garantiza que el valor decidido es uno de los valores propuestos, que todos los nodos deciden sobre
el mismo valor (a excepción de los nodos defectuosos, que pueden no decidir nada), y que la decisión es definitiva (un
nodo no cambiará su cuenta una vez que ha decidido un valor).
Se ha demostrado formalmente que el consenso y la transmisión de orden total son equivalentes entre sí, es decir, un
algoritmo para uno puede convertirse en un algoritmo para el otro, y viceversa [Chandra y
Toeg, 1996]:

• Para convertir la emisión de orden total en consenso, un nodo que quiere proponer un valor lo emite, y el primer
mensaje entregado por la emisión de orden total se toma como valor decidido.

• Para convertir el consenso en una transmisión de orden total, usamos una instancia separada del protocolo de
consenso para decidir sobre el primero, segundo, tercero, . . . mensaje a entregar. Un nodo que quiere emitir un
mensaje lo propone para una de estas rondas de consenso. El algoritmo de consenso luego asegura que todos los
nodos estén de acuerdo en la secuencia de mensajes que se entregarán.

Difusión de consenso y orden total

I Formulación tradicional de consenso: varios nodos quieren


ponerse de acuerdo sobre un solo valor
I En contexto de emisión de orden total: este valor es el siguiente
mensaje para entregar

I Una vez que un nodo decide un cierto orden de mensajes, todos


los nodos decidirán el mismo orden

I Consenso y transmisión de orden total son formalmente equivalentes

Algoritmos de consenso comunes:

I Paxos: consenso de valor único


Multi-Paxos: generalización a emisión de orden total

I Raft, replicación con sello de vista, Zab:


FIFO-pedido total transmitido por defecto

Diapositiva 106

Los dos algoritmos de consenso más conocidos son Paxos [Lamport, 1998] y Raft [Ongaro and Ouster hout, 2014].
En su formulación original, Paxos proporciona solo consenso sobre un único valor, y el algoritmo Multi Paxos es una
generalización de Paxos que proporciona transmisión de orden FIFO total. Por otro lado, Raft está diseñado para
proporcionar una transmisión de orden FIFO total "lista para usar".

55
Machine Translated by Google

Modelos de sistemas de consenso

Paxos, Raft, etc. asumen un modelo de sistema de recuperación


de fallas parcialmente síncrono .

¿Por qué no asíncrono?

I Resultado de FLP (Fischer, Lynch, Paterson): No


existe un algoritmo de consenso determinista que garantice
terminar en un modelo de sistema asincrónico de parada por choque.

I Paxos, Raft, etc. usan relojes que solo se usan para tiempos de espera/
detector de fallas para garantizar el progreso. La seguridad (corrección)
no depende del tiempo.

También existen algoritmos de consenso para un sistema parcialmente síncrono.


Modelo de sistema bizantino (usado en blockchains)

Diapositiva 107

El diseño de un algoritmo de consenso depende de manera crucial del modelo del sistema, como se analiza en la Sección 2.3.
Paxos y Raft asumen un modelo de sistema con enlaces de pérdida justa (Diapositiva 33), comportamiento de recuperación de fallas de los
nodos (Diapositiva 34) y sincronía parcial (Diapositiva 35).
Los supuestos sobre el comportamiento de la red y los nodos pueden debilitarse a bizantinos, y dichos algoritmos se utilizan en
cadenas de bloques. Sin embargo, los algoritmos de consenso tolerantes a fallas bizantinos son significativamente más complicados y
menos eficientes que los no bizantinos. Por ahora, nos centraremos en los algoritmos de recuperación de fallas y pérdida justa, que son
útiles en muchos entornos prácticos (como centros de datos con redes privadas confiables). La unidad L47 en la Parte III entra en detalles
del consenso bizantino.
Por otro lado, la suposición de sincronía parcial no puede debilitarse a asincronía. La razón es que el consenso requiere un detector
de fallas (Diapositiva 40), que a su vez requiere un reloj local para activar tiempos de espera [Chandra y Toueg, 1996]. Si no tuviéramos
ningún reloj, entonces un algoritmo de consenso determinista nunca podría terminar. De hecho, se ha demostrado que ningún algoritmo
asíncrono determinista puede resolver el problema del consenso con terminación garantizada. Este hecho se conoce como el resultado
FLP, uno de los teoremas más importantes de la computación distribuida, llamado así por sus tres autores Fischer, Lynch y Paterson
[Fischer et al., 1985].

Es posible eludir el resultado de FLP utilizando un algoritmo no determinista (aleatorio).


Sin embargo, la mayoría de los sistemas prácticos evitan la no terminación mediante el uso de relojes para los tiempos de espera.
Recuerde, sin embargo, que en un sistema parcialmente síncrono, no podemos asumir una latencia de red limitada o una velocidad de
ejecución limitada de los nodos. Por esta razón, los algoritmos de consenso deben garantizar sus propiedades de seguridad (es decir, que
cada nodo decida sobre los mismos mensajes en el mismo orden) independientemente del tiempo en el sistema, incluso si los mensajes se
retrasan arbitrariamente. Solo la vivacidad (es decir, que un mensaje finalmente se entregue) depende de los relojes y el tiempo.

Elección de líder
Multi-Paxos, Raft, etc. utilizan un líder para secuenciar los mensajes.
Usar un detector de fallas ( tiempo de espera) para determinar sospechas
accidente o indisponibilidad del líder.
I En caso de choque del líder sospechoso, elija uno nuevo.
¡ Prevengo a dos líderes al mismo tiempo ("split-brain")!

Asegurar ÿ 1 líder por término:


I El término se incrementa cada vez que se inicia una elección de líder
I Un nodo solo puede votar una vez por término
Requiero un quórum de nodos para elegir un líder en un término

A B C D Y

elige un líder no puede elegir un líder diferente


porque C ya votó
Diapositiva 108

En el centro de la mayoría de los algoritmos de consenso se encuentra un proceso para elegir un nuevo líder cuando el líder existente

56
Machine Translated by Google

deja de estar disponible por cualquier motivo. Los detalles difieren entre los algoritmos; en esta lección nos
concentraremos en el enfoque adoptado por Raft, pero muchas de las lecciones de Raft son igualmente relevantes
para otros algoritmos de consenso. Howard y Mortier [2020] brindan una comparación detallada de Raft y Multi-
Paxos (sin embargo, Paxos/Multi-Paxos no son examinables).
La elección de un líder se inicia cuando los otros nodos sospechan que el líder actual ha fallado, generalmente
porque no han recibido ningún mensaje del líder durante algún tiempo. Uno de los otros nodos se convierte en
candidato y les pide a los otros nodos que voten si aceptan al candidato como su nuevo líder. Si un quórum
(Sección 5.2) de nodos vota a favor del candidato, se convierte en el nuevo líder. Si se utiliza un quórum mayoritario,
esta votación puede tener éxito siempre que la mayoría de los nodos (2 de 3 o 3 de 5, etc.) estén funcionando y
puedan comunicarse.
Si hubiera varios líderes, podrían tomar decisiones inconsistentes que llevarían a violaciones de las propiedades
de seguridad de la transmisión de orden total (una situación conocida como cerebro dividido). Por lo tanto, la clave
que queremos de la elección de un líder es que solo debe haber un líder a la vez. En Raft, el concepto de "en
cualquier momento" se captura al tener un número de término, que es solo un número entero que se incrementa
cada vez que se inicia una elección de líder. Si se elige un líder, el algoritmo de votación garantiza que sea el único
líder dentro de ese período en particular. Diferentes términos pueden tener diferentes líderes.

¿Podemos garantizar que solo hay un líder?

Puede garantizar líder único por término.

No se puede evitar tener múltiples líderes de diferentes términos.

Ejemplo: el nodo 1 es líder en el término t, pero debido a una partición


de red ya no puede comunicarse con los nodos 2 y 3:

nodo 1 nodo 2 nodo 3

Los nodos 2 y 3 pueden elegir un nuevo líder en el término t + 1.

¡Es posible que el Nodo 1 ni siquiera sepa que se ha elegido un nuevo líder!

Diapositiva 109

Sin embargo, recuerde de la diapositiva 41 que en un sistema parcialmente síncrono, un detector de fallas
basado en el tiempo de espera puede ser inexacto: puede sospechar que un nodo se ha bloqueado cuando en
realidad funciona bien, por ejemplo, debido a un pico en la latencia de la red. Por ejemplo, en la diapositiva 109, el
nodo 1 es el líder en el término t, pero la red entre este y los nodos 2 y 3 se interrumpe temporalmente. Los nodos
2 y 3 pueden detectar que el nodo 1 ha fallado y elegir un nuevo líder en el término t + 1, aunque el nodo 1 todavía
funciona correctamente. Además, es posible que el nodo 1 ni siquiera haya notado el problema de la red, y
tampoco sabe aún sobre el nuevo líder. Por lo tanto, terminamos con dos nodos que creen ser el líder.

Comprobar si un líder ha sido expulsado


Para cada decisión (mensaje a entregar), el líder primero debe obtener
reconocimientos de un quórum.

líder seguidor 1 seguidor 2

¿Seré tu líder en
el término t?

sí sí

Podemos
entregar mensaje m siguiente
en el término t?

bueno bueno

Bien, ahora
entrega m por favor

Diapositiva 110

57
Machine Translated by Google

Por esta razón, incluso después de que un nodo ha sido elegido líder, debe actuar con cuidado, ya que en cualquier
momento el sistema puede contener a otro líder con un mandato posterior del que aún no ha tenido conocimiento. No es
seguro que un líder actúe unilateralmente. En cambio, cada vez que un líder quiere decidir sobre el próximo mensaje a
entregar, debe solicitar nuevamente la confirmación de un quórum de nodos. Esto se ilustra en la diapositiva 110:
1. En el primer ida y vuelta, el nodo izquierdo es elegido líder gracias a los votos de los otros dos nodos.

2. En el segundo ida y vuelta, el líder propone el siguiente mensaje a entregar, y los seguidores toman conocimiento
de que no conocen ningún líder con término posterior a t.

3. Finalmente, el líder realmente entrega m y transmite este hecho a los seguidores, para que puedan hacer
lo mismo.

Si se ha elegido otro líder, el antiguo líder se enterará por al menos uno de los reconocimientos en la segunda vuelta,
porque al menos uno de los nodos del quórum de la segunda vuelta también debe haber votado por el nuevo líder. Por lo
tanto, a pesar de que pueden existir varios líderes al mismo tiempo, los líderes anteriores ya no podrán decidir qué
mensajes enviar, lo que hace que el algoritmo sea seguro.

6.2 El algoritmo de consenso Raft


Para concretar estas ideas, ahora analizaremos el algoritmo completo de Raft para la transmisión total de órdenes FIFO
tolerante a fallas. Es, con mucho, el algoritmo más complejo que veremos en este curso, con un pseudocódigo que abarca
nueve diapositivas. (Paxos y otros algoritmos de consenso son igualmente complejos, si no peores). No es necesario
memorizar todo el algoritmo para el examen, pero vale la pena estudiarlo detenidamente para comprender los principios
subyacentes. Para una visualización gráfica del algoritmo, consulte http://thesecretlivesofdata.com/raft/.

Para comprender el algoritmo, vale la pena tener en cuenta la máquina de estados de la diapositiva 111. Un nodo
puede estar en uno de tres estados: líder, candidato o seguidor. Cuando un nodo comienza a ejecutarse por primera vez,
o cuando falla y se recupera, se inicia en el estado de seguidor y espera mensajes de otros nodos.
Si no recibe mensajes de un líder o candidato durante un período de tiempo, el seguidor sospecha que el líder no está
disponible y puede intentar convertirse en líder. El tiempo de espera para detectar la falla del líder es aleatorio, para reducir
la probabilidad de que varios nodos se conviertan en candidatos al mismo tiempo y compitan para convertirse en líder.

Cuando un nodo sospecha que el líder ha fallado, pasa al estado candidato, incrementa el número del término e inicia
una elección de líder en ese término. Durante esta elección, si el nodo recibe noticias de otro candidato o líder con un
mandato más alto, vuelve al estado de seguidor. Pero si la elección tiene éxito y recibe votos de un quórum de nodos, el
candidato pasa al estado líder. Si no se reciben suficientes votos dentro de un período de tiempo, la elección finaliza y el
candidato reinicia la elección con un término más alto.

Una vez que un nodo está en el estado líder, sigue siendo líder hasta que se cierra o falla, o hasta que recibe un
mensaje de un líder o candidato con un término superior al suyo. Un término más alto podría ocurrir si una partición de la
red hiciera que el líder y otro nodo no pudieran comunicarse durante el tiempo suficiente para que el otro nodo comenzara
una elección para un nuevo líder. Al enterarse de un término más alto, el ex líder se retira para convertirse en seguidor.

Comienzo del video sección


Transiciones de estado de nodo en Raft 6.2 (descarga mp4)

pone en marcha
elección
o recupera
se acabó el tiempo
del accidente

sospecha
del fracaso del líder

recibe votos del

quórum
Seguidor Candidato Líder
descubre
nuevo término

descubre un nuevo término

Diapositiva 111

58
Machine Translated by Google

La diapositiva 112 muestra el pseudocódigo para iniciar y para iniciar una elección. Las variables definidas en el bloque
de inicialización constituyen el estado de un nodo. Cuatro de las variables (currentTerm, voteFor log y commitLength) deben,
mantenerse en un almacenamiento estable (por ejemplo, en el disco), ya que sus valores no deben perderse en caso de
falla. Las otras variables pueden estar en la memoria volátil, y la función de recuperación de fallas restablece sus valores.
Cada nodo tiene una ID única y suponemos que hay una constante global, nodos, que contiene el conjunto de ID de todos
los nodos del sistema. Esta versión del algoritmo no se ocupa de la reconfiguración (agregar o eliminar nodos en el sistema).

La variable log contiene una serie de entradas, cada una de las cuales tiene las propiedades msg y term. La propiedad
msg de cada entrada de matriz contiene un mensaje que queremos entregar a través de la transmisión de orden total, y la
propiedad term contiene el número de término en el que se transmitió. El registro utiliza una indexación basada en cero, por
lo que log[0] es la primera entrada de registro y log[log.lengthÿ1] es la última. El registro crece agregando nuevas entradas
al final y Raft replica este registro en todos los nodos. Cuando una entrada de registro (y todos sus predecesores) se han
replicado en un quórum de nodos, se confirma. En el momento en que confirmamos una entrada de registro, también
enviamos su mensaje a la aplicación. Antes de que se confirme una entrada de registro, aún puede cambiar, pero Raft
garantiza que una vez que se confirma una entrada de registro, es definitiva y todos los nodos confirmarán la misma
secuencia de entradas de registro. Por lo tanto, la entrega de mensajes de entradas de registro confirmadas en su orden de
registro nos brinda una transmisión de orden FIFO total.
Cuando un nodo sospecha que un líder falla, inicia una elección de líder de la siguiente manera: incrementa currentTerm,
establece su propio rol como candidato y vota por sí mismo configurando voteFor y votesReceived en su propia ID de nodo.
Luego envía un mensaje VoteRequest a cada uno de los nodos, pidiéndoles que voten si este candidato debe ser el nuevo
líder. El mensaje contiene el nodeId del candidato, su término actual (después de incrementarse), el número de entradas en
su registro y la propiedad de término de su última entrada de registro.

m1 m2 m3
Balsa (1/9): inicialización registro =
mensaje

término
1 1 1
en la inicialización
do currentTerm := 0; votado por := registro[0] registro[1] registro[2]
registro nulo := hola; commitLength := 0
rolActual := seguidor; líder actual: = votos nulos
recibidos: = {}; enviadoLength := hola; ackedLength := hola fin en

al recuperarse de un bloqueo ,
haga currentRole := seguidor; líder actual: = votos
nulos recibidos: = {}; enviadoLength := hola; ackedLength := hola
fin en

en el nodo nodeId sospecha que el líder ha fallado, o en el tiempo de espera


de la elección, haga currentTerm := currentTerm + 1; rol actual :=
candidato votado por := nodeId; votos recibidos := {nodeId}; lastTerm :=
0 if log.length > 0 then lastTerm := log[log.length ÿ 1].term; end if msg :=
(VoteRequest, nodeId, currentTerm, log.length, lastTerm) para cada nodo
ÿ nodos: enviar mensaje al nodo iniciar el temporizador de elección finalizar
el

Diapositiva 112

c para candidato
Balsa (2/9): votando por un nuevo líder

al recibir (VoteRequest, cId, cTerm, cLogLength, cLogTerm)


en el nodo nodeId
do myLogTerm := log[log.length ÿ 1].term
logOk := (cLogTerm > myLogTerm) ÿ
(cLogTerm = myLogTerm ÿ cLogLength ÿ log.length)

términoOk := (cTérmino > TérminoActual) ÿ


(cTérmino = TérminoActual ÿ votadoPor ÿ {cId, nulo})

si logOk ÿ termOk entonces


currentTerm := cTerm
currentRole := seguidor
votado por := cId enviar
(VoteResponse, nodeId, currentTerm,true) al nodo cId else enviar
(VoteResponse, nodeId, currentTerm, false) al nodo cId end if end on

Diapositiva 113

59
Machine Translated by Google

La diapositiva 113 muestra lo que sucede cuando un nodo recibe un mensaje de solicitud de voto de un candidato. Un
nodo solo votará por un candidato si el registro del candidato está al menos tan actualizado como el registro del votante; esto
evita que un candidato con un registro desactualizado se convierta en líder, lo que podría provocar la pérdida de entradas de
registro confirmadas. El registro del candidato es aceptable si el término de su última entrada de registro es mayor que el
término de la última entrada de registro en el nodo que recibió el mensaje VoteRequest. Además, el registro también es
aceptable si los términos son los mismos y el registro del candidato contiene al menos tantas entradas como el registro del destinatario.
Esta lógica se refleja en la variable logOk.
Además, un nodo no votará por un candidato si ya ha votado por otro candidato en el mismo período o en un período
posterior. La variable termOk es verdadera si está bien votar por el candidato de acuerdo con esta regla. La variable voteFor
realiza un seguimiento de cualquier voto anterior del nodo actual en currentTerm.
Si logOk y termOk, entonces el nodo actualiza su término actual al término del candidato, lo registra y envía un mensaje
, de VoteResponse
el nodo envía un mensaje VoteResponse que
que
contiene
contiene
verdadero
falso (que
(que
indica
indica
unaéxito)
negativa
al candidato.
a votar por
vote
el candidato).
in voteFor De
Además
lo contrario,
del
indicador de éxito o fracaso, el mensaje de respuesta contiene el ID de nodo del nodo que envía el voto y el término del voto.

Balsa (3/9): recogiendo votos

al recibir (VoteResponse, voterId, term, grant) en nodeId do


si rol actual = candidato ÿ término = término actual ÿ concedido entonces
votosRecibidos := votosRecibidos ÿ {voterId} si |
votosRecibidos| ÿ d(|nodos| + 1)/2e entonces
Rolactual := líder; currentLeader := nodeId cancelar
el temporizador de elección para cada seguidor ÿ
nodos \ {nodeId} do sentLength[follower] := log.length
ackedLength[follower] := 0 ReplicateLog(nodeId,
follower ) end for end if

de lo contrario, si término > término actual,


entonces término actual := término rol
actual := seguidor votado por := nulo
cancelar el temporizador de elección

terminar

si termina en
Diapositiva 114

Volviendo al candidato, la diapositiva 114 muestra el código para procesar los mensajes VoteResponse. Ignoramos
cualquier respuesta relacionada con términos anteriores (que podrían llegar tarde debido a demoras en la red). Si el término
en la respuesta es más alto que el término del candidato, el candidato cancela la elección y vuelve al estado de seguidor.
Pero si el término es correcto y el indicador de éxito otorgado se establece en verdadero, el candidato agrega la ID de nodo
del votante al conjunto de votos recibidos.
Si el conjunto de votos constituye un quórum, el candidato pasa al estado líder. Como su primera acción como líder,
actualiza las variables sentLength y ackedLength (explicadas a continuación) y luego llama a la función ReplicateLog (definida
en la diapositiva 116) para cada seguidor. Esto tiene el efecto de enviar un mensaje a cada seguidor, informándoles sobre el
nuevo líder.
En el líder, sentLength y ackedLength son variables que asignan cada ID de nodo a un número entero (los nodos que
no son líderes no usan estas variables). Para cada seguidor F, sentLength[F] rastrea cuántas entradas de registro, contando
desde el comienzo del registro, se han enviado a F, y ackedLength[F] rastrea cuántas entradas de registro han sido
reconocidas como recibidas por F. Al convertirse en un líder, un nodo inicializa sentLength[F] a log.length (es decir, asume
que el seguidor ya ha recibido el registro completo), e inicializa ackedLength[F] a 0 (es decir, todavía no se ha reconocido
nada). Estas suposiciones pueden ser incorrectas: por ejemplo, es posible que al seguidor le falten algunas de las entradas
de registro que están presentes en el líder.
En este caso, sentLength[F] se corregirá a través de un proceso que analizamos en la diapositiva 119.
La diapositiva 115 muestra cómo se agrega una nueva entrada al registro cuando la aplicación desea transmitir un
mensaje a través de la transmisión de orden total. Un líder puede simplemente seguir adelante y agregar una nueva entrada
a su registro, mientras que cualquier otro nodo debe pedirle al líder que haga esto en su nombre, a través de un enlace FIFO
(para garantizar la transmisión total del pedido FIFO). Luego, el líder actualiza su propia entrada en ackedLength a log.length,
lo que indica que ha reconocido su propia adición al registro y llama a ReplicateLog para cada nodo.
Además, un líder también llama periódicamente a ReplicateLog para cada nodo, incluso si no hay un mensaje nuevo
para transmitir. Esto sirve para múltiples propósitos: permite que los seguidores sepan que el líder todavía está vivo; sirve
como retransmisión de cualquier mensaje del líder al seguidor que se haya perdido; y

60
Machine Translated by Google

actualiza al seguidor sobre qué mensajes se pueden enviar, como se explica a continuación.

Balsa (4/9): transmisión de mensajes


a pedido para transmitir msg en el nodo nodeId do if
currentRole = leader then
agregue el registro (msg : msg, term : currentTerm) para registrar
ackedLength[nodeId] := log.length para cada seguidor ÿ nodos \
{nodeId} do ReplicateLog(nodeId, follower ) end for else

reenviar la solicitud a currentLeader a través de un enlace


FIFO si finaliza el

periódicamente en el nodo nodeId


do si currentRole = líder
entonces para cada seguidor ÿ nodes \ {nodeId} do
ReplicateLog(nodeId, seguidor) final
por final si final hacer

Diapositiva 115

Balsa (5/9): replicando de líder a seguidores

Llamado al líder cada vez que hay un nuevo mensaje en el registro, y también
periódicamente. Si no hay mensajes nuevos, las entradas son la lista vacía.
Los mensajes de LogRequest con las entradas = hola sirven como latidos del corazón, lo que les
permite a los seguidores saber que el líder todavía está vivo.

función ReplicateLog(leaderId, followerId) i :=


sentLength[followerId] entradas := hlog[i], log[i +
1], . . . , log[log.length ÿ 1]i prevLogTerm
0 then
:= 0prevLogTerm
if i > :=
log[i ÿ 1].term end if se envía (LogRequest, leaderId,
currentTerm, i, prevLogTerm, commitLength, entradas) a
followerId función final

Diapositiva 116

La función ReplicateLog se muestra en la diapositiva 116. Su propósito es enviar cualquier nueva entrada de registro
del líder al nodo seguidor con ID followerId. Primero establece las entradas variables en el sufijo del registro que
comienza con el índice sentLength[followerId], si existe. Es decir, si sentLength[followerId] es el número de entradas de
registro ya enviadas a followerId, entonces las entradas contienen las entradas restantes que aún no se han enviado. Si
sentLength[followerId] = log.length, las entradas de la variable se establecen en la matriz vacía.
Luego, ReplicateLog envía un mensaje LogRequest a followerId que contiene entradas y varios otros valores: la ID
del líder; su término actual; la longitud del prefijo de registro que precede a las entradas; el término de la última entrada
de registro que precede a las entradas; y commitLength, que es el número de entradas de registro que se han
confirmado, contadas desde el inicio del registro. Más información sobre la confirmación de entradas de registro en breve.
Cuando un seguidor recibe uno de estos mensajes LogRequest del líder, procesa el mensaje como se muestra en
la diapositiva 117. Primero, si el mensaje es para un término posterior al que el seguidor ha visto anteriormente,
actualiza su término actual y acepta el remitente de el mensaje como líder.
Luego, el seguidor verifica si su registro es consistente con el del líder. logLength es el número de entradas de
registro que preceden a las nuevas entradas contenidas en el mensaje LogRequest. El seguidor requiere que su registro
sea al menos tan largo como logLength (es decir, que no falte ninguna entrada), y que el término de la última entrada
de registro en el prefijo logLength del registro del seguidor sea el mismo que el término del mismo registro entrada en el
líder. Si ambas comprobaciones pasan, la variable logOk se establece en verdadero.
Si el mensaje LogRequest es para el término esperado y si logOk, entonces el seguidor acepta el mensaje y llama
a la función AppendEntries (definida en la diapositiva 118) para agregar entradas a su propio registro. Luego responde
al líder con un mensaje LogResponse que contiene la ID del seguidor, el término actual, un reconocimiento del número
de entradas de registro recibidas y el valor verdadero que indica que LogRequest

61
Machine Translated by Google

fue exitoso. Si el mensaje es de un término desactualizado o logOk es falso, el seguidor responde con un LogResponse
que contiene falso para indicar un error.

Raft (6/9): seguidores recibiendo mensajes


al recibir (LogRequest, leaderId, term, logLength, logTerm, leaderCommit,
entradas) en el nodo nodeId do if term > currentTerm
then
términoactual := término; votado por := null rol
actual := seguidor; líder actual := ID de líder end if if término
= término actual ÿ rol actual = candidato then rol actual :=
seguidor; líderActual := IDLíder end if logOk := (log.length ÿ
logLength) ÿ (logLength = 0 ÿ logTerm = log[logLength ÿ
1].term) if term = currentTerm ÿ logOk entonces

AppendEntries(logLength, leaderCommit, entrys) ack :=


logLength + entrys.length enviar (LogResponse, nodeId,
currentTerm, ack,true) to leaderId else send (LogResponse, nodeId,
currentTerm, 0, false) to leaderId end if end on

Diapositiva 117

Balsa (7/9): actualizando los registros de los seguidores

función AppendEntries(logLength, leaderCommit, entradas) si


entradas.longitud > 0 ÿ log.longitud > logLength entonces si
log[logLength].término 6= entradas[0].término entonces
registro := hlog[0], registro[1], . . . , log[logLength ÿ
1]i end if end if if logLength + entradas.longitud >
log.longitud entonces

for i := log.length ÿ logLength to entrys.length ÿ 1 agregue las


entradas[i] al registro de un extremo a otro si if leaderCommit
> commitLength then for i := commitLength to leaderCommit ÿ
1 entregue log[i].msg to el final de la aplicación para commitLength :=
leaderCommit end if end function

Diapositiva 118

La diapositiva 118 muestra la función AppendEntries, a la que llama un seguidor para ampliar su registro con las
entradas recibidas del líder. logLength es el número de entradas de registro (contadas desde el principio del registro) que
preceden a las nuevas entradas. Si el registro del seguidor ya contiene entradas en log[logLength] y más allá, compara el
término de esa entrada existente con el término de la primera entrada nueva recibida del líder.
Si son inconsistentes, el seguidor descarta esas entradas existentes truncando el registro. Esto sucede si las entradas de
registro existentes provienen de un líder anterior, que ahora ha sido reemplazado por un nuevo líder.
A continuación, cualquier entrada nueva que aún no esté presente en el registro del seguidor se agrega al registro. En
el caso de la duplicación de mensajes, esta operación es idempotente, ya que se entiende que las nuevas entradas
comienzan en el índice logLength en el registro.
Finalmente, el seguidor comprueba si el número entero leaderCommit en el mensaje LogRequest es mayor que su
variable local commitLength. Si es así, esto significa que los nuevos registros están listos para confirmarse y enviarse a la
aplicación. El líder mueve su commitLength hacia adelante y realiza la entrega de difusión de orden total de los mensajes
en las entradas de registro correspondientes.
Esto completa el algoritmo desde el punto de vista de los seguidores. Lo que queda es volver a
el líder y para mostrar cómo procesa los mensajes LogResponse de los seguidores (consulte la diapositiva 119).

62
Machine Translated by Google

Balsa (8/9): líder recibiendo reconocimientos de registro

al recibir (LogResponse, seguidor, término, acuse de recibo, éxito) en nodeId


do if término = término actual ÿ rol actual = líder entonces
si el éxito = verdadero ÿ ack ÿ ackedLength[seguidor] entonces
sentLength [seguidor]: = ack
ackedLength [seguidor]: = ack
CommitLogEntries () de lo
contrario, si la longitud enviada [seguidor]> 0 ,
entonces la longitud enviada [seguidor]: = la longitud enviada [seguidor] - 1
ReplicateLog(nodeId, seguidor) finaliza
si
de lo contrario, si término > término
actual, entonces término actual :=
término rol actual := seguidor votado
por := fin nulo si finaliza el

Diapositiva 119

Un líder que recibe un mensaje LogResponse primero verifica el término en el mensaje: si el término del remitente es
posterior al término del destinatario, eso significa que se ha iniciado una nueva elección de líder y, por lo tanto, este nodo pasa
de líder a seguidor. Los mensajes con un término obsoleto se ignoran. Para los mensajes con el término correcto, verificamos el
campo booleano de éxito para ver si el seguidor aceptó las entradas del registro.
Si el éxito = verdadero, el líder actualiza sentLength y ackedLength para reflejar la cantidad de entradas de registro
reconocidas por el seguidor y luego llama a la función CommitLogEntries (Diapositiva 120). Si éxito = falso, sabemos que el
seguidor no aceptó las entradas del registro porque su variable logOk era falsa. En este caso, el líder disminuye el valor de
sentLength para este seguidor y llama a ReplicateLog para volver a intentar enviar el mensaje LogRequest comenzando con
una entrada de registro anterior. Esto puede suceder varias veces, pero eventualmente el líder enviará al seguidor una serie de
entradas que amplían limpiamente el registro existente del seguidor, momento en el cual el seguidor aceptará LogRequest. (Este
algoritmo podría optimizarse para requerir menos reintentos, pero en este curso evitaremos que sea más complejo de lo
necesario).

Balsa (9/9): líder que comete entradas de registro


Cualquier entrada de registro que haya sido reconocida por un quórum de nodos
está lista para ser confirmada por el líder. Cuando se confirma una entrada de
registro, su mensaje se envía a la aplicación.

define acks(longitud) = |{n ÿ nodos | ackedLength[n] ÿ longitud}|

función CommitLogEntries
minAcks := d(|nodos| + 1)/2e
ready := {len ÿ {1, . . . , registro.longitud} | acks(len) ÿ minAcks} si
está listo 6= {} ÿ max(ready) > commitLength ÿ
log[max(ready) ÿ 1].term = currentTerm entonces
for i := commitLength to max(ready) ÿ 1 entregue
log[i].msg al final de la aplicación for
commitLength := max(ready) end if end function

Diapositiva 120

Finalmente, la diapositiva 120 muestra cómo el líder determina qué entradas de registro se deben confirmar. Definimos la
función acks(longitud) para tomar un número entero, una cantidad de entradas de registro contadas desde el inicio del registro.
Esta función devuelve el número de nodos que han confirmado la recepción de entradas de registro de longitud o más.
CommitLogEntries usa esta función para determinar cuántas entradas de registro han sido reconocidas por un quórum
mayoritario de nodos o más. La variable ready contiene el conjunto de longitudes de prefijo de registro que están listas para
confirmar, y si ready no está vacío, max(ready) es la longitud máxima de prefijo de registro que podemos confirmar. Si esto
excede el valor actual de commitLength, significa que hay nuevas entradas de registro que ahora están listas para confirmar
porque han sido reconocidas por suficientes nodos. El mensaje en cada una de estas entradas de registro ahora se entrega a la
aplicación en el líder y se actualiza la variable commitLength. En el siguiente mensaje LogRequest que el líder envíe a los
seguidores, se incluirá el nuevo valor de commitLength, lo que hará que los seguidores confirmen y entreguen las mismas
entradas de registro.

63
Machine Translated by Google

Ejercicio 16. Tres nodos están ejecutando el algoritmo Raft. En un momento dado, cada nodo tiene el registro que se muestra a
continuación:

m1 m2 m1 m4 m5 m6 m1 m4 m7 mensaje
registro en el nodo A: registro en el nodo B: registro en el nodo C:
1 1 1 2 2 2 1 2 3 término

(a) Explique qué eventos pueden haber ocurrido que causaron que los nodos estuvieran en este estado.
(b) ¿Cuáles son los valores posibles de la variable commitLength en cada nodo? (c) El nodo A inicia una
elección de líder en el término 4, mientras que los nodos están en el estado anterior. ¿Es posible que obtenga quórum de votos?
¿Qué pasa si la elección fue iniciada por uno de los otros nodos? (d) Suponga que el nodo B es elegido líder en el término 4,
mientras que los nodos están en el estado anterior. Dé la secuencia de mensajes intercambiados entre B y C después de esta
elección.

7 Consistencia de la réplica
Hemos visto cómo realizar la replicación usando quórums de lectura/escritura y la replicación de máquinas de estado usando
transmisión de orden total. En este contexto hemos dicho que queremos que las réplicas tengan “copias consistentes de los mismos
datos”, sin definir exactamente a qué nos referimos con consistente.
Desafortunadamente, la palabra "coherencia" significa diferentes cosas en diferentes contextos. En el contexto de las
transacciones, la C en ACID significa consistencia que es una propiedad de un estado: es decir, podemos decir que una base de
datos está en un estado consistente o inconsistente, lo que significa que el estado satisface o viola ciertas invariantes definidas por
el solicitud. Por otro lado, en el contexto de la replicación, hemos usado informalmente “consistencia” para referirnos a una relación
entre réplicas: queremos que una réplica sea consistente con otra réplica.

Dado que no existe una definición verdadera de consistencia, hablamos en cambio de una variedad de modelos de consistencia.
Hemos visto un ejemplo particular de un modelo de consistencia, a saber, consistencia de lectura después de escritura (Diapositiva
97), que restringe los valores que una operación de lectura puede devolver cuando el mismo nodo escribe previamente en el mismo
elemento de datos. Veremos más modelos en esta conferencia.

Comienzo del video sección


"Consistencia" 7.1 (descarga mp4)
¡Una palabra que significa muchas cosas diferentes en diferentes contextos!
I ACID: una transacción transforma la base de datos de una
estado “coherente” con otro

Aquí, "consistente" = satisfacer invariantes específicos de la


aplicación

p.ej. “todo curso con alumnos matriculados debe tener al menos un


profesor”

I Consistencia de lectura después de escritura (lección 5)

I Replicación: la réplica debe ser “coherente” con otras


réplicas

"consistentes" = en el mismo estado? (¿cuando exactamente?)

"consistente" = ¿las operaciones de lectura devuelven el mismo resultado?

I Modelo de consistencia: muchos para elegir


Diapositiva 121

7.1 Confirmación en dos fases


Comencemos con un problema de coherencia que surge cuando se ejecuta una transacción distribuida, es decir, una transacción
que lee o escribe datos en múltiples nodos. Los datos en esos nodos pueden ser réplicas del mismo conjunto de datos o diferentes
partes de un conjunto de datos más grande; se aplica una transacción distribuida en ambos casos.
Recuerde de la mitad de los sistemas concurrentes de este curso que una propiedad clave de una transacción es la atomicidad.
Cuando una transacción abarca múltiples nodos, aún queremos atomicidad para la transacción como un todo: es decir, todos los
nodos deben confirmar la transacción y hacer que sus actualizaciones sean duraderas, o todos los nodos deben cancelar la
transacción y descartar o revertir sus actualizaciones.
Por lo tanto, necesitamos un acuerdo entre los nodos sobre si la transacción debe abortar o comprometerse. Quizás se
pregunte: ¿es este acuerdo lo mismo que el consenso, que discutimos en la lección 6? La respuesta es no: aunque ambos tratan
superficialmente de llegar a un acuerdo, los detalles difieren significativamente.

64
Machine Translated by Google

Transacciones distribuidas

Recuerde la atomicidad en el contexto de las transacciones ACID:


I Una transacción se confirma o aborta

Si se compromete, sus actualizaciones son duraderas.

Si aborta, no tiene efectos secundarios visibles

I La consistencia ACID (preservación de invariantes) se basa en


la atomicidad

Si la transacción actualiza datos en múltiples nodos, esto implica:

I Todos los nodos deben comprometerse o todos deben abortar

I Si algún nodo falla, todos deben abortar

Asegurar esto es el problema del compromiso atómico .


¿Se parece un poco al consenso?

Diapositiva 122

Compromiso atómico versus consenso

Consenso compromiso atómico

Uno o más nodos proponen un Cada nodo vota si se compromete


valor o aborta

Se decide cualquiera de los Debe comprometerse si todos los


valores propuestos nodos votan para comprometerse; debe
abortar si ÿ 1 nodos votan para abortar

Se pueden tolerar los nodos Debe abortar si un nodo participante


bloqueados, siempre que falla
funcione el quórum.

Diapositiva 123

El algoritmo más común para asegurar el compromiso atómico a través de múltiples nodos es el protocolo de compromiso de dos fases (2PC) [Gray, 1978]. (No debe confundirse

con el bloqueo de dos fases (2PL), discutido en la primera mitad de este curso: 2PL garantiza el aislamiento serializable, mientras que 2PC garantiza el compromiso atómico. También

hay un protocolo de compromiso de tres fases, pero asume el protocolo sincrónico poco realista modelo de sistema, por lo que no lo discutiremos aquí.) El flujo de comunicación de

2PC se ilustra en la diapositiva 124.

Compromiso de dos fases (2PC)

cliente coordinador
A B
iniciar T1

T1
. . . ejecución habitual de transacciones. . . T1
cometer T1
preparar

OK OK

decisión de
cometer o abortar cometer

Diapositiva 124

sesenta y cinco
Machine Translated by Google

Cuando se utiliza la confirmación en dos fases, un cliente primero inicia una transacción regular de un solo nodo en cada
réplica que participa en la transacción y realiza las lecturas y escrituras habituales dentro de esas transacciones. Cuando el
cliente está listo para confirmar la transacción, envía una solicitud de confirmación al coordinador de transacciones, un nodo
designado que administra el protocolo 2PC. (En algunos sistemas, el coordinador es parte del cliente).

El coordinador primero envía un mensaje de preparación a cada réplica que participa en la transacción y cada réplica
responde con un mensaje que indica si puede confirmar la transacción (esta es la primera fase del protocolo). Las réplicas
aún no confirman la transacción, pero deben asegurarse de que definitivamente podrán confirmar la transacción en la
segunda fase si así lo indica el coordinador. Esto significa, en particular, que la réplica debe escribir todas las actualizaciones
de la transacción en el disco y verificar cualquier restricción de integridad antes de responder ok al mensaje de preparación,
mientras continúa manteniendo los bloqueos para la transacción.

El coordinador recopila las respuestas y decide si realmente compromete o no la transacción.


Si todos los nodos responden bien, el coordinador decide comprometerse; si algún nodo quiere abortar, o si algún nodo no
responde dentro de un tiempo de espera, el coordinador decide abortar. Luego, el coordinador envía su decisión a cada una
de las réplicas, quienes se comprometen o abortan según las instrucciones (esta es la segunda fase). Si la decisión fue
confirmar, se garantiza que cada réplica pueda confirmar su transacción porque la solicitud de preparación anterior sentó las
bases. Si la decisión fue abortar, la réplica revierte la transacción.

El coordinador en la confirmación de dos fases

¿Qué pasa si el coordinador falla?


El Coordinador escribe su decisión en el disco .

I Cuando se recupere, lea la decisión del disco y envíela a las réplicas (o cancele

si no se tomó ninguna decisión antes del bloqueo)

I Problema: si el coordinador falla después de la preparación, pero antes de la


decisión de transmisión, otros nodos no saben cómo
ha decidido

I Las réplicas que participan en la transacción no pueden comprometerse o


cancelarse después de responder "ok" a la solicitud de preparación (de lo
contrario, corremos el riesgo de violar la atomicidad)

I El algoritmo está bloqueado hasta que el coordinador se recupere

Diapositiva 125

El problema con la confirmación de dos fases es que el coordinador es un único punto de falla. Los bloqueos del
coordinador se pueden tolerar haciendo que el coordinador escriba sus decisiones de compromiso/cancelación en un
almacenamiento estable, pero aun así, puede haber transacciones que se hayan preparado pero que aún no se hayan
comprometido/cancelado en el momento del bloqueo del coordinador (llamadas transacciones dudosas). actas). Cualquier
transacción dudosa debe esperar hasta que el coordinador se recupere para conocer su destino; no pueden decidir
unilateralmente comprometerse o abortar, porque esa decisión podría terminar siendo inconsistente con el coordinador y
otros nodos, lo que podría violar la atomicidad.
Afortunadamente, es posible evitar el punto único de falla del coordinador mediante el uso de un algoritmo de consenso
o un protocolo de transmisión de orden total. La diapositiva 126 muestra un algoritmo de compromiso de dos fases tolerante
a fallas basado en Paxos Commit [Gray y Lamport, 2006]. La idea es que cada nodo que participe en la transacción use la
transmisión de orden total para difundir su voto sobre si comprometerse o abortar.
Además, si el nodo A sospecha que el nodo B ha fallado (porque no se recibió ningún voto de B dentro de un tiempo de
espera), entonces A puede intentar votar para abortar en nombre de B. Esto introduce una condición de carrera: si el nodo B
es lento, podría ser que el nodo B transmita su propio voto para comprometerse casi al mismo tiempo que el nodo A
sospecha que B ha fallado y vota en nombre de B.
Estos votos se entregan a cada nodo por emisión de orden total, y cada destinatario cuenta los votos de forma
independiente. Al hacerlo, contamos solo el primer voto de cualquier réplica dada e ignoramos cualquier voto posterior de la
misma réplica. Dado que la transmisión de orden total garantiza la misma orden de entrega en cada nodo, todos los nodos
acordarán si el primer voto entregado de una réplica dada fue un voto de confirmación o un voto de cancelación, incluso en
el caso de una condición de carrera entre múltiples nodos que transmiten votos contradictorios. por la misma réplica.

Si un nodo observa que el primer voto entregado desde alguna réplica es un voto para abortar, entonces la transacción

66
Machine Translated by Google

puede ser abortado inmediatamente. De lo contrario, un nodo debe esperar hasta que haya entregado al menos un voto de cada réplica.
Una vez que se han entregado estos votos, y ninguna de las réplicas vota para abortar en su primer mensaje entregado, entonces se puede
confirmar la transacción. Gracias a la transmisión de orden total, se garantiza que todos los nodos tomarán la misma decisión sobre si
abortar o confirmar, lo que preserva la atomicidad.

Confirmación de dos fases tolerante a errores (1/2)


en la inicialización de la transacción T
haga commitVotes[T] := {}; réplicas[T] := {}; decidido[T] := fin falso en

a petición de cometer la transacción T con los nodos participantes R do


para cada r ÿ R enviar (Preparar , T, R) a r final en

al recibir (Preparar, T, R) en el nodo replicaId do


replicas[T] := R ok = "¿la transacción T puede
comprometerse en esta réplica?" transmisión de orden total
(Vote, T, replicaId, ok) a réplicas [T] finaliza en

en un nodo se sospecha que el replicaId del nodo se bloqueó


para cada transacción T en la que participó replicaId hacer
transmisión de orden total (voto, T, replicaId, falso) a réplicas [T] final
por final en

Diapositiva 126

Compromiso de dos fases tolerante a fallas (2/2)

en la entrega (Vote, T, replicaId, ok) por emisión de orden total do if


replicaId ÿ/ commitVotes[T] ÿ replicaId ÿ replicas[T] ÿ ¬decided[T] then

if ok = true ,
entonces commitVotes[T] := commitVotes[T] ÿ {replicaId}
if commitVotes[T] = replicas[T] then decide[T] := true
confirma la transacción T en este nodo

terminar
si se decidió [T] :=
true abortar transacción T en este
nodo terminar si
terminar

si termina en

Diapositiva 127

7.2 Linealizabilidad
Un protocolo de compromiso atómico es una forma de preservar la coherencia entre múltiples réplicas frente a fallas, al garantizar que todos
los participantes de una transacción se comprometan o cancelen. Sin embargo, cuando hay varios nodos que leen y modifican
simultáneamente algunos datos compartidos, no es suficiente garantizar el mismo resultado de confirmación o cancelación para todos los
nodos. También tenemos que razonar sobre la interacción que surge de la actividad concurrente.

En esta sección, presentaremos un modelo de consistencia particular para el sistema concurrente que se llama linearizabilidad.
Discutiremos la linealizabilidad de manera informal; si está interesado en los detalles, Herlihy y Wing [1990] dan una definición formal. La
gente a veces dice consistencia fuerte cuando se refiere a la linealizabilidad, pero el concepto de "consistencia fuerte" es bastante vago e
impreciso. Nos apegaremos al término linearizabilidad, que tiene un significado definido con precisión.

En la diapositiva 128 aparece una definición informal de linealizabilidad . En las siguientes diapositivas aclararemos
lo que esto significa a través de ejemplos.
La linealizabilidad es un concepto útil no solo en sistemas distribuidos, sino también en el contexto de la concurrencia de memoria
compartida en una sola máquina. Curiosamente, en una computadora con múltiples núcleos de CPU (prácticamente todos los servidores,
computadoras portátiles y teléfonos inteligentes en la actualidad), el acceso a la memoria no se puede linealizar de manera predeterminada. Esta

67
Machine Translated by Google

se debe a que cada núcleo de CPU tiene sus propios cachés y una actualización realizada por un núcleo no se refleja inmediatamente
en el caché de otro núcleo. Por lo tanto, incluso una sola computadora comienza a comportarse un poco como un sistema replicado. La
unidad L304 en la Parte III entra en detalles del comportamiento de la memoria multinúcleo.
No confunda la linealizabilidad con la serializabilidad, aunque ambas palabras parecen significar algo así como "puede organizarse
en un orden secuencial". La serialización significa que las transacciones tienen el mismo efecto que si se hubieran ejecutado en algún
orden en serie, pero no define cuál debería ser ese orden.
La linealizabilidad define los valores que deben devolver las operaciones, según la concurrencia y el orden relativo de esas operaciones.
Es posible que un sistema proporcione serializabilidad y linealizabilidad: la combinación de las dos se denomina serializabilidad estricta
o serializabilidad de una copia.

Comienzo del video sección 7.2

linealizabilidad (descarga mp4)

Múltiples nodos accediendo simultáneamente a datos replicados.


¿Cómo definimos aquí “coherencia”?

La opción más fuerte: linealizabilidad

Informalmente: cada operación surte efecto atómicamente en algún


momento después de que comenzó y antes de que terminara

I Todas las operaciones se comportan como si se ejecutaran en una sola


copia de los datos (incluso si de hecho hay varias réplicas)
I Consecuencia: cada operación devuelve un valor "actualizado", también
conocido como "coherencia fuerte"

I No solo en sistemas distribuidos, también en concurrencia de memoria


compartida (¡la memoria en CPU multinúcleo no se puede linealizar de
forma predeterminada!)

Nota: linealizabilidad 6= serializabilidad!

Diapositiva 128

El objetivo principal de la linealización es garantizar que los nodos observen el sistema en un estado "actualizado"; es decir, no
leen valores obsoletos (obsoletos). Anteriormente hemos visto este concepto de leer un valor "actualizado" en el contexto de la
consistencia de lectura después de escritura (Diapositiva 97). Sin embargo, mientras que la coherencia de lectura después de la
escritura define solo un modelo de coherencia para las lecturas y escrituras realizadas por el mismo nodo, la linealización generaliza
esta idea a las operaciones realizadas simultáneamente por diferentes nodos.
Desde el punto de vista de un cliente, cada operación lleva una cierta cantidad de tiempo. Decimos que una operación comienza
en el momento en que es solicitada por la aplicación y finaliza cuando el resultado de la operación es devuelto a la aplicación. Entre el
inicio y el final, pueden ocurrir varios pasos de comunicación de red; por ejemplo, si se utilizan quórums, una operación puede finalizar
cuando el cliente haya recibido respuestas de un quórum de réplicas.

En la diapositiva 129 y las diapositivas siguientes, representamos la vista del cliente de una operación de obtener/establecer como
un rectángulo que cubre el período de tiempo desde el principio hasta el final de una operación. Dentro del rectángulo escribimos el
efecto de la operación: set(x, v) significa actualizar el elemento de datos x para que tenga el valor v, y get(x) ÿ v significa una lectura de
x que devuelve el valor v.

Revisión de la consistencia de lectura después de escritura

cliente
A B C

(t1, conjunto (x, v1))

OK OK

obtener (x)

(t0, v0) (t1, v1)

Diapositiva 129

68
Machine Translated by Google

La linealizabilidad es independiente de la implementación del sistema y los protocolos de comunicación: todo lo


que importa es el tiempo de inicio y finalización de cada operación, y el resultado de la operación. Por lo tanto,
podemos omitir todas las réplicas y las flechas de envío de mensajes y observar el comportamiento del sistema solo
desde el punto de vista del cliente.
Lo más importante de lo que se preocupa la linealización es si una operación terminó antes de que comenzara
otra, independientemente de los nodos en los que tuvieron lugar. En la diapositiva 130, las dos operaciones get
comienzan después de que la operación set haya terminado y, por lo tanto, esperamos que las operaciones get
devuelvan el valor v1 escrito por set.
Por otro lado, en la diapositiva 131, las operaciones get y set se superponen en el tiempo: en este caso, no
necesariamente sabemos en qué orden tienen efecto las operaciones. get puede devolver el valor v1 escrito por set,
o el valor anterior de x v0, y cualquier resultado es aceptable.
Tenga en cuenta que "la operación A terminó antes de que comenzara la operación B" no es lo mismo que "A
sucedió antes que B". La relación sucede antes (Sección 3.3) se define en términos de mensajes enviados y
recibidos; es posible tener dos operaciones que no se superponen en el tiempo, pero que aún son concurrentes de
acuerdo con la relación antes de que ocurra, porque no ha ocurrido comunicación entre esas operaciones. Por otro
lado, la linealizabilidad se define en términos de tiempo real: es decir, un observador global hipotético que puede ver
instantáneamente el estado de todos los nodos (o un reloj perfectamente sincronizado en cada nodo) determina los
tiempos de inicio y finalización de cada operación. . En realidad, tal observador global o reloj perfectamente
sincronizado no existe en un sistema con latencia de red variable, pero podemos definir la linealizabilidad en términos
de tal observador hipotético. Esto tiene la ventaja de que si demostramos que un sistema es linealizable, podemos
estar seguros de que sus garantías de consistencia se mantienen independientemente de si se ha producido alguna
comunicación o no.

Desde el punto de vista del cliente

cliente 1 cliente 2
Me enfoco en observable por el cliente
comportamiento: cuándo y qué
?
vuelve la operación

Ignoro cómo la replicación


el sistema se implementa internamente
? I ¿Terminó la operación A antes de que
tiempo real
comenzara la operación B?
tiempo real

¿ Incluso si las operaciones están en


?
nodos diferentes?

I Esto no sucede antes: queremos que el


cliente 2 lea el valor escrito por el cliente
?
1, ¡incluso si los clientes no se han
comunicado!

Diapositiva 130

Operaciones superpuestas en el tiempo

cliente 1 cliente 2

I La operación de obtención del cliente 2

se superpone en el tiempo con la

operación de establecimiento del

cliente 1. ¿ Quizás la operación de


establecimiento surte efecto primero?

Es igualmente probable que la


operación de obtención se
ejecute primero

Cualquiera de los dos resultados está bien

en este caso

Diapositiva 131

69
Machine Translated by Google

La linealizabilidad no se trata solo de la relación de una operación de obtención con una operación de establecimiento anterior,
sino que también puede relacionar una operación de obtención con otra. La diapositiva 132 muestra un ejemplo de un sistema que
utiliza lecturas y escrituras de quórum, pero que, sin embargo, no es linealizable. Aquí, el cliente 1 establece x en v1 y, debido a una
peculiaridad de la red, la actualización de la réplica A se realiza rápidamente, mientras que las actualizaciones de las réplicas B y C se retrasan.
El cliente 2 lee de un quórum de {A, B}, recibe las respuestas {v0, v1} y determina que v1 es el valor más nuevo en función de la marca
de tiempo adjunta. Una vez finalizada la lectura del cliente 2, el cliente 3 inicia una lectura desde un quórum de {B, C}, recibe v0 de
ambas réplicas y devuelve v0 (ya que no conoce v1).
Por lo tanto, el cliente 3 observa un valor más antiguo que el cliente 2, aunque el orden de las operaciones en tiempo real requeriría
que la lectura del cliente 3 devuelva un valor que no es más antiguo que el resultado del cliente 2. Este comportamiento no está
permitido en un sistema linealizable.

No linealizable, a pesar de las lecturas/escrituras de quórum

cliente 1 cliente 2 cliente 3


A B C

(t1, conjunto (x, v1))


obtener (x)
OK

(t1, v1)

(t0, v0)
obtener (x)

(t0, v0)

OK
OK (t0, v0)

Diapositiva 132

No linealizable, a pesar de las lecturas/escrituras de quórum

cliente 1 cliente 2 cliente 3

I La operación del cliente 2 finaliza


antes que la operación del cliente 3
empieza

I Linealizabilidad por lo tanto


requiere la operación del cliente 3 tiempo real
observar un estado no mayor
que la operación del cliente 2
I Este ejemplo viola
linearizabilidad porque v0 es
anterior a v1

Diapositiva 133

Afortunadamente, es posible hacer que las operaciones get y set sean linealizables usando lecturas y escrituras de quórum. Las
operaciones de configuración no cambian: como antes, envían la actualización a todas las réplicas y esperan el reconocimiento de un
quórum de réplicas.
Para las operaciones de obtención, se requiere otro paso, como se muestra en la diapositiva 134. Un cliente primero debe enviar
la solicitud de obtención a las réplicas y esperar las respuestas de un quórum. Si algunas respuestas incluyen un valor más reciente
que otras respuestas, como lo indican sus marcas de tiempo, entonces el cliente debe volver a escribir el valor más reciente en todas
las réplicas que aún no respondieron con el valor más reciente, como en la reparación de lectura (Diapositiva 100) . La operación de
obtención finaliza solo después de que el cliente está seguro de que el valor más reciente está almacenado en un quórum de réplicas:
es decir, después de que un quórum de réplicas respondió bien a la reparación de lectura o respondió con el valor más reciente en
primer lugar. .
Este enfoque se conoce como el algoritmo ABD, en honor a sus autores Attiya, Bar-Noy y Dolev [Attiya et al., 1995]. Garantiza
lecturas y escrituras linealizables, porque cada vez que finaliza una operación de obtención y configuración,

70
Machine Translated by Google

sabemos que el valor leído o escrito está presente en un quórum de réplicas y, por lo tanto, se garantiza que cualquier lectura
de quórum posterior observará ese valor (o un valor posterior).

Hacer que las lecturas/escrituras de quórum sean linealizables

cliente 1 cliente 2 cliente 3


A B C

(t1, conjunto (x, v1))


obtener (x)
OK

(t1, v1)

(t0, v0)
(t1, conjunto (x, v1))
...
OK
OK

...
OK
OK

Diapositiva 134

Linealizabilidad para diferentes tipos de operación.

Esto garantiza la linealización de get (lectura de quórum) y set


(escritura ciega en quórum)

I Cuando finaliza una operación, el valor leído/escrito es


almacenado en un quórum de réplicas

I Cada operación de quórum posterior verá ese valor

Múltiples escrituras simultáneas pueden sobrescribirse entre sí

¿Qué pasa con una operación atómica de comparación e intercambio ?

I CAS(x, oldValue, newValue) establece x en newValue iff


el valor actual de x es oldValue

Discutí anteriormente en la concurrencia de memoria compartida

¿ Podemos implementar comparaciones e intercambios linealizables en un


¿Sistema distribuido?

Yo sí: ¡ orden total emitida al rescate de nuevo!

Diapositiva 135

La operación de conjunto para la que el algoritmo ABD garantiza la linealización es la denominada escritura ciega (escritura
incondicional): simplemente sobrescribe el valor de un elemento de datos, independientemente de su valor anterior. Si varios
clientes escriben al mismo tiempo en el mismo elemento, y si se utiliza una política de resolución de conflictos donde el último
escritor gana (Diapositiva 95), entonces una de esas escrituras terminará como el "ganador" y los otros valores se descartarán
silenciosamente.
En algunas aplicaciones, queremos ser más cuidadosos y sobrescribir un valor solo si otro nodo no lo ha modificado al
mismo tiempo. Esto se puede lograr con una operación atómica de comparación e intercambio (CAS). En la primera mitad de
este curso se analizó una operación CAS para la concurrencia entre subprocesos en un solo nodo. Esto plantea la pregunta:
¿cómo podemos implementar una operación CAS linealizable en un sistema replicado y distribuido?

Recuerde que el propósito de la linealizabilidad es hacer que un sistema se comporte como si hubiera una sola copia de los
datos, y todas las operaciones en él sucedan de forma atómica, incluso si el sistema está de hecho replicado. Esto hace que
CAS sea una operación natural que se desee admitir en un contexto linealizable.
El algoritmo ABD no puede implementar CAS, porque diferentes réplicas pueden ver las operaciones en un orden diferente
y, por lo tanto, llegar a conclusiones inconsistentes sobre si una operación CAS en particular tuvo éxito o no. Sin embargo, es
posible implementar una operación CAS linealizable y replicada utilizando la transmisión de orden total, como se muestra en la
diapositiva 136. Simplemente transmitimos cada operación que queremos realizar y, de hecho, ejecutamos la operación cuando
se entrega. Al igual que en la replicación de máquinas de estado (Diapositiva 101), este algoritmo asegura que una operación
tenga el mismo efecto y resultado en cada réplica.

71
Machine Translated by Google

Comparación e intercambio linealizable (CAS)


a pedido para realizar get (x) hacer la transmisión
total del pedido (get, x) y esperar a que finalice la entrega el

a pedido para realizar CAS (x, antiguo, nuevo) hacer una


transmisión total del pedido (CAS, x, antiguo, nuevo) y esperar a que finalice la entrega el

en la entrega (obtener, x) por emisión de pedido total hacer


devolver localState[x] como resultado de la operación get(x) end on

al entregar (CAS, x, antiguo, nuevo) por emisión de orden total hacer éxito := false if
localState[x] = antiguo then localState[x] := nuevo; exito := final verdadero si devuelve

exito como resultado de la operacion CAS(x, antiguo, nuevo)

finalizará el

Diapositiva 136

Ejercicio 17. ¿Es linealizable la siguiente ejecución? Si no, ¿dónde ocurre la violación?

nodo A nodo B nodo C nodo D

obtener(x)
ÿ1 conjunto(x,
0)
conjunto(x,
1)
obtener(x)
ÿ1
ÿverdad
cas(x,
2)
1,
obtener(x)
ÿ2
case(x,
ÿfalso
3)
0,
obtener(
ÿ4
ÿverdadero
cas(x,
4)
2,
obtener(x)
ÿ2

7.3 Coherencia eventual


La linealizabilidad es un modelo de consistencia muy conveniente para los sistemas distribuidos, porque garantiza que un
sistema se comporte como si solo hubiera una copia de los datos, incluso si de hecho se replican. Esto permite que las
aplicaciones ignoren algunas de las complejidades de trabajar con sistemas distribuidos. Sin embargo, esta sólida garantía
también tiene un costo y, por lo tanto, la linealización no es adecuada para todas las aplicaciones.
Parte del costo es el rendimiento: tanto el algoritmo ABD como el algoritmo CAS linealizable basado en la transmisión
de orden total necesitan enviar una gran cantidad de mensajes a través de la red y requieren una cantidad significativa de
espera debido a la latencia de la red. Parte es la escalabilidad: en los algoritmos donde todas las actualizaciones deben
secuenciarse a través de un líder, como Raft, el líder puede convertirse en un cuello de botella que limita la cantidad de
operaciones que se pueden procesar por segundo.
Quizás el mayor problema con la linealización es que cada operación requiere comunicación con un quórum de réplicas.
Si un nodo no puede comunicarse temporalmente con suficientes réplicas,

72
Machine Translated by Google

no puede realizar ninguna operación. Aunque el nodo se esté ejecutando, una falla de comunicación de este tipo hace que no esté
disponible.

Comienzo del video sección 7.3

Consistencia eventual (descarga mp4)

Ventajas de linealizabilidad:

Hace que un sistema distribuido se comporte como si no fuera


distribuido

I Fácil de usar para las aplicaciones

Desventajas:

I Costo de desempeño : muchos mensajes y esperando


respuestas

I Límites de escalabilidad : el líder puede ser un cuello de botella

I Problemas de disponibilidad : si no puede comunicarse con un quórum de


nodos, no puede procesar ninguna operación

Consistencia eventual: un modelo más débil que la linealizabilidad.


Diferentes opciones de compensación.

Diapositiva 137

Como ejemplo, considere la aplicación de calendario que puede encontrar en la mayoría de los teléfonos, tabletas y computadoras.
Nos gustaría que las citas y las entradas en esta aplicación se sincronicen en todos nuestros dispositivos; en otras palabras, queremos
que se replique de manera que cada dispositivo sea una réplica. Además, nos gustaría poder ver, modificar y agregar eventos de
calendario incluso cuando un dispositivo está fuera de línea (por ejemplo, debido a una cobertura de red móvil deficiente). Si el protocolo
de replicación de la aplicación de calendario fuera linealizable, esto no sería posible, ya que un dispositivo fuera de línea no puede
comunicarse con un quórum de réplicas.
En cambio, las aplicaciones de calendario permiten al usuario leer y escribir eventos en su calendario incluso cuando un dispositivo
está desconectado, y sincronizan las actualizaciones entre dispositivos en algún momento posterior, en segundo plano, cuando hay una
conexión a Internet disponible. El video de esta conferencia incluye una demostración de actualizaciones sin conexión a un calendario.

Diapositiva 138

Esta compensación se conoce como el teorema CAP (llamado así por la consistencia, la disponibilidad y la tolerancia de partición),
que establece que si hay una partición de red en un sistema, debemos elegir entre una de las siguientes opciones [Gilbert y Lynch,
2002 ]:

1. Podemos tener consistencia linealizable, pero en este caso, algunas réplicas no podrán responder a las solicitudes porque no
pueden comunicarse con un quórum. No poder responder a las solicitudes hace que esos nodos no estén disponibles.

2. Podemos permitir que las réplicas respondan a las solicitudes incluso si no pueden comunicarse con otras réplicas.
En este caso, siguen estando disponibles, pero no podemos garantizar la linearización.

A veces, el teorema CAP se formula como una elección de "elegir 2 de 3", pero ese encuadre es engañoso.

73
Machine Translated by Google

Un sistema puede ser linealizable y disponible siempre que no haya una partición de red, y la elección se fuerza solo en
presencia de una partición [Kleppmann, 2015].
Esta compensación se ilustra en la diapositiva 139, donde el nodo C no puede comunicarse con los nodos A y B. En el lado
de la partición de A y B, las operaciones linealizables pueden continuar con normalidad, porque A y B constituyen un quórum.
Sin embargo, si C desea leer el valor de x, debe esperar (posiblemente indefinidamente) hasta que se repare la partición de la
red, o debe devolver su valor local de x, que no refleja el valor previamente escrito por A en el otro. lado de la partición.

El teorema de la PAC
Un sistema puede ser fuertemente consistente (linealizable) o
Disponible en presencia de una partición de red

nodo A nodo B nodo C

redconjunto
v1)
(x,
partició
de
ÿv0 obtener(x)
obtener(x)
ÿv1 obtener
ÿv1
C debe esperar indefinidamente a que la red se recupere o devolver un valor
potencialmente obsoleto
Diapositiva 139

La aplicación de calendario elige la opción 2: renuncia a la linealización a favor de permitir que el usuario continúe realizando
operaciones mientras un dispositivo está fuera de línea. Muchos otros sistemas también hacen esta elección para varios
razones.
El enfoque de permitir que cada réplica procese lecturas y escrituras basándose únicamente en su estado local y sin esperar
la comunicación con otras réplicas se denomina replicación optimista. Se han propuesto una variedad de modelos de consistencia
para sistemas replicados de manera optimista, siendo el más conocido el de consistencia eventual.

La coherencia eventual se define como: "si no se realizan nuevas actualizaciones en un objeto, eventualmente todas las
lecturas devolverán el último valor actualizado" [Vogels, 2009]. Esta es una definición muy débil: ¿qué sucede si las
actualizaciones de un objeto nunca se detienen, por lo que la premisa de la declaración nunca es cierta? Un modelo de
consistencia ligeramente más fuerte llamado consistencia eventual fuerte, definido en la diapositiva 140, suele ser más apropiado [Shapiro et al., 201
Se basa en la idea de que cuando dos réplicas se comunican, convergen hacia el mismo estado.

Consistencia eventual
Las operaciones de procesamiento de réplicas se basan únicamente en su estado local.

Si no hay más actualizaciones, eventualmente todas las réplicas estarán en el


mismo estado. (No hay garantías de cuánto tiempo podría tomar).

Fuerte consistencia eventual: I

Entrega eventual: cada actualización realizada en una réplica no defectuosa


finalmente es procesada por cada réplica no defectuosa.

I Convergencia: dos réplicas cualesquiera que hayan procesado el mismo


conjunto de actualizaciones están en el mismo estado (incluso si las
actualizaciones se procesaron en un orden diferente).

Propiedades:

I No requiere esperar la comunicación de red I La transmisión causal

(o más débil) puede difundir actualizaciones I Actualizaciones simultáneas


=ÿ los conflictos deben resolverse

Diapositiva 140

Tanto en la consistencia final como en la consistencia final fuerte, existe la posibilidad de que diferentes nodos actualicen
simultáneamente el mismo objeto, lo que genera conflictos (como se discutió anteriormente en la Diapositiva 95). Se han
desarrollado varios algoritmos para resolver esos conflictos automáticamente [Shapiro et al., 2011].

74
Machine Translated by Google

El video de la conferencia muestra un ejemplo de un conflicto en la aplicación de calendario eventualmente consistente:


en un dispositivo actualizo la hora de un evento, mientras que en otro dispositivo actualizo el título del mismo evento.
Después de sincronizar los dos dispositivos, la actualización de la hora se aplica a ambos dispositivos, mientras que la
actualización del título se descarta. Por lo tanto, el estado de los dos dispositivos converge, a costa de una pequeña pérdida
de datos. Este es el último enfoque que gana el escritor para la resolución de conflictos que hemos visto en la diapositiva
95 (suponiendo que la actualización de la hora es la "última" actualización en este ejemplo). Un enfoque más refinado
podría fusionar las actualizaciones de la hora y el título, como se muestra en la diapositiva 143.
Esto nos lleva al final de nuestra discusión sobre los modelos de consistencia. La diapositiva 141 resume algunas de
las propiedades clave de los modelos que hemos visto, en orden descendente de la fuerza mínima de las suposiciones que
deben hacer sobre el modelo del sistema.
La confirmación atómica hace las suposiciones más fuertes, ya que debe esperar la comunicación con todos los nodos
que participan en una transacción (potencialmente todos los nodos en el sistema) para completarse con éxito.
Los algoritmos de consenso, transmisión de orden total y linealizables hacen suposiciones más débiles, ya que solo
requieren esperar la comunicación con un quórum, por lo que pueden tolerar algunos nodos no disponibles. El resultado
de FLP (Diapositiva 107) nos mostró que el consenso y la transmisión de orden total requieren una sincronía parcial. Se
puede demostrar que una operación CAS linealizable es equivalente al consenso [Herlihy, 1991] y, por lo tanto, también
requiere una sincronía parcial. Por otro lado, el algoritmo ABD para get/set linealizable es asíncrono, ya que no requiere
relojes ni tiempos de espera. Finalmente, la consistencia eventual y la consistencia final fuerte hacen las suposiciones más
débiles: las operaciones se pueden procesar sin esperar ninguna comunicación con otros nodos y sin ninguna suposición
de tiempo. De manera similar, en la transmisión causal y las formas más débiles de transmisión (FIFO, confiable, etc.), un
nodo que transmite un mensaje puede entregárselo a sí mismo de inmediato sin esperar la comunicación con otros nodos,
como se explica en la Sección 4.2; esto corresponde a una réplica que procesa inmediatamente sus propias operaciones
sin esperar la comunicación con otras réplicas.
Esta jerarquía tiene algunas similitudes con el concepto de clases de complejidad de algoritmos, por ejemplo, la
clasificación generalmente es O (n log n), en el sentido de que captura los requisitos mínimos inevitables de comunicación
y sincronía para una variedad de problemas comunes en sistemas distribuidos.

Resumen de los requisitos mínimos del modelo de sistema

Problema Debe esperar la Requiere


comunicación. sincronía

compromiso atómico todos los nodos


participantes parcialmente síncrono

consenso, cuyo
transmisión de orden parcialmente síncrono
total, CAS linealizable

get/set linealizable cuyo asincrónico

consistencia eventual, réplica local solo asíncrona


transmisión causal,
transmisión FIFO

Diapositiva 141

8 Control de concurrencia en aplicaciones


En esta última lección, veremos un par de ejemplos de sistemas distribuidos que necesitan administrar el acceso simultáneo
a los datos. En particular, incluiremos algunos estudios de casos de sistemas prácticos del mundo real que deben lidiar con
la concurrencia y que se basan en los conceptos del resto de este curso.

8.1 Colaboración y resolución de conflictos

El software de colaboración es una amplia categoría de software que facilita que varias personas trabajen juntas en alguna
tarea. Esto incluye aplicaciones como Google Docs/Office 365 (documentos de texto multiusuario, hojas de cálculo,
presentaciones, etc.), Overleaf ( documentos LATEX colaborativos), software de gráficos multiusuario (p. ej., Figma),
herramientas de planificación de proyectos (p. ej., Trello), aplicaciones para tomar notas (por ejemplo, OneNote, Evernote,
Notion) y calendarios compartidos entre colegas o familiares (como la sincronización de calendario que vimos en la diapositiva 138).

75
Machine Translated by Google

El software de colaboración moderno permite que varias personas actualicen un documento al mismo tiempo, sin tener que
enviar y recibir archivos por correo electrónico. Esto hace que la colaboración sea otro ejemplo de replicación: cada dispositivo
en el que un usuario ha abierto un documento es una réplica, y cualquier actualización realizada en una réplica debe enviarse a
través de la red a las réplicas en otros dispositivos.
En principio, sería posible utilizar un esquema de replicación linealizable para software de colaboración.
Sin embargo, dicho software sería lento de usar, ya que cada operación de lectura o escritura tendría que ponerse en contacto
con un quórum de réplicas; además, no funcionaría en un dispositivo que no esté conectado. En su lugar, en aras de un mejor
rendimiento y una mayor solidez frente a las interrupciones de la red, la mayoría del software de colaboración utiliza una
replicación optimista que proporciona una fuerte consistencia eventual (Diapositiva 140).

Comienzo del video sección


Colaboración y resolución de conflictos 8.1 (descarga mp4)

Hoy en día usamos mucho software de colaboración:

I Ejemplos: sincronización de calendario (última lección), Google Docs, . . .

I Varios usuarios/dispositivos trabajando en un archivo/documento compartido


I Cada dispositivo de usuario tiene una réplica local de los datos I Actualizar

la réplica local en cualquier momento (incluso sin conexión), sincronizar con


otros cuando la red esté disponible I Desafío: ¿cómo reconciliar

actualizaciones simultáneas?

Familias de algoritmos:

Tipos de datos replicados sin conflictos ( CRDT)


Basado en operaciones
basado en el estado

I Transformación Operacional (OT)

Diapositiva 142

En esta sección veremos algunos algoritmos que se utilizan para este tipo de colaboración. Como ejemplo, considere la
demostración de sincronización del calendario en la grabación de la conferencia de la Sección 7.3. Inicialmente, dos nodos
comienzan con la misma entrada de calendario. En el nodo A, el título cambia de "Conferencia" a "Conferencia 1" y, al mismo
tiempo, en el nodo B, la hora cambia de 12:00 a 10:00. Estas dos actualizaciones ocurren mientras los dos nodos no pueden
comunicarse temporalmente, pero finalmente se restaura la conectividad y los dos nodos sincronizan sus cambios. En el
resultado que se muestra en la diapositiva 143, la entrada final del calendario refleja tanto el cambio en el título como el cambio
en la hora.

Conflictos debido a actualizaciones simultáneas

nodo A nodo B

{ {
"título": "Conferencia", "título": "Conferencia",
"fecha": "2020-11-05", "fecha": "2020-11-05",
"hora": "12:00" "hora": "12:00"
} }

título = "Conferencia 1" hora = "10:00"


{ {
"título": "Conferencia 1", "título": "Conferencia",
"fecha": "2020-11-05", "fecha": "2020-11-05",
"hora": "12:00" "hora": "10:00"
} }
sincronizar

{ {
"título": "Conferencia 1", "título": "Conferencia 1",
"fecha": "2020-11-05", "fecha": "2020-11-05",
"hora": "10:00" "hora": "10:00"
} }

Diapositiva 143

Este escenario es un ejemplo de resolución de conflictos, que ocurre cada vez que varias escrituras simultáneas en el
mismo objeto deben integrarse en un solo estado final (consulte también la diapositiva 95). Los tipos de datos replicados sin
conflictos, o CRDT para abreviar, son una familia de algoritmos que realizan dicha resolución de conflictos [Shapiro et al., 2011].
Un CRDT es un objeto replicado al que accede una aplicación a través de la interfaz orientada a objetos de un tipo de datos
abstracto, como un conjunto, una lista, un mapa, un árbol, un gráfico, un contador, etc.
La diapositiva 144 muestra un ejemplo de un CRDT que proporciona un mapa de claves a valores. La aplicación

76
Machine Translated by Google

puede invocar dos tipos de operaciones: leer el valor de una clave dada y establecer el valor de una clave dada (que
agrega la clave si aún no está presente).
El estado local en cada nodo consta de los valores establecidos que contienen (marca de tiempo, clave, valor) triples.
La lectura del valor de una clave dada es una operación puramente local que solo inspecciona los valores en el nodo
actual y no realiza ninguna comunicación de red. El algoritmo conserva la invariante de que los valores contienen como
máximo un elemento para cualquier clave dada. Por lo tanto, al leer el valor de una clave, el valor es único si existe.

Mapa basado en operaciones CRDT


en la inicialización
hacer valores: =
{} final en

a pedido para leer el valor de la clave k hacer


si ÿt, v. (t, k, v) ÿ valores luego devolver v de lo contrario devolver
nulo final en

a pedido para establecer la clave k en el valor v


do t := newTimestamp() . único a nivel mundial, por ejemplo, transmisión de marca de
tiempo de Lamport (establecido, t, k, v) por transmisión confiable (incluso a uno mismo)
finaliza el

al entregar (establecer, t, k, v) por transmisión confiable


0 0
hacer anterior := {(t ,k0
= {}
,entonces
v0)
ÿ ÿ(t
ÿ valores
k0 , v0) |ÿkanterior.
= k} si anterior
t0 < t
0
,
valores := (valores \ anterior) ÿ {(t, k, v)} final si
finaliza en

Diapositiva 144

Para actualizar el valor de una clave dada, creamos una marca de tiempo global única para la operación (una marca
de tiempo de Lamport (Diapositiva 66) es una buena opción) y luego transmitimos un mensaje que contiene la marca de
tiempo, la clave y el valor. Cuando se entrega ese mensaje, verificamos si la copia local de valores ya contiene una
entrada con una marca de tiempo más alta para la misma clave; si es así, ignoramos el mensaje, porque el valor con la
marca de tiempo más alta tiene prioridad. De lo contrario, eliminamos el valor anterior (si lo hay) y agregamos el triple
nuevo (marca de tiempo, clave, valor) a los valores. Esto significa que resolvemos las actualizaciones simultáneas de la
misma clave utilizando el enfoque de último escritor gana (LWW) que vimos en la diapositiva 95.

CRDT basados en operaciones


La transmisión confiable puede entregar actualizaciones en cualquier orden:

Transmito (set, t1 , “título”, “Conferencia 1”)

Transmito (set, t2 , “hora”, “10:00”)

Recuerde la consistencia eventual fuerte:

I Entrega eventual: cada actualización realizada en una réplica no defectuosa


finalmente es procesada por cada réplica no defectuosa.

I Convergencia: dos réplicas cualesquiera que hayan procesado el mismo


conjunto de actualizaciones están en el mismo estado

El algoritmo CRDT implementa esto:

I La transmisión confiable garantiza que cada operación se entregue


finalmente a cada réplica (no bloqueada)
I Aplicar una operación es conmutativa: orden de entrega
no importa
Diapositiva 145

Este algoritmo es un ejemplo de un enfoque que insinuamos en la diapositiva 104, a saber, un método para realizar
la replicación utilizando una transmisión confiable, sin requerir una entrega totalmente ordenada. Es un CRDT basado en
operaciones porque cada mensaje de difusión contiene una descripción de una operación de actualización (a diferencia
de los CRDT basados en estado que veremos en breve). Permite que las operaciones se completen sin conectividad de
red, porque el remitente de una transmisión confiable puede enviarse un mensaje a sí mismo de inmediato y enviarlo a
otros nodos en algún momento posterior. Además, aunque los mensajes pueden entregarse en diferentes órdenes en
diferentes réplicas, el algoritmo garantiza una fuerte consistencia eventual porque la función que actualiza el estado de
una réplica es conmutativa.

77
Machine Translated by Google

Ejercicio 18. Demuestre que el algoritmo CRDT del mapa basado en operaciones proporciona una fuerte consistencia eventual.

Ejercicio 19. Proporcione un pseudocódigo para una variante del algoritmo CRDT de mapa basado en operaciones que tiene una
semántica de registro de valores múltiples en lugar de una semántica de que el último escritor gana; es decir, cuando hay varias
actualizaciones simultáneas para la misma clave, el algoritmo debe conservar todas esas actualizaciones en lugar de conservar
solo la que tiene la marca de tiempo más grande.

En la diapositiva 146 se muestra un algoritmo CRDT alternativo para el mismo tipo de datos de mapa. La definición de valores
y la función para leer el valor de una clave es la misma que en la diapositiva 144. Sin embargo, las actualizaciones se manejan de
manera diferente: en lugar de transmitir cada operación, actualizamos directamente los valores y luego transmitimos la totalidad
de los valores. Al entregar este mensaje en otra réplica, fusionamos los estados de las dos réplicas usando una función de fusión
t. Esta función de combinación compara las marcas de tiempo de las entradas con la misma clave y conserva las que tienen la
marca de tiempo mayor.

Mapa basado en estado CRDT


El operador t fusiona dos estados s1 y s2 de la siguiente manera:
0
s1 t s2 = {(t, k, v) ÿ (s1 ÿ s2) | @(t 0 , k0 , v0) ÿ (s1 ÿ s2). k0 = k ÿ t > t}

en la inicialización
hacer valores: =
{} final en

a pedido para leer el valor de la clave k hacer


si ÿt, v. (t, k, v) ÿ valores luego devolver v de lo contrario devolver
nulo final en

a pedido para establecer la clave k en el valor v


do t := newTimestamp() . único globalmente, por ejemplo, valores de marca de tiempo
0 0
, de
de Lamport := {(t k0 , v0)
mejor
ÿ valores
esfuerzo en 6=
| k valores de k} ÿ {(t, k, v)} por fin de transmisión
transmisión

sobre la entrega de V por transmisión de mejor esfuerzo


valores := valores t V
terminan en
Diapositiva 146

Este enfoque de transmitir todo el estado de la réplica y fusionarlo con el estado de otra réplica se denomina CRDT basado
en el estado. La desventaja del enfoque basado en el estado es que es probable que los mensajes de difusión sean más grandes
que en el enfoque basado en operaciones. La ventaja del enfoque basado en el estado es que puede tolerar mensajes perdidos o
duplicados: siempre que dos réplicas finalmente logren intercambiar sus últimos estados, convergerán al mismo estado, incluso si
se perdieron algunos mensajes anteriores. Los mensajes duplicados también están bien porque el operador de combinación es
idempotente (cf. Diapositiva 90). Esta es la razón por la cual un CRDT basado en el estado puede usar una transmisión de mejor
esfuerzo no confiable, mientras que un CRDT basado en operaciones requiere una transmisión confiable (y algunos incluso
requieren una transmisión causal).

CRDT basados en el estado

El operador de fusión t debe satisfacer: ÿs1, s2, s3. . .


I Conmutativo: s1 t s2 = s2 t s1.

I Asociativo: (s1 t s2) t s3 = s1 t (s2 t s3).


I Idempotente: s1 t s1 = s1.

Basado en el estado versus basado en la operación:

El CRDT basado en Op generalmente tiene mensajes más pequeños

I CRDT basado en estado puede tolerar la pérdida/duplicación de mensajes

No necesariamente usa transmisión:

también puedo fusionar actualizaciones simultáneas con réplicas, por ejemplo, en


replicación de quórum, anti-entropía, . . .

Diapositiva 147

78
Machine Translated by Google

Además, los CRDT basados en el estado no se limitan a los sistemas de replicación que utilizan la transmisión. Otros métodos
de replicación, como los algoritmos de escritura de quórum y los protocolos anti-entropía que vimos en la lección 5, también pueden
usar los CRDT para la resolución de conflictos (consulte la diapositiva 94).
Como otro ejemplo de actualizaciones simultáneas y la necesidad de resolución de conflictos, consideraremos el software de
colaboración como Google Docs. Cuando escribe en un documento de Google, las pulsaciones de teclas se aplican inmediatamente
a la copia local del documento en su navegador web, sin esperar a que se sincronicen con un servidor o cualquier otro usuario. Esto
significa que cuando dos usuarios escriben al mismo tiempo, sus documentos pueden diferir temporalmente; a medida que se lleva a
cabo la comunicación de red, el sistema debe garantizar que todos los usuarios converjan en la misma vista del documento. El video
de esta conferencia incluye una demostración de Google Docs que muestra este proceso de resolución de conflictos en acción.

Diapositiva 148

Podemos pensar en un documento de texto editable en colaboración como una lista de caracteres, donde cada usuario puede
insertar o eliminar caracteres en cualquier índice de la lista. Las fuentes, el formato, las imágenes incrustadas, las tablas, etc. añaden
más complejidad, por lo que por ahora nos concentraremos en el texto sin formato. Cuando varios usuarios pueden actualizar
simultáneamente un documento de texto, surge un problema particular, que se demuestra en el ejemplo de la diapositiva 149.

En este ejemplo, dos usuarios A y B comienzan con el mismo documento, "BC". El usuario A agrega el carácter "A" al principio
del documento, para que se lea "ABC". Al mismo tiempo, el usuario B agrega el carácter "D" al final del documento, para que se lea
"BCD". A medida que A y B fusionan sus ediciones, esperaríamos que el documento final diga "ABCD".

En la diapositiva 149, las réplicas de los usuarios se comunican enviándose las operaciones que han realizado. El usuario A
envía (insertar, 0, "A") a B, y B aplica esta operación, lo que lleva al resultado deseado "ABCD". Sin embargo, cuando B envía
(insertar, 2, "D") a A y A inserta el carácter "D" en el índice 2, el resultado es "ABDC", no el "ABCD" esperado.

Edición colaborativa de texto: el problema

usuario A usuario B

antes de Cristo antes de Cristo

01 01

insertar (0, "A") insertar (2, "D")

ABC BCD
012 012

(insertar, 0, “A”) (insertar, 2, “D”)

ABDC ABCD
0123 0123

Diapositiva 149

79
Machine Translated by Google

El problema es que en el momento en que B realizó la operación de inserción (2, "D"), el índice 2 se refería a la
posición posterior al carácter "C". Sin embargo, la inserción simultánea de A en el índice 0 tuvo el efecto de aumentar los
índices de todos los caracteres posteriores en 1, por lo que la posición después de "C" ahora es el índice 3, no el índice 2.
La transformación operativa es un enfoque que se utiliza para resolver este problema. Existe una familia de diferentes
algoritmos que utilizan este enfoque y que varían en los detalles de cómo resuelven los conflictos. Pero el principio
general que tienen en común se ilustra en la diapositiva 150.

Transformación operativa
usuario A usuario B

insertar (0, "A") insertar (2, "D")

ABC BCD
012 012

(insertar, 0, “A”) (insertar, 2, “D”)

T((insertar, 2, “D”), T((insertar, 0, “A”),


(insertar, 0, “A”)) = (insertar, 2, “D”)) =
(insertar, 3, “D”) (insertar, 0, “A”)

ABCD ABCD
0123 0123

Diapositiva 150

Un nodo realiza un seguimiento del historial de operaciones que ha realizado. Cuando un nodo recibe la operación
de otro nodo que es concurrente con una o más de sus propias operaciones, transforma la operación entrante en relación
con sus propias operaciones concurrentes.
La función T(op1 , op2 ) toma dos operaciones: op1 es una operación entrante y op2 es una operación local
concurrente. T devuelve una operación transformada op0 tal que 1
aplicar op0 al por
originalmente estado
op1local
. Por
1
tiene
2, el yefecto
ejemplo,
“D”) op2 =previsto
si op1(insertar,
= (insertar,
0,
“A”) entonces la operación transformada es T(op1 , op2 ) = (insertar, 3, “D”) porque el la inserción original op1 en el índice
2 ahora debe realizarse en el índice 3 debido a la inserción concurrente en el índice 0. Por otro lado, T(op2 , op1 ) = op2
devuelve el op2 no modificado porque la inserción en el índice 0 no es afectado por una inserción concurrente posterior
en el documento.

La función de transformación se vuelve más complicada cuando se tienen en cuenta las eliminaciones, el formateo,
etc., y omitiremos los detalles. Sin embargo, este enfoque se utiliza en la práctica: por ejemplo, el algoritmo de resolución
de conflictos en Google Docs utiliza un enfoque de transformación operativa basado en el sistema de investigación Júpiter
de Xerox PARC [Nichols et al., 1995]. Una limitación de este enfoque es que requiere la comunicación entre los usuarios
para usar la transmisión de orden total, lo que requiere el uso de un nodo líder designado para secuenciar las
actualizaciones, o un algoritmo de consenso como en la lección 6.
Una alternativa a la transformación operativa, que evita la necesidad de una transmisión de orden total, es usar un
CRDT para la edición de texto. En lugar de identificar posiciones en el texto mediante índices y, por lo tanto, necesitar
una transformación operativa, los CRDT de edición de texto funcionan adjuntando un identificador único a cada carácter.
Estos identificadores permanecen sin cambios, incluso si se insertan o eliminan los caracteres circundantes.

Se han propuesto varias construcciones para estos identificadores únicos, una de las cuales se ilustra en la diapositiva
151. Aquí, a cada carácter se le asigna un número racional i ÿ Q con 0 < i < 1, donde 0 representa el comienzo del
documento, 1 es el final y los números intermedios identifican los caracteres del documento en orden ascendente.
También usamos el símbolo ` para representar el comienzo del documento y a para representar el final; estos símbolos
son parte del estado interno del algoritmo, no visibles para el usuario.
Cuando queremos insertar un nuevo carácter entre dos caracteres adyacentes existentes con números de posición i
y j, podemos asignar a ese nuevo carácter un número de posición de i+j que siempre se 2,
encuentra
posición siempre
entre i y existe,
j. Esta siempre
nueva
que usemos aritmética de precisión arbitraria (los números de coma flotante tienen una precisión limitada, por lo que ya
no funcionarán una vez que los intervalos se vuelvan demasiado pequeños). Es posible que dos nodos diferentes generen
caracteres con el mismo número de posición si se insertan simultáneamente en la misma posición, por lo que podemos
usar el ID del nodo que generó un carácter para desempatar cualquier carácter que tenga el mismo número de posición.

Usando este enfoque, la resolución de conflictos se vuelve fácil: una inserción con un número de posición particular

80
Machine Translated by Google

simplemente se puede transmitir a otras réplicas, que luego agregan ese carácter a su conjunto de caracteres y ordenan por número de
posición para obtener el documento actual.

Edición de texto CRDT

usuario A usuario B

` BC un ` BC un
0,0 0,5 0,75 1,0 0,0 0,5 0,75 1,0

insertar (0.25, "A") insertar (0.875, "D")

`ABC `BCD a
0,0 0,25 0,5 0,75 1,0 0,0 0,5 0,75 0,875 1,0

(insertar, 0.25, “A”) (insertar, 0.875, “D”)

` ABCD un ` ABCD un
0,0 0,25 0,5 0,75 0,875 1,0 0,0 0,25 0,5 0,75 0,875 1,0

Diapositiva 151

Este algoritmo se muestra en las siguientes dos diapositivas. El estado de una réplica son los caracteres establecidos, que contienen
(posición, ID de nodo, carácter) se triplica.

Texto basado en operaciones CRDT (1/2)


function ElementAt(chars, index ) min = el
único triple (p, n, v) ÿ chars tal que n0 , v0) ÿ chars. p0 < p ÿ (p
, < n)}
0 0
@(pag 0
= pag ÿ norte
si el índice = 0 , entonces devuelve
min ; de lo contrario, devuelve ElementAt(chars \ {min}, index ÿ
1) end function

en la inicialización do
chars := {(0, null, `), (1, null, a)} end on

a pedido para leer el carácter en el índice index do let (p,


n, v) := ElementAt(chars, index + 1); volver v terminar en

a pedido para insertar el carácter v en el índice índice en el nodo nodeId do let (p1,
n1, v1) := ElementAt(chars, index ) let (p2, n2, v2) := ElementAt(chars, index +
1) broadcast (insertar ,(p1 + p2)/2, nodeId, v) por final de transmisión causal
el

Diapositiva 152

Texto basado en operaciones CRDT (2/2)

al entregar (insertar, p, n, v) por transmisión causal do chars :=


chars ÿ {(p, n, v)} end on

a pedido para eliminar el carácter en el índice de índice hacer


let (p, n, v) := ElementAt(chars, index + 1) broadcast
(delete, p, n) by causal broadcast end on

al entregar (eliminar, p, n) por transmisión causal do chars :=


0 0 0
, | ¬(p = n)} terminar en
{(p n0 , v0) ÿ chars = pag ÿ norte

Uso transmisión causal para que la inserción de un carácter sea


entregado antes de su eliminación
I Inserción y borrado de diferentes caracteres conmutan

Diapositiva 153

81
Machine Translated by Google

La función ElementAt itera sobre los elementos de chars en orden ascendente del número de posición.
Lo hace encontrando primero el elemento mínimo, es decir, el elemento para el cual no existe otro elemento con un número de
posición más bajo. Si hay varios elementos con el mismo número de posición, se elige el elemento con el ID de nodo más bajo.
Si index = 0, devolvemos este elemento mínimo; de lo contrario, eliminamos el elemento mínimo, disminuimos el índice y
repetimos. (Este es un algoritmo bastante lento; una implementación real haría un esfuerzo por ser más eficiente).

Los caracteres de una réplica se inicializan con elementos para ` y a. Para obtener el carácter en un índice particular,
usamos el ElementAt que acabamos de definir, agregando 1 al índice para omitir el primer elemento en caracteres, que siempre
es (0, nulo, `).
Para insertar un carácter en una posición particular, obtenemos los números de posición p1 y p2 del predecesor y sucesor
inmediatos, y luego calculamos el nuevo número de posición como (p1+p2)/2. Difundimos entonces esta operación por
transmisión causal. En la entrega de un mensaje de inserción, simplemente agregamos el triple a chars.
Para eliminar un carácter en una posición particular, usamos ElementAt, agregando 1 para omitir ` como antes, para
encontrar el número de posición y el ID de nodo de ese carácter. Luego, transmitimos esta información, que identifica de manera
única a un carácter en particular, mediante una transmisión causal como un mensaje de eliminación. En la entrega de un
mensaje de eliminación, una réplica elimina el elemento en caracteres que coincida tanto con el número de posición como con
el ID de nodo en el mensaje, si existe.
La razón para usar la transmisión causal (en lugar de solo la transmisión confiable) en este algoritmo es garantizar que si
se elimina un carácter, todas las réplicas procesan la inserción del carácter antes de procesar la eliminación. Esta restricción es
necesaria porque las operaciones para insertar y eliminar el mismo carácter no conmutan. Sin embargo, las inserciones y
eliminaciones de diferentes caracteres conmutan, lo que permite que este algoritmo asegure la convergencia y una fuerte
consistencia eventual.

8.2 La llave inglesa de Google


A pesar de tener "fuerte" en el nombre, la consistencia final fuerte es una propiedad de consistencia bastante débil: por ejemplo,
al leer un valor, no hay garantía de que la operación devuelva el valor más actualizado, porque puede llevar algún tiempo. para
que las actualizaciones se propaguen de una réplica a otra. Por el contrario, examinemos ahora un sistema diferente que ofrece
garantías de coherencia mucho más sólidas. Como siempre, estas garantías tienen un costo, pero para algunas aplicaciones
esta es la elección correcta.
La base de datos Spanner desarrollada por Google [Corbett et al., 2012] es un ejemplo de un sistema que proporciona las
garantías de consistencia más sólidas posibles: transacciones con aislamiento serializable y compromiso atómico, y lecturas y
escrituras linealizables. Spanner logra esas propiedades sin dejar de ser muy escalable, admite grandes volúmenes de datos,
un gran rendimiento de transacciones y permite que los datos se distribuyan en todo el mundo. Las réplicas de Spanner están
diseñadas para ubicarse en centros de datos (a diferencia del software de colaboración de la última sección, donde un dispositivo
de usuario final puede ser una réplica).

Comienzo del video sección


La llave inglesa de Google 8.2 (descarga mp4)
Un sistema de base de datos con millones de nodos, petabytes de datos,
distribuidos en centros de datos de todo el mundo

Propiedades de consistencia:
Aislamiento de transacciones serializable

I Linealizable lee y escribe

I Muchos fragmentos, cada uno con un subconjunto de los datos;


compromiso atómico de transacciones a través de fragmentos

Muchas técnicas estándar:

I Replicación de máquinas de estado (Paxos) dentro de un fragmento

Bloqueo de dos fases para serialización

I Compromiso de dos fases para atomicidad entre fragmentos

Lo interesante: ¡las transacciones de solo lectura no requieren bloqueos!


Diapositiva 154

Muchas de las técnicas utilizadas por Spanner son muy convencionales y las hemos visto anteriormente en este curso:
utiliza el algoritmo de consenso de Paxos para la replicación de máquinas de estado, bloqueo de dos fases para garantizar el
aislamiento serializable entre transacciones y compromiso de dos fases para garantizar compromiso atómico. Se requiere
mucho esfuerzo de ingeniería para hacer que estos algoritmos funcionen bien en la práctica, pero a nivel arquitectónico,

82
Machine Translated by Google

estas opciones bien establecidas no son sorprendentes.


Sin embargo, Spanner es famoso por un aspecto muy inusual de su diseño, a saber, el uso de relojes atómicos.
Este es el aspecto en el que nos centraremos en este apartado. El motivo de este uso de relojes es permitir transacciones de
solo lectura sin bloqueo.
Algunas transacciones de solo lectura necesitan leer una gran cantidad de objetos en una base de datos; por ejemplo, un
proceso de respaldo o auditoría necesita esencialmente leer toda la base de datos. Realizar este tipo de transacciones con
bloqueo de dos fases sería extremadamente disruptivo, ya que la copia de seguridad puede llevar mucho tiempo y el bloqueo
de lectura en toda la base de datos evitaría que los clientes escriban en la base de datos durante la copia de seguridad. Por
esta razón, es muy importante que las transacciones grandes de solo lectura puedan ejecutarse en segundo plano, sin
necesidad de bloqueos y, por lo tanto, sin interferir con las transacciones simultáneas de lectura y escritura.

Spanner evita los bloqueos en las transacciones de solo lectura al permitir que una transacción lea desde una
instantánea consistente de la base de datos: es decir, la transacción observa toda la base de datos tal como era en un
solo momento, incluso si algunas partes de la base de datos son subsecuentemente actualizado por otras transacciones
mientras se ejecuta la transacción de solo lectura. La palabra "consistente" en el contexto de una instantánea significa
que es consistente con la causalidad: si la transacción T1 ocurrió antes de la transacción T2, y si la instantánea contiene
los efectos de T2, entonces también debe contener los efectos de T1.

Instantáneas consistentes
Una transacción de solo lectura observa una instantánea consistente: si T1 ÿ T2

(por ejemplo, T2 lee los datos escritos por T1). . .

I La instantánea que refleja las escrituras de T2 también refleja las escrituras de T1

I La instantánea que no refleja las escrituras de T1 tampoco refleja las escrituras


de T2

En otras palabras, la instantánea es consistente con la causalidad .

Incluso si la transacción de solo lectura se ejecuta durante mucho tiempo

Enfoque: control de concurrencia de múltiples versiones (MVCC)

I Cada transacción de lectura y escritura Tw tiene una marca de tiempo de compromiso tw

I Cada valor está etiquetado con la marca de tiempo tw de la transacción que lo

escribió (sin sobrescribir el valor anterior)

I La transacción de solo lectura Tr tiene una marca de tiempo de instantánea tr

I Tr ignora los valores con tw > tr; observa el valor más reciente con tw ÿ tr

Diapositiva 155

La implementación de instantáneas de Spanner utiliza el control de concurrencia de múltiples versiones (MVCC), una forma
particular de control de concurrencia optimista similar a lo que se discutió en la primera mitad de este curso.
MVCC se basa en asignar una marca de tiempo de compromiso a cada transacción; cada objeto de datos está etiquetado con
la marca de tiempo de la transacción que lo escribió. Cuando se actualiza un objeto, no solo lo sobrescribimos, sino que
almacenamos varias versiones antiguas (cada una etiquetada con una marca de tiempo) además de la última versión. La
instantánea de una transacción de solo lectura también se define mediante una marca de tiempo: es decir, la transacción lee la
versión más reciente de cada objeto que precede a la marca de tiempo de la instantánea e ignora cualquier versión del objeto
cuya marca de tiempo sea mayor que la de la instantánea. Muchas otras bases de datos también usan MVCC, pero lo que hace
que Spanner sea especial es la forma en que asigna marcas de tiempo a las transacciones.
Para garantizar que las instantáneas sean consistentes con la causalidad, el algoritmo MVCC requiere que si la transacción
T1 ocurrió antes de la transacción T2, entonces la marca de tiempo de confirmación de T1 debe ser menor que la de T2. Sin
embargo, recuerde de la diapositiva 61 que las marcas de tiempo de los relojes físicos no necesariamente satisfacen esta
propiedad. Por lo tanto, nuestra respuesta natural debería ser usar marcas de tiempo lógicas, como las marcas de tiempo de
Lamport, en su lugar (Sección 4.1).
Desafortunadamente, las marcas de tiempo lógicas también tienen problemas. Considere el ejemplo de la diapositiva 156,
donde un usuario observa los resultados de la transacción T1 y luego realiza alguna acción, que se ejecuta en una transacción
T2. Esto significa que tenemos una dependencia en tiempo real (Sección 7.2) entre las transacciones: T1 debe haber terminado
antes de que comience T2 y, por lo tanto, esperamos que T2 tenga una marca de tiempo mayor que T1.
Sin embargo, las marcas de tiempo de Lamport no pueden garantizar necesariamente esta propiedad de ordenación: recuerde
que funcionan adjuntando una marca de tiempo a cada mensaje que se comunica a través de la red y tomando el máximo cada
vez que se recibe dicho mensaje. Sin embargo, en el ejemplo de la diapositiva 156, es posible que no se envíe ningún mensaje
desde la réplica A, donde se ejecuta T1 , a la réplica B, donde se ejecuta T2 . En cambio, la comunicación se realiza a través
de un usuario, y no podemos esperar que un ser humano incluya una marca de tiempo formada correctamente.

83
Machine Translated by Google

en cada acción que realizan. Sin un mecanismo confiable para propagar la marca de tiempo en cada paso de la comunicación,
las marcas de tiempo lógicas no pueden proporcionar la garantía de pedido que necesitamos.

Obtención de marcas de tiempo de confirmación


Hay que asegurarse de que siempre que T1 ÿ T2 tengamos t1 < t2.
I Los relojes físicos pueden ser inconsistentes con la causalidad
I ¿Podemos usar relojes Lamport en su lugar?
I Problema: la capacidad de linealización depende del orden en tiempo real, ¡y
es posible que los relojes lógicos no reflejen esto!

A B

T1

resultados

acción

T2

Diapositiva 156

Otra opción para generar marcas de tiempo lógicas sería tener un único servidor designado que firme las marcas de
tiempo de las transacciones. Sin embargo, este enfoque falla en una base de datos distribuida globalmente, ya que ese servidor
se convertiría en un único punto de falla y un cuello de botella en el rendimiento. Además, si las transacciones que se ejecutan
en un continente diferente al del servidor de marca de tiempo deben esperar una respuesta, el inevitable tiempo de ida y vuelta
debido a los retrasos a la velocidad de la luz haría que las transacciones fueran lentas de ejecutar. Se requiere un enfoque
menos centralizado para las marcas de tiempo.
Aquí es donde entra en juego el mecanismo TrueTime de Spanner. TrueTime es un sistema de relojes físicos que no
devuelve una sola marca de tiempo, sino un intervalo de incertidumbre. Aunque no podemos garantizar relojes perfectamente
sincronizados en sistemas prácticos (sección 3.2), podemos realizar un seguimiento de los errores que pueden introducirse en
varios puntos del sistema. Para los relojes atómicos, los límites de error son informados por el fabricante. Para los receptores
GPS, el error depende de la calidad de las señales de los satélites actualmente dentro del alcance. El error que se presenta al
sincronizar relojes en una red depende del tiempo de ida y vuelta (ejercicio 5). El error de un reloj de cuarzo depende de su
tasa de desviación y del tiempo transcurrido desde su última sincronización con un reloj más preciso.

Cuando le pide a TrueTime la marca de tiempo actual, devuelve un intervalo [más temprano, más reciente]. El sistema no
conoce la verdadera marca de tiempo física actual treal, pero puede garantizar que tearliest ÿ treal ÿ tlatest con una probabilidad
muy alta al tener en cuenta todas las fuentes de error anteriores.

TrueTime: incertidumbre explícita del reloj físico El reloj TrueTime de Spanner devuelve

[tearliest, tlatest].
La verdadera marca de tiempo física debe estar dentro de ese rango.
Al confirmar, espere la incertidumbre ÿi = ti,latest ÿ ti,earliest.

tiempo
A fisico B

T1 t1, más temprano

compromiso requerido
ÿ1
ÿ1
t1, último
compromiso hecho tiempo real

t2, más temprano T2


compromiso requerido
ÿ2
ÿ2
t2, último
compromiso hecho

Diapositiva 157

Cuando la transacción Ti desea confirmar en Spanner, obtiene un intervalo de marca de tiempo [ti,earliest, ti,latest] de
TrueTime y asigna ti,latest para que sea la marca de tiempo de confirmación de Ti . Sin embargo, antes de que la transacción
realmente se comprometa y libere sus bloqueos, primero se detiene y espera una duración igual al período de incertidumbre del reloj.

84
Machine Translated by Google

ÿi = ti,último ÿ ti,primero. Solo después de que haya transcurrido este tiempo de espera, la transacción se confirma y sus escrituras
se vuelven visibles para otras transacciones.
Aunque no tenemos relojes perfectamente sincronizados y, por lo tanto, un nodo no puede saber la hora física exacta de un evento,
este algoritmo asegura que la marca de tiempo de una transacción sea menor que la hora física real en el momento en que se confirma la
transacción. Por lo tanto, si T2 comienza más tarde en tiempo real que T1, la marca de tiempo más temprana posible que podría asignarse
a T2 debe ser mayor que la marca de tiempo de T1.
Dicho de otra manera, la espera garantiza que los intervalos de marca de tiempo de T1 y T2 no se superpongan, incluso si las transacciones
se ejecutan en nodos diferentes sin comunicación entre las dos transacciones.
Dado que cada transacción tiene que esperar a que transcurra el intervalo de incertidumbre, el desafío ahora es mantener ese intervalo
de incertidumbre lo más pequeño posible para que las transacciones sigan siendo rápidas. Google logra esto instalando relojes atómicos y
receptores GPS en cada centro de datos y sincronizando el reloj de cuarzo de cada nodo con un servidor horario en el centro de datos local
cada 30 segundos. En el centro de datos local, los viajes de ida y vuelta suelen ser inferiores a 1 ms, por lo que el error de reloj introducido
por la latencia de la red es bastante pequeño. Si aumenta la latencia de la red, por ejemplo, debido a la congestión, el intervalo de
incertidumbre de TrueTime crece en consecuencia para dar cuenta del aumento del error.

Determinación de la incertidumbre del reloj en TrueTime


Servidores de reloj con reloj atómico o receptor GPS en cada centro de
datos; los servidores informan sobre la incertidumbre de su reloj.
Cada nodo sincroniza su reloj de cuarzo con un servidor cada 30 segundos.
Entre sincronizaciones, suponga una deriva en el peor de los casos de 200 ppm.

incertidumbre del reloj local [ms]

2
tiempo
incertidumbre del servidor + tiempo de ida y vuelta al servidor del reloj
0
0 10 20 30 40 50 60 70 80 90

sincronizar con el servidor del reloj


Diapositiva 158

Entre las sincronizaciones periódicas del reloj cada 30 segundos, el reloj de un nodo está determinado únicamente por su oscilador de
cuarzo local. El error introducido aquí depende de la tasa de deriva del cuarzo. Para estar seguro, Google supone una tasa de deriva
máxima de 200 ppm, que es significativamente más alta que la deriva observada en condiciones normales de funcionamiento (Diapositiva
45). Además, Google monitorea la deriva de cada nodo y alerta a los administradores sobre cualquier valor atípico.

¿Es la tasa de deriva de 200 ppm una suposición segura? Según el artículo de Spanner: “Las estadísticas de nuestras máquinas
muestran que las CPU defectuosas son 6 veces más probables que los relojes defectuosos. Es decir, los problemas de reloj son
extremadamente poco frecuentes, en comparación con problemas de hardware mucho más graves. Como resultado, creemos que la
implementación de TrueTime es tan confiable como cualquier otra pieza de software de la que depende Spanner”. [Corbett et al., 2012].

Si asumimos una deriva de cuarzo de 200 ppm y han pasado 30 segundos desde la última sincronización del reloj, esto implica una
incertidumbre de reloj de 6 ms debido a la deriva de cuarzo (además de cualquier incertidumbre de la latencia de la red, el receptor GPS y
los relojes atómicos) . El resultado es un intervalo de incertidumbre que crece gradualmente con el tiempo transcurrido desde la última
sincronización del reloj, hasta aproximadamente 7 ms, y que se restablece a aproximadamente 1 ms (tiempo de ida y vuelta + incertidumbre
del servidor del reloj) en cada sincronización del reloj, como se muestra en la figura. Diapositiva 158.
El intervalo de incertidumbre medio es, por tanto, de aproximadamente 4 ms en condiciones normales de funcionamiento, y estos 4 ms
son, por tanto, el tiempo medio que debe esperar una transacción antes de poder comprometerse. Esto es mucho más rápido de lo que
podrían ser las transacciones si tuvieran que esperar un viaje de ida y vuelta de la red intercontinental (que tomaría del orden de 100 ms o
más).
Para resumir: a través de una contabilidad cuidadosa de la incertidumbre, TrueTime proporciona límites superiores e inferiores en el
tiempo físico actual; a través de relojes de alta precisión mantiene pequeño el intervalo de incertidumbre; al esperar a que pase la
incertidumbre, Intervalo Spanner asegura que las marcas de tiempo sean consistentes con la causalidad; y al usar esas marcas de tiempo
para MVCC, Spanner proporciona transacciones serializables sin necesidad de bloqueos para transacciones de solo lectura. Este enfoque
mantiene las transacciones rápidas, sin imponer ningún requisito a los clientes para propagar marcas de tiempo lógicas.

85
Machine Translated by Google

¡Eso es todo amigos!

¿Alguna pregunta? ¡Envíe un correo electrónico a mk428@cst.cam.ac.uk!

Resumen:
I Los sistemas distribuidos están en todas
partes I Los usa todos los días: por ejemplo,
aplicaciones web I Objetivos clave: disponibilidad,
escalabilidad, rendimiento I Problemas clave: simultaneidad, fallas,
latencia ilimitada I Abstracciones clave: replicación, transmisión,
consenso I Nadie de manera correcta , solo compensaciones

Diapositiva 159

Esto nos lleva al final del curso sobre Sistemas Concurrentes y Distribuidos. Partimos de una premisa simple: cuando envías un mensaje
por la red y no obtienes respuesta, no sabes lo que pasó. Tal vez el mensaje se perdió, o la respuesta se perdió, o el mensaje se retrasó, o
el nodo remoto falló, y no podemos distinguir entre estos tipos de fallas.

Los sistemas distribuidos son fascinantes porque tenemos que trabajar con conocimientos parciales y verdades inciertas. Nunca tenemos
certeza sobre el estado del sistema, porque para cuando nos enteramos de algo, ese estado ya puede estar desactualizado. ¡De esta manera
se parece más a la vida real que a la mayoría de las computadoras! En la vida real, a menudo necesitas tomar decisiones con información
incompleta.
Pero los sistemas distribuidos también son inmensamente prácticos: todos los sitios web y la mayoría de las aplicaciones son sistemas
distribuidos, y los servidores y las bases de datos que subyacen a la mayoría de los sitios web son, a su vez, sistemas distribuidos adicionales.
Después de graduarse, muchos de ustedes terminarán trabajando en dichos sistemas. Con suerte, las ideas de este curso le han brindado
una base sólida para que pueda ir y hacer que esos sistemas sean confiables y comprensibles.

Referencias
Steven L Allen. ¡Los aviones se estrellarán! Cosas que los segundos intercalares no causaron, y causaron, 2013. URL http://
www.hanksville.org/futurofutc/preprints/files/2 AAS%2013-502 Allen.pdf.

Hagit Attiya, Amotz Bar-Noy y Danny Dolev. Compartiendo memoria de manera robusta en sistemas de paso de mensajes. Journal of the
ACM, 42(1):124–142, enero de 1995. doi:10.1145/200836.200869. URL http://www.cse.huji.ac.il/course/2004/dist/p124-attiya.pdf .

Peter Bailis y Kyle Kingsbury. La red es confiable. Cola ACM, 12(7), 2014. doi:10.1145/2639988.2639988. URL
https://queue.acm.org/detail.cfm?id=2655736.

Tushar Deepak Chandra y Sam Toueg. Detectores de fallas poco confiables para sistemas distribuidos confiables. Journal of the ACM,
43(2):225–267, marzo de 1996. doi:10.1145/226643.226647. URL http://courses.csail.mit.edu/6.852/08/papers/CT96-JACM.pdf .

James C. Corbett, Jeffrey Dean, Michael Epstein, Andrew Fikes, Christopher Frost, JJ Furman, Sanjay Ghemawat, Andrey Gubarev,
Christopher Heiser, Peter Hochschild, Wilson C. Hsieh, Sebastian Kanthak, Eugene Kogan, Hongyi Li, Alexander Lloyd, Sergey Melnik,
David Mwaura, David Nagle, Sean Quinlan, Rajesh Rao, Lindsay Rolig, Yasushi Saito, Michal Szymaniak, Christopher Taylor, Ruth
Wang y Dale Woodford. Spanner: la base de datos distribuida globalmente de Google. En el 10º Simposio USENIX sobre Diseño e
Implementación de Sistemas Operativos, OSDI 2012, octubre de 2012.
URL https://www.usenix.org/conference/osdi12/technical-sessions/presentation/corbett.

Giuseppe DeCandia, Deniz Hastorun, Madan Jampani, Gunavardhan Kakulapati, Avinash Lakshman, Alex Pilchin, Swaminathan
Sivasubramanian, Peter Vosshall y Werner Vogels. Dynamo: la tienda clave-valor de alta disponibilidad de Amazon. ACM SIGOPS
Operating Systems Review, 41 (6): 205–220, diciembre de 2007. doi: 10.1145 / 1323293.1294281. URL http://www.allthingsdistributed.com/
files/amazon-dynamo-sosp2007.pdf.

Cynthia Dwork, Nancy A. Lynch y Larry Stockmeyer. Consenso en presencia de sincronía parcial. Journal of the ACM, 35(2):288–323,
abril de 1988. doi:10.1145/42282.42283. URL http://www.net.t-labs.tu-berlin.de/ÿpetr/ADC 07/documentos/DLS88.pdf.

Roy Thomas Fielding. Estilos arquitectónicos y diseño de arquitecturas de software basadas en red. tesis doctoral, Universidad
de California, Irvine, 2000. URL https://www.ics.uci.edu/ÿfielding/pubs/dissertation/top.htm.

86
Machine Translated by Google

Michael J. Fischer, Nancy A. Lynch y Michael S. Paterson. Imposibilidad de consenso distribuido con un proceso defectuoso.
Journal of the ACM, 32(2):374–382, abril de 1985. doi:10.1145/3149.214121. URL https://groups.csail.mit.edu/tds/papers/ Lynch/jacm85.pdf.

Seth Gilbert y Nancy Lynch. La conjetura de Brewer y la viabilidad de servicios web coherentes, disponibles y tolerantes a particiones. ACM
SIGACT News, 33(2):51–59, junio de 2002. doi:10.1145/564585.564601. URL https://www.comp.nus.edu.sg/ ÿgilbert/pubs/BrewersConjecture-
SigAct.pdf.

Jim Grey y Leslie Lamport. Consenso sobre el compromiso de la transacción. ACM Transactions on Database Systems, 31(1):133–160, marzo
de 2006. doi:10.1145/1132863.1132867. URL http://db.cs.berkeley.edu/cs286/papers/paxoscommit-tods2006.pdf.

Jim N. Gray. Notas sobre sistemas operativos de bases de datos. En R. Bayer, RM Graham y G. Seegmüller, editores, Op erating Systems,
volumen 60 de LNCS, páginas 393–481. Springer, 1978. doi:10.1007/3-540-08755-9 9. URL http: //jimgray.azurewebsites.net/papers/
dbos.pdf.

Mauricio Herlihy. Sincronización sin esperas. Transacciones de ACM en lenguajes y sistemas de programación, 13(1):124–149,
Enero de 1991. doi:10.1145/114005.102808. URL http://cs.brown.edu/ÿmph/Herlihy91/p124-herlihy.pdf.

Maurice P. Herlihy y Jeannette M. Wing. Linealizabilidad: una condición de corrección para objetos concurrentes. ACM Transactions on
Programming Languages and Systems, 12(3):463–492, julio de 1990. doi:10.1145/78969.78972. URL http://cs.brown.edu/ÿmph/HerlihyW90/
p463-herlihy.pdf.

Heidi Howard y Richard Mortier. Paxos vs Raft: ¿hemos llegado a un consenso sobre el consenso distribuido? En 7th Workshop on Principles
and Practice of Consistency for Distributed Data, PaPoC, abril de 2020. doi:10.1145/3380787.3393681. URL https://arxiv.org/abs/2004.05074.

Marcos Imbriaco. Tiempo de inactividad el sábado pasado, diciembre de 2012. URL https://github.com/blog/1364-downtime-last-saturday.

Martín Kleppmann. Una crítica del teorema CAP. arXiv, septiembre de 2015. URL http://arxiv.org/abs/1509.05393.

Sandeep S. Kulkarni, Murat Demirbas, Deepak Madappa, Bharadwaj Avva y Marcelo Leone. Relojes físicos lógicos.
En 18th International Conference on Principles of Distributed Systems (OPODIS), volumen 8878 de LNCS, páginas 17–32.
Springer, diciembre de 2014. doi:10.1007/978-3-319-14472-6 2. URL https://cse.buffalo.edu/ÿdemirbas/publications/hlc.pdf.

Leslie Lamport. Tiempo, relojes y el orden de los eventos en un sistema distribuido. Comunicaciones de la ACM, 21(7): 558–565, 1978.
doi:10.1145/359545.359563. URL http://research.microsoft.com/en-US/um/people/Lamport/pubs/time clocks.pdf.

Leslie Lamport. El parlamento a tiempo parcial. ACM Transactions on Computer Systems, 16(2):133–169, mayo de 1998.
doi:10.1145/279227.279229. URL http://research.microsoft.com/en-us/um/people/lamport/pubs/lamport-paxos.pdf.

Leslie Lamport, Robert Shostak y Marshall Pease. El problema de los generales bizantinos. ACM Transactions on Programming Languages
and Systems, 4(3):382–401, 1982. doi:10.1145/357172.357176. URL http://research.microsoft.com/en-us/um/ people/lamport/pubs/byz.pdf.

Nelson Minar. El segundo intercalar colapsa la mitad de Internet, julio de 2012. URL http://www.somebits.com/weblog/tech/bad/leap
segundo-2012.html.

David A. Nichols, Pavel Curtis, Michael Dixon y John Lamping. Ventanas de alta latencia y bajo ancho de banda en el sistema de colaboración
de Júpiter. En el 8º Simposio Anual de ACM sobre Interfaz de Usuario y Tecnología de Software, UIST 1995, páginas 111–120, noviembre
de 1995. doi:10.1145/215585.215706. URL http://www.lively-kernel.org/repository/webwerkstatt/projects/Collaboration/paper/Jupiter.pdf .

Diego Ongaro y John Ousterhout. En busca de un algoritmo de consenso comprensible. En Conferencia Técnica Anual USENIX, ATC. USENIX,
junio de 2014. URL https://www.usenix.org/conference/atc14/technical-sessions/presentation/
óngaro

Nuno Pregui¸ca, Carlos Baquero, Paulo Sérgio Almeida, Victor Fonte, and Ricardo Gon¸calves. Vectores de versión punteada:
Relojes lógicos para replicación optimista, noviembre de 2010. URL http://arxiv.org/pdf/1011.5808v1.pdf.

Marc Shapiro, Nuno Pregui¸ca, Carlos Baquero, and Marek Zawirski. Tipos de datos replicados sin conflictos. En 13.° Simposio internacional
sobre estabilización, seguridad y protección de sistemas distribuidos, SSS, páginas 386–400, octubre de 2011. doi:10.1007/978-3-642-24550-3
29. URL https://pages.lip6.fr/Marek.Zawirski/papers/RR-7687.pdf.

Martín Thompson. Recolección de basura de Java destilada, junio de 2013. URL https://www.infoq.com/articles/Java Garbage
Colección Destilados/.

Pájaros Werner. Eventualmente consistente. Comunicaciones de la ACM, 52(1):40–44, enero de 2009.


doi:10.1145/1435417.1435432. URL http://cacm.acm.org/magazines/2009/1/15666-eventually-consistent/fulltext.

Jim Waldo, Geoff Wyant, Ann Wollrath y Sam Kendall. Una nota sobre computación distribuida. Informe Técnico TR-94-29,
Laboratorios Sun Microsystems, 1994. URL http://m.mirror.facebook.net/kde/devel/smli tr-94-29.pdf.

87

También podría gustarte