Presentación y ejecución del sistema
PedidosRust es una nueva aplicación para conectar restaurantes, repartidores y comensales. Gracias a su innovadora implementación distribuida, permitirá reducir los costos y apuntar a ser líder en el mercado.
Para ejecutar el sistema ejecutar los comandos indicados aqui.
Créditos
| Nombre(s) | Apellido(s) | Padrón | GitHub |
|---|---|---|---|
| Nilo Josue | Martel Valentín | 110696 | josValentin-fiuba |
| Andrea Emperatriz | Figueroa Rodriguez | 110450 | AndreaFigueroaR |
| Lucas | Jang | 109151 | lucasjang01 |
| Leticia Antuaned | Figueroa Rodriguez | 110510 | leticiafrR |
La arquitectura del sistema contará de 5 tipos de procesos con funciones distintas: Comensal, Restaurant, Interfaz Repartidores, Repartidor, Gateway.
La cooperación y comunicación de estos 5 procesos para lograr el correcto funcionamiento del sistema Pedidos Rust determina la arquitectura distribuida del mismo.
La convivencia de estos procesos en el sistema es tal que:
-
Cada proceso
Comensaljuega un rol de cliente para con las inatancias de:Gatewayy deRestaurant. Primero se contacta con elGatewayLider para iniciar la transaccción y posterior a esta operación se inicia el contacto elRestaurantde su elección para acceder al servicio que este ofrece. Además, de haberse realizado correctamente un pedido al restaurant deseado se abre un socket UDP para poder eventualmente comunicarse con el repartidor que tiene su pedido. -
El Restaurante le ofrece el servicio al Comensal: tomará o no su pedido, en caso de tomarlo lo cocinará, lo despachará a un delivery y le irá comunicando el estado de dicho pedido al Comensal.
-
A su vez el
Restaurantcontactará a algún procesoInterfaz Repartidores, eligiendo cual de forma pseudo aleatoria, para acceder al servicio que estos ofrecen. -
Las instancias de
Interfaz Repartidoresofrecen a los retaurants el siguiente servicio:- Hacer llegar el pedido disponible a Repartidores cercanos mediante su red de
Repartidores. - Llevar a cabo el proceso de selección (entre los repartidores interesados)
- Darle los datos necesarios al Repartidor seleccionado para que se contacte con el restaurant.
- Hacer llegar el pedido disponible a Repartidores cercanos mediante su red de
-
El repartidor usa el servicio de alguna
Interfaz Repartidorespara acceder a las publicaciones de pedidos disponibles, decidir si acepta o no los pedidos que le lleguen para poder ser partícipe del proceso de elección, y al saber que es el repartidor elegido para llevar el pedido X se contacta con el restaurant para indicarle que está en camino, así como para comunicar cuando llegue al restaurant. Para concluir con la entrega del pedido se dirigirá a la dirección del comensal y se comunicará con el mismo cuando llegue. -
Una vez el comensal haya recibido su pedido este se comunica con el
Gatewaypara efectuar el pago o en caso de la ausencia de comunicación por parte del Repartidor se abortará la transacción.
ContactoRestaurante
- Se comunica con el restaurante por TCP
- Envía solicitudes de pedido al restaurante
- Recibe estados del pedido
- Maneja confirmaciones y cancelaciones
ContactoRider
- Se comunica con el repartidor por UDP
- Recibe actualizaciones de ubicación del repartidor
- Gestiona notificaciones de llegada
ContactoGateway
- Se comunica con el Gateway de pagos por TCP
- Procesa autorizaciones de pago
- Recibe confirmaciones de transacciones
- Maneja rechazos
Al iniciar la aplicación se lee un archivo inmutable que contiene las ubicaciones fisicas y las direcciones de todos los restaurantes. Se fija cuales son los restaurantes cercanos según la ubicación actual del comensal y hace un broadcast por UDP a todos ellos preguntándoles si están conectados, los que le manden un ACK confirmando que están conectados son los que va a considerar para hacer la elección. Cabe aclarar que si no contestan se reintentara un par de veces para verificar que efectivamente están desconectados y que no se haya perdido el paquete.
Una vez que tiene los restaurantes cercanos y conectados se le muestran al usuario y éste elige uno de ellos para realizar el pedido.
Primero se le pide al Gateway de pagos, a través de ContactoGateway, la autorización del pago y éste la puede aceptar o rechazar; si la rechaza se cancela el pedido pero si la acepta el actor ContactoRestaurante envía la solicitud del pedido al restaurante elegido. Desde ahí ContactoRestaurante va recibiendo el estado del pedido hasta que le llega que el repartidor recibió el pedido. Luego el repartidor en cuestión se comunica con el comensal, específicamente con el actor ContactoRider, para ir recibiendo el estado del repartidor. Una vez que ContactoRider recibe que el repartidor llegó a la ubicación del comensal y entrega el pedido, le informa a ContactoGateway que el pedido fue completado, y éste último le envía al Gateway de pagos que finalice la transacción.
ControlPedido
- Gestiona los mensajes entrantes(pedido) y salientes(actualizaciones del estado del pedido) para con el Cliente Comensal mediante una conexión TCP con el mismo.
- Envía mensaje al actor InterfazPedidos con los datos del comensal, pedido, etc. BackUpChecker
- Timer con determinada frecuencia para realizar un backup del estado actual de los pedidos del restaurant.
- Se comunica con la Interfaz Pedidos para indicarle que realice un bakcup en memria persistente del estado actual de los pedidos del restaurant. InterfazPedidos
- Interfaz central del restaurant accesible para aquellos actores que deban notificar cambios en el estado de pedidos existentes o solcitar la generaci´ón de un estado para un nuevo pedido.
- Cuando recibe un mensaje para registrar un nuevo pedido:
- Funciona como punto central donde se generan los IDs unívocos de los pedidos, se genera el estado del pedido identificado y se registra. Además, se mantiene un directorio que vincula el ID con el acceso (
addr) al actorControlPedido(para comunicar cambios en el estado al clentre remoto). - Finalmente despacha el pedido al actor
Tableropara que eventaualmente empiece su preparación en laCocina.
- Funciona como punto central donde se generan los IDs unívocos de los pedidos, se genera el estado del pedido identificado y se registra. Además, se mantiene un directorio que vincula el ID con el acceso (
- Recibe mensajes del
BackupCheckery los maneja volcando el estado actual de los pedidos en la interfaz en el archivo de caché (recuperabilidad). - Recibe actualizaciones del estado de un pedido por parte de la cocina (indicando que terminó su preparación), se registra el cambio de estado, se notifica la actualización al actor
ControlPedidocorrespondiente para su comunicación al cliente remoto y finalmente se le notifica al actorDifusorPedidosSalientespara la gestión de su envío. Tablero - Recepciona pedidos nuevos (ya registrados): pasándoselos ahora o eventualnmente a la cocina.
- Se encarga de mantener una cantidad acotada de pedidos siendo preparados en la cocina. De haber alcanzado el máximo el tablero gestona una cola de pedidos en espera de donde sacará pedidos para ser pasados a la cocina en cuanto haya disponibilidad. (Similar a un semáforo que regula el acceso a la cocina).
Cocina
- Entidad que recepciona una cantidad acotada de pedidos para simular por cada uno un tiempo de preparación.
- Posee acceso a la
InterfazPedidosy alTablero. De manera que, en cuanto finaliza la preparación de un pedido:- Se notifica el cambio de estado a la
InterfazPedidos. - Se le comunica el evento al
Tableroque posiblemenete tenga pedidos en espera, de modo que este pueda gestionar adecuadamente esta nueva disponibilidad en la cocina.
- Se notifica el cambio de estado a la
DifusorPedidosSalientes
- Entidad que gestiona la conexión TCP con un proceso
Interfaz Repartidorespara solicitar pertinenetemente la difusión-publicación de un nuevo pedido. Así como, de ser necesario, gestiona la reconexión con alguna replica de procesoInterfaz Repartidores. - Al recibir el mensaje de la
InterfazPedidosindicando que un pedido terminó de prepararse se arma un mensaje de aplicación con la información relativa al pedido, la ubicación del restaurant, la ubicación destino del comensal, y los datos de puerto al cual el repartidor que fuera a ser elegido debe enviar mensajes (tanto para con este restaurant como para con el comensal) y se lo pasa al procesoInterfaz Repartidores. - Además le indica al actor local
RecepcionRepartidoresque debe gestionar la recepción del mensaje del repartidor para el pedido X indicando que está en camino al restaurant, así como también detectar y reaccionar la ausencia de este mensaje. RecepcionRepartidores - Al recibir el mensaje (local) del actor
DifusorPedidosSalientesse dispara un timer para el tiempo máximo de espera de la llegada del mensaje del repartidor indicando que está en camino, y se agrega el ID del pedido a la lista de pedidos en espera de rider. - Recepciona mediante un socket UDP y gestiona los mensajes entrantes de repatidores que hayan sido seleccionados para llevar a cabo el delivery de alguno de los pedidos que publicamos y hayan indicado que están en camino: se aumenta el tiempo del timer para esperar el siguiente mensaje del repartidor que indique que llegó al restaurant.
- En caso de no llegar el mensaje de en camino del repartidor le indica al
DifusorPedidosSalientesque vuelva a solicitar un rider. - Recepciona el mensaje de llegué por el pedido X del repartidor y le indica a la interfaz de pedidos que actualice el estado del pedido (y se lo comunique al cliente remoto indirectamente).
-
Un conjunto de nodos (procesos) que :
- Cumplen el papel de una interfaz para contactar a los repartidores respecto a un pedido
- Implementa la estructura del sistema para llevar a cabo la toma de decisiones respecto a qué rider toma un pedido en particular
- Toma como criterio la cercanía física de los repartidores con los que se conecta para decidir a quiénes notificar de una orden
-
Procesos restaurantes
- Se conectan con un nodo en particular.
- Es capaz de contactar automáticamente a otro nodo en caso de que el actual se caiga.
- Modela la toma de decisiones del rider en el sistema
- Nuestro diseño asigna a cada repartidor el nodo al que se conectará (mediante TCP) siguiendo una distribución uniforme. Si una conexión no se puede establecer se intenta con el siguiente nodo. Si la conexión con un nodo se cae, se retoma con otro nodo disponible.
- Al lograr establecer una conexión con un nodo de la interfaz, el repartidor debe proveer de su ubicación física a esta.
La interfaz de repartidores recibe ofertas de pedidos de cualquier restaurante, a través de un mensaje UDP, donde el restaurante le provee:
- La ubicación fisica del local.
- La dirección IP del restaurante.
- Ubicación física del comensal.
- La dirección IP del comensal
Si el nodo ya tiene una votación activa (respecto a qué repartidor toma un pedido en particular):
- Se reponde al resaturante que su oferta se recepcionó
- Se encola dicjo pedido
Si el nodo no tiene una votación activa (que se le haya comunicado a este directamente desde un restaurante)
- Se responde al resaturante que su oferta se recepcionó.
- Se realiza broadcast a todos los demás nodos para que cada uno inicie una votación interna, indicandoles:
- ID del nodo que recibió el pedido.
- La oferta tal como se la pasó el restaurante.
- El nodo actual inicia su propia votación interna
-
El nodo notifica a sus repartidores que se encuentran en cercanía. Cuenta las respuestas negativas que obtiene, cuenta las respuestas positivas que obtiene. Cada que obtiene una respuesta positiva:
-
Verifica si es mejor que el timestamp más bajo de aceptación que se haya recibido
- Si la aceptación recibida tiene un timestamp mejor que el anterior entonces se actualiza el timestamp y se notifica al anterior repartidor que no obtuvo el pedido .
- Si la aceptación recibida tiene un timestamp que no mejora el anterior, se notifica al rider de la respuesta que no obtuvo el pedido.
-
Una vez conseguidas las respuestas a todos los mensajes enviados, el nodo actual realiza un broadcast comunicando:
- El ID del nodo que recepcionó el pedido
- El ID del nodo actual
- El menor timestamp de su votación interna.
-
Realizamos un derivado del algoritmo de Ricard-Agrawala, en donde el nodo que obtiene los
Okde todos los demás nodos, es el nodo ganador de la votación. Los demás nodos obtuvieron al menos una respuesta negativa. -
El nodo comunica al único rider al cual aún no le dió una respuesta si obtuvo o no el pedido. Si lo obtuvo tambien le proporciona la información de contacto del comensal y del cliente.
Nota: Del lado del nodo que se entera de una oferta de un pedido mediante un broadcast, el mensaje se interpreta como una votación interna que el nodo debe llevar a cabo, realiza el mismo proceso y distingue esta votación interna de una suya por medio del ID del nodo que recepcionó el pedido. Por el mimso detalle de que cada nodo solo puede tener una propia votación activa, aseguramos que en el peor de los casos, cada nodo tendrá N votaciones activas (N siendo la cantidad de nodos)
El repartidor que fue elegido para tomar un pedido, usa la información provista para:
- Comunicarse con el restaurante para indicar mediante UDP que se encuentra en camino al local
- Comunicarse con el restaurante para indicar mediante UDP que llegó al restaurante
- Comunicarse con el comensal para indicar que se encunetra llegó al domicilio.
Los actores que se encargan de llevar a cabo el procesamiento de cada pedido son:
Restaurantes- Representa el actor que posee el socket TCP con puerto público hacia los restaurantes
- Por medio del sus stream UDP recibe las ofertas de los pedidos
- Se encarga de mandar respuestas ACK a los restaurantes que enviaron sus ofertas de pedidos
- Cuando recibe una oferta, envia un mensaje al actor
InterfazPedidospara que procese este pedido.
InterfazPedidos- Representa el actor que gestiona que solo se realice una votación interna propia (hasta que termine y se pueda realizar la siguiente).
- Tiene una cola con las solicitudes de pedidos de restaurantes y una flag que indica si hay una propia votación interna. Cada que le llega un mensaje del actor
Restaurantesse fija la flag- Si la flag indica que hay una votación activa entonces se encola el pedido
- Caso contrario, se cambia la flag, y se le envía un mensaje al actor
AdministradorVotaciónActivapara que inicie una votación interna (esta se identifica por medio del ID del nodo que la recepcionó)
- Cuando
AdministradorVotacionesnActivale comunica que ya no hay una votación activa, procede a desencolar una para inciar una nueva.
AdministradorVotacionesnActiva- Tiene de stream un Socket UDP interno (deuso exclusivo entre los nodos del subgrafo completo) por el cual se envian mensajes de Broadcast y se reciben mensajes de tipo Broadcast.
- Tiene una lista de las addresses de los actores
RiderAgent - La address de
InterfazPedidos - Por cada votación interna que se esté llevando a cabo, tiene:
- Un contador de las respuestas negativas (no les interesa el pedido) que recibe
- Un contador de las respuestas de aceptación que recibe
- El timestamp y la address del
RiderAgentque mantiene el timestamp más pequeño. - Las respuestas de los demás nodos de mi timestamp represetantivo para esta votación.
- Para realizar una votación interna:
- Envía un mensaje a todos los
RiderAgentindicando la ubicación de la oferta - Cada que recibe una respuesta actualiza el contador pertinente. En caso de ser una aceptación, el mensaje contiene el timestamp, en base a este valor es que se actualiza la información del menor timestamp.
- Al terminar de recibir todas las respuestas de esta votación interna, se habrán indicado a todos los
RiderAgentque no obtuvieron el pedido, excepto del actor que se asocia al menor timestamp, este es el representante de este nodo en esta votación. Se realiza un llamado recursivo para realizar un broadcast. - Para realizar el broadcast usamos el socket UDP para comunicar nuestro timestamp de esta votación interna. Cuando recibe la respuesta de un nodo se actualiza cuanta respuestas se tiene.
- Si el nodo recibe todos los
OKde todos los demás nodos, entonces se le comunica alRiderAgentque le indique al repartidor que tiene el pedido. - En caso contrario, comunica al
RiderAgentque le indique al repartidor que no obtuvo el pedido.
- Si el nodo recibe todos los
- Al comunicar la respuesta al último repartidor que no tenia respuesta, se le comunica a
InterfazPedidosque ya no hay una votación activa
- Envía un mensaje a todos los
RiderAgent- Representa la conexión TCP con un repartidor.
- Posee la ubicación física de este repartidor.
- Puede recibir mensajes de actualización de ubicación, así como mensajes de acpetación de un pedido o rechazo de un pedido.
-
InterfazRepartidores- Mantiene un stream con la conexión TCP del nodo.
- Tiene el lado de escritura del socket TCP.
- Tiene la address del actor
Repartidor - Tiene la ubicación física del repartidor.
- Cuando se le indica que inicie la comunicación, este solicita conexión a la dirección indicada.
- Si logra establecer conexión:
- Setea su stream
- Actualiza su extremo de escritura
- Si no puede, le indica a
Repartidorque no pudo restablesca la conexión y se indicastop()porque pediró su conexión.
- Si logra establecer conexión:
- Cuando recibe una notificación del stream, le indica a
Repartidorque tome una decisión random (que modela la decisión del repartidor) - Cuando la conexión del stream falla, le indica al
Repartidorque recupere la conexión. - Puede comunicarle al otro extremo de la comunicación TCP que la ubicación física del cliente cambió (y cambiarla localmente) cuando el actor
Repartidorasí lo indique.
-
Repartidor- Tiene una lista con un orden aleatorio con las direcciones de los nodos a los que puede conectar. Esta lista la recorre circulamente intenado conectar con algún nodo, corta el recorrido a penas concretiza una conexión. La lista la levanta en base a un archivo inmutable que posee.
- Tiene la ubicación física del cliente que actualiza después de cada entrega (le indica a
InterfazRepartidoresque tambien la actualice) - Cuando le indican que restablesca la conexión, avanza en la lista de direcciones de los nodos y vuelve a instanciar otro
InterfazRepartidores, a quien le indica que establesca conexión con la dirección indicada.
La instancia de aplicación que se comunicará con los comensales será el líder de un conjunto de nodos que actuarán como registro y réplica de los pagos autorizados por los mismos.
ContactoCliente
- Se comunica con el comensal por TCP
- Recibe solicitud de authorización de pagos
- Envía la aceptación o rechazo de tarjeta.
Gateway
- Mantiene el registro de pagos autorizados en proceso.
- Recibe actualizaciones de registro de pagos.
ReplicaGateway
- Registra pagos autorizados en proceso en otros Gateways.
- Recibe actualizaciones del registro de pagos en proceso del líder.
De no existir un líder designado o detectarse la caída del mismo, se procede a la elección de uno nuevo usando el algoritmo Ring. Con el líder establecido, se esperan conexiones de comensales para registrar autorizaciones de pago a travéz del líder. Cada autorización y finalización de pago es transmitida al Gateway líder, y éste retransmite estas actualizaciones a los demás gateways del conjunto, generando respaldo ante caída.
Diseñamos el sistema como una transacción, basándonos en Two-Phase Commit (2PC). El mismo requiere de la participación de las instancias de todas aplicaciones.
- Comensal
- Gateway de pagos
- Restaurante y Repartidor
-
El comensal inicia la transacción al realizar un pedido, recibiendo OK por parte del Gateway de pagos al ser aceptada su tarjeta. En caso contrario se procede al Abort de la transacción.
-
Recibida la confirmación por parte del Gateway de pagos, se realiza la solicitud del pedido al restaurante. Si el restaurante cancela el pedido, se recibe el Abort. Si se detecta la conexión del restaurante por más de un tiempo determinado, se interpretará también como Abort.
-
Una vez el repartidor recoge el pedido, esperamos recibir su OK (al llegar a la ubicación del comensal). Hasta entonces esperamos recibir mensajes Keep Alive(KA) de su parte. De no recibir KAs hasta luego de cierto tiempo, se considerará como Abort.
-
Recibida la confirmación del repartidor, el comensal procede al Commit efectuando el "cobro".
-
De detectarse un Abort, realizamos la limpieza de la autorización en el registro del gateway de pagos.
-
Aplicaciones Rider
-
Rider Interface
cargo run -p rider --bin riders_interface <ID_node>
ID_node: es un numero entre el 0 y el máximo numero de nodos (actualmente 5)
-
Rider
cargo run -p rider --bin rider "Rider {nombre del rider}"Los nombres de los riders son letras que van de la A a la T, en orden alfabetico. Por ejemplo:
cargo run -p rider --bin rider "Rider A"
-
-
Aplicación Restaurante
Para ejecutar los siguientes comandos es necesario estar posicionados en el directorio restaurant. Entonces, desde la raíz del proyecto:
cd restaurantLuego
cargo run "Restaurant {nombre del restaurante}"Los nombres de los restaurantes son letras que van de la A a la J, en orden alfabetico. Por ejemplo:
cargo run "Restaurant A"Además, opcionalmente se puede levantar el restaurante con las siguientes flags:
--verbosepara mostrar más información de las acciones que suceden en el restaurant.--recoverypara levantar el restaurante desde el backuop (si existe) y poder retomar pedidos preparados recientemente (2 min).
-
Aplicación Gateway de Pagos
cargo run --package gateway --bin gateway
Opcionalmente podemos ejecutarlo en modo verbose:
cargo run --package gateway --bin gateway -- -v
-
Aplicación Comensal
Para ejecutar el prgrama de un restaurant ubíquese primero en el directorio costumer, desde la raíz del proyecto:
cd costumerLuego
cargo run "Costumer {nombre del comensal}"Los nombres de los comensales son letras que van de la A a la J, en orden alfabetico. Por ejemplo:
cargo run "Costumer A"
ConnectionManager
- Se encarga de buscar los restaurantes cercanos que están conectados a través de mensajes UDP
UserInteractionManager
- Es el "Instrucciones input" del diagrama. Se encarga de presentar al usuario las opciones de restaurantes disponibles y solicitarle que seleccione uno.
ReconnectionSupervisor
- Si el restaurante se desconecta, el actor RestaurantContact finaliza su ejecución. Este actor se encarga de intentar restablecer la conexión con el restaurante caído repetidas veces y, una vez que lo logra, vuelve a crear a RestaurantContact para completar el flujo de la orden.
GatewayReconnectionSupervisor
- Si el gateway se desconecta, el actor GatewayContact finaliza su ejecución. Este actor se encarga de intentar restablecer la conexión con el gateway caído indefinidamente y, una vez que lo logra, vuelve a crear a GatewayContact para completar la transacción.
Se agregaron archivos json para manejar la persistencia y recuperabilidad de los pedidos tanto para RestaurantContact, RiderContact y GatewayContact.
Además de los mensajes mostrados en el diagrama originalmente, estos son los mensajes agregados más importantes:
- StartDelivery: RestaurantContact -> RiderContact
- CancelDelivery: RestaurantContact -> RiderContact
- CancelTransaction: RestaurantContact -> GatewayContact
En el diagrama original no se detallaban los diferentes escenarios de caída del comensal o de aplicaciones externas. Cada uno de estos casos requiere un manejo particular y, en algunos, es necesario cancelar los pedidos, lo que motivó la incorporación de estos mensajes.
En el caso de StartDelivery, anteriormente RiderContact consideraba iniciado el delivery al recibir el mensaje "OnTheWay" del rider asignado. Sin embargo, si el comensal estaba caído en ese momento, el viaje nunca se daba por iniciado. Ahora, el delivery se considera iniciado cuando RestaurantContact envía el mensaje StartDelivery, que ocurre al recibir el mensaje "Delivering" del restaurante, indicando que el rider ya tiene el pedido y está en camino.
Nota: Solo es posible recuperar pedidos que hayan alcanzado el estado Prepared y siempre que el comensal se reconecte dentro de los 2 minutos posteriores a la caída del restaurant. Los pedidos en estados anteriores no pueden ser recuperados.
| Actor | Semántica | Nombre viejo (si ya existía) |
|---|---|---|
OtherNodesAgent |
Abstracción de la comunicación entre nodos (vía UDP) | La responsabilidad era de AdministradorVotacionesnActiva, solo se delegó |
OrderManager |
Administrador de las ordenes recibidas directo desde los restaurantes dentro de un nodo | La responsabilidad era de AdministradorVotacionesnActiva, solo se delegó |
Restaurant Agent |
Comunicación con cada restaurante atendido en el nodo | Restaurants |
VotingSystem |
Administrador de múltiples votaciones llevándose a cabo dentro de un nodo. Puente para conecta a cualquier VotingInProgress de forma segura. |
La responsabilidad era de AdministradorVotacionesnActiva, solo se delegó |
VotingInProgress |
Representación de un una votación de una orden llevándose a cabo | La responsabilidad era de AdministradorVotacionesnActiva, solo se delegó. La idea preexistente se denominó VotaciónInterna |
| Mensaje(s) | Semántica | Nombre viejo (si ya existía) |
|---|---|---|
ResultInternalVoting |
Mensajes entre nodos (vía UDP) | Es una "implementación" de un BroadcastMsg. La idea ya existía |
NewOrder |
Mensajes entre nodos (vía UDP) | Es una "implementación" de un BroadcastMsg. La idea ya existía |
Ack |
Mensajes entre nodos (via UDP) | Es una "implementación" de un BroadcastMsg. La idea ya existía |
WinnerByTimeout |
Mensajes entre nodos (via UDP) | Es una "implementación" de un BroadcastMsg. La idea surge debido a los timeouts para recibir resultados de otros nodos respecto a una misma orden. |
Wrappers... |
Mensajes entre actores respecto comunicación entre nodos (vía UDP) | No existía |
LeaveVoting,AcceptOrder, RejectOrder |
Comunicación saliente del RiderAgent al VotingSystem |
No existía |
HandshakeMsg, DeleteRiderLocation, UpdateRiderLocation |
Mensajes del actor RiderAgent a RiderLocationGuard |
No existía |
NewOrder |
Mensaje autoncontenido para iniciar una votación en todos los nodos. Construida por VotingInProgress |
Ya existía pero se le agregó más metadata. La responsabilidad de enviarlo era de AdministradorVotacionesnActiva. |
NewVotation |
Mensaje entre actores a partir de NewOrder agregando metadada necesaria para iniciar la votación |
No existía |
ResultInternalVoting |
Un nodo comunica el resultado de una votación en particular | Nombre no definido |
EndVotingForTimeout |
Mensaje de actores para evitar bloqueo del flujo de realización de votaciones propias en un nodo (VotingInProgress a sí mismo) |
No existía |
La comnicación con un restuarante con una interfaz se realiza a travéz de una conexión TCP (aprovechamos la comunicación reliable ).
Todos los actores que representan una conexión con otro extremo de comunicación (es decir, que manejan el stream de entrada y el extremo de escritura) poseen un subfijo "Agent" en el nombre del actor.
La votación interna de un pedido en particular cuenta con un timeout. Si es que los riders contactados para una votación interna se tardan mucho en responder se termina la votación con los riders que ya hayan respondido, de esta forma, el flujo de ordenes "internas" (es decir que se recibieron via TCP directo de un restaurante) no se ve atascado en caso de que un rider no responda. Véase el procedimiento para procesar un pedido desde la perspectiva de un nodo para obtener más detalles de los cuidados tomados debido a la introducción del timeout antes no planteado.
Como los riders pueden recibir múltiples ofertas de diferentes resaturantes, se agregó una cola de ofertas pendientes para responder, de esta forma no permitimos inconsistencias dejando a un rider aceptar dos publicaciones de ordenes.
Cuando llega al restaurante y se recibe el pedido se guarda en disco por si la aplicación falla. Esto es que si ya se indicó al restaurante que este rider realizará el envío, entonces se realiza el backup con la información necesaria para poder retomar el pedido:
pub struct DeliveryState {
pub order_publishing: OrderPublishing,
pub pickup_timestamp: u64,
pub delivery_duration_secs: u64,
}NOTA:
delivery_duration_secses el tiempo que se habia estimado en llegar al destino.
- Si es que al recuperase el tiempo desde que se recogió el pedido desde el restaurante ya excedió el tiempo estimado de entrega: se asume que el pedido ya se realizó y el costumer recibió su orden.
- Si es que al recuperase, el rider sigue en trayecto, se retoma desde alli. (entonces se lleva a cabo el envio del mensaje al costumer indicando que el rider llegó a su ubicación )
TcpSender: Se encarga del envío de datos a través del lado de escritura de un socket TCP, vinculado a un cliente.
ReplicaGateway: Se removió el actor en favor de la simplicidad de envío de datos a través de sockets UDP.
SendDataToNext: Gatway -> Gateway(Remote)SendDataSafe: Gatway -> Gateway(Remote)RecvFromRing: Gateway(Remote) -> GatewayKeepLeaderAlive: Gateway -> Gateway(Remote, Leader)ClientRegister: CostumerContact -> Gateway(Leader)ClientCommit: CostumerContact -> Gateway(Leader)ClientAbort: CostumerContact -> Gateway(Leader)
Al levantarse una instancia de Gateway, ésta trata de encontrar al líder actual, de no encontrarlo, se autoproclama líder. De existir un líder, éste recibirá el mensaje y le responderá con el registro de clientes actual.
La instancia líder, que se comunica con los clientes, al procesar el registro de los mismos les asignará un id, el cuál será la clave para realizar COMMITS y ABORTS futuros.
A diferencia de lo planteado en el diseño inicial, en la presnte entrega, se implementó el lanzamiento de 2 tareas asincrónicas adicionales al ejecutar la aplicación (inicio), estas precisamente cubren los siguientes aspectos:
- Abrir un socket UDP (no bloqueante) vinculado al puerto público para comensales e iniciar un loop en el que se reciban mensajes de los posibles copmensales (comensales interesados en saber si el restaurant está atendiendo) y se delegue su procesamiento y respuesta asicrónica a un actor nuevo
PosibleCustomerCommunication. - Abrir un socket UDP (no bloqueante) vinculado al puerto público para riders e inciar un loop en el que se reciban los mensajes de los riders que tienen que recoger algún pedido del restauran y se dirija su procesamiento y posible respuesta al actor
RidersWaiter. Esto en contraste con el anterior diseño que solo planteaba la apertura de una tarea inicial que aperture el socket TCP vinculado al puerto público para los comensales y ejecutase el loop en que se acepten conexiones y se delegue el seguimiento a esta conexión (procesamiento de mensajes recibidos y respectivas respuestas) al actor dedicadoCustomerCommunication.
Como se mencionó previamente se agregaron dos actores nuevos: PosibleCustomerCommunication y CustomerCommunication, además de esto los actores planteados en el rpimer diseño se vieron modificados de la siguiente foirma:
OrdersInteface: Ahora también incluye la lógica para manejar que el servicio de publicación no esté disponible y cuando vuelve a estar disponible:- Se maneja un mensaje que indica que el servicio de publicación de pedidos no está disponible de manera que futuros pedidos no sean aceptados como precaución y se refleja en el estado interno (el
publisher_addrpasa de serSomeaNone) - Frente a la preparación de un pedido y la necesidad de publicarlo, en caso de no tener disponible el servicio de publicación se guarda como pendiente a los pedidos que deberían ser publicados para que se manejen adecuadamente cuando el servicio vuelva a estar disponible
- Se maneja el mensaje que indica que se logró reestablecer la conexión con el sevicio de riders para que futuros pedidos puedan ser aceptados y se republiquen aquellos pedidos que no pudieron ser publicados anteriormente.
OrdersPublisher: Se agregó la lógica necesaria para gestionar el establecimeinto, pérdida y recuparación de la coenxión con alguna instancia deRidersInterfacepuesto que no tiene sentido que el restaurante acepte nuevos pedidos si no tiene acceso a algú servicio de delivery. Por lo cual resultó necesario agregar comunicación entre este actor y elOrdersInterfaceque comunique el estado de la disponibilidad del servicio externo.
Se estableció que el servicio RidersInterface opera bajo el principio de best effort. El sistema tolera un tiempo considerable para que se desarrolle la votación y el rider seleccionado (de existir) establezca comunicación con el restaurant. En caso de no recibir el primer mensaje del rider como confirmación de éxito en la operación, el restaurant es responsable de recontactar su RidersInterface para republicar el pedido hasta lograr la confirmación.
Se definieron las siguientes políticas para el manejo de desconexiones de clientes con pedidos en curso:
-
Pedidos en estado previo a
Prepared: Se desestima el pedido. Se acuerda con el Customer que no se realizarán intentos de recuperación y el pedido deja de ser responsabilidad del Restaurant. -
Pedidos en estado
Preparedo posterior: Se acuerda que la aplicación Customer eventualmente se reconectará con el Restaurant para recuperar el seguimiento de su pedido. Por esta razón, el restaurant continúa con la publicación del pedido.
Para recuperarse de una caída, el restaurant restaura pedidos que cumplen las siguientes condiciones:
- Se encontraban en estado
Prepared - No se ha guardado su estado hace más de dos minutos
Cuando el Restaurant se levanta en modo --recovery, el cliente que tenía un pedido en estado Prepared y perdió conexión dispone de una ventana de 2 minutos para reconectarse con el restaurant y enviar un mensaje de recuperación del seguimiento del pedido.
Casos especiales:
- Pedidos en otros estados: No se restauran desde el backup, ya que el customer no intentará contactarse para recuperar su seguimiento.
- Pedidos
Preparedcon más de 2 minutos de antigüedad: Tanto el restaurant como el Cliente ejecutan las acciones necesarias para reflejar la cancelación del pedido.
-
Solo se recuperan pedidos en estado
Preparedy dentro de los 2 minutos posteriores a la caída. -
La publicación de pedidos a través de la interfaz de repartidores es best effort y se reintenta si no hay confirmación del rider.
-
Los pedidos en estados previos a
Preparedno se restauran ni se recuperan. -
En caso de no recibir el primer mensaje de contacto del rider en 20 segundos, el restaurant considera que la operación no fue exitosa y reintentará la publicación del pedido a través de la interfaz de repartidores, siguiendo la política de best effort definida para el sistema.









