# Rediseño de la base para mensajeria de Light Client En este documento vamos a mostrar propuestas para el rediseño de las tablas involucradas en la mensajería para los Light Clients. La idea final es poder lograr un refactor de la base para que la mensajería de los LC sea mas génerica y no tan arraigada a los pagos como lo es hoy en día. Mostraremos varios enfoques, algunos requieren más cambios que otros, la idea es evaluar estos enfoques y definir cual sería el mejor y mas adecuado a la situación actual. ## Estructura actual de nuestra base de datos ![](https://i.imgur.com/Q3BsGqY.png) En el esquema veremos múltiples tablas como es de esperarse. Nosotros nos enfocaremos en la mensajería del LC, por lo que comprobaremos que las tablas involucradas son ```light_client_protocol_message``` y ```light_client_payment```, a su vez también tenemos una relación directa con la tabla ```client``` que posee toda la información de los LC. Por lo que se puede apreciar vemos que la tabla ```light_client_payment``` posee toda la informacion de los pagos realizados pero en el esquema se encuentra entre la tabla ```client``` y ```light_client_protocol_message``` lo cual en su momento tuvo sentido pero ahora se nos pone en el camino si queremos generalizar la mensajería para cosas por fuera de los pagos. ## Primer Enfoque En este primer enfoque vamos a asumir que podemos refactorizar la base sin grandes dramas por lo que será el enfoque que más cambios en la base implique pero que cumple con un buen esquema. ![](https://i.imgur.com/tEpNiKE.png) Como podemos ver en este esquema planteamos la posibilidad de "quitar" del medio la tabla ```light_client_payment```. A continuación pasamos a explicar como funcionaría el esquema mencionado. La idea del nuevo esquema es tener un manejo genérico de mensajes por lo que primero que sugerimos es quitar la relacion directa entre ```light_client_payment``` y ```light_client_protocol_message```. Luego para seguir cumpliendo con el equema que actualmente tenemos y mantener la retrocompatibilidad en cierta medida, tenemos que agregar la tabla ```light_client_payment_message``` que será el mapeo entre ```light_client_payment``` y ```light_client_protocol_message``` dejando la posibilidad de que haya muchos mensajes para un payment. A su vez necesitamos agregar una segunda tabla llamada ```light_client_protocol_message_data``` para almacenar todos los datos que pueden ser utilizados por el LC provenientes del nodo, esto es para suplantar la ausencia del parámetro ```light_client_payment_id``` en el caso de los pagos pero también para utilizarla en cualquier caso donde se necesite brindar informacion relacionada con el mensaje al LC desde el nodo. Por último agregamos una tabla llamada ```light_client_protocol_message_required_parameter``` que contiene los parámetros que son requeridos en el mensaje y que el LC es encargado de proveer. Si bien son 3 tablas nuevas el esquema queda mas extendible dado que para nuevos tipos de mensajes solo tendríamos que agregar un nuevo message_type en la tabla ```light_client_protocol_message``` y luego el manejo del mensaje seria indiferente. #### Como funcionaría este enfoque? Basicamente hariamos lo siguiente: 1. Cuando tenemos un mensaje que requiere interaccion del LC almacenamos dicho mensaje en la tabla ```light_client_protocol_message``` como se hace actualmente 2. Si hay parametros que pasarle al LC de parte del hub simplemente se almacenan en la tabla ```light_client_protocol_message_data```. 3. Si hay parametros que el hub no puede proveer y que son requeridos para poder firmar el mensaje y mandar la transaccion se agregan a la tabla ```light_client_protocol_message_required_parameter``` 4. Si el mensaje esta relacionado con un pago se agrega la relacion a la tabla ```light_client_payment_message``` para seguir cumpliendo con el esquema actual. (Se deben actualizar todos los puntos donde actualmente se hace referencia al payment para utilizar dicha tabla). El long polling se mantiene, pero en vez de ser un long polling de una sola tabla, habría que modifcar la consulta para que se convierta en un join entre las 3 tablas. A nivel de performance como estamos hablando de SQLite y por lo que se pudo investigar dado que dicha base esta en memoria gran parte del tiempo, agregar un join con las tablas mencionadas no haría diferencia. ## Segundo Enfoque Aquí planteamos una alternativa parecida al primer caso. Mantenemos la relación con payments (y en casos futuros evaluamos si se agrega alguna relacion extra dependiendo de que otra entidad se requiera relacionar). Se agrega una tabla de parámetros donde se especifican aquellos requeridos y aquellos que el nodo provee para que sean consumidos por el LC. El esquema planteado seria el siguiente: ![](https://i.imgur.com/tXhrHaE.png) Como se ve en el esquema podemos apreciar que se mantiene la relación entre ```light_client_protocol_message``` y ```light_client_payment```. Ademas se agrego una relacion nueva entre ```light_client_protocol_message``` y ```client``` dado que un client puede tener mensajes que no estan necesariamente relacionados con un pago. Por último se agrego una nueva tabla llamada ```light_client_protocol_message_parameter``` donde se guardan los parametros relacionados con un mensaje (requeridos para que el LC los genere y los que el nodo provee para que el LC utilice). #### Como funcionaría este enfoque? Si bien este enfoque es muy parecido al primero, aqui eliminamos dos tablas del set de tablas nuevas ya que dejamos todo en una sola tabla. El procedimiento seria el siguiente: 1. Cuando tenemos un mensaje que requiere interaccion del LC almacenamos dicho mensaje en la tabla ```light_client_protocol_message``` como se hace actualmente. 2. Si hay parametros que pasarle al LC de parte del hub simplemente se almacenan en la tabla ```light_client_protocol_message_parameter``` como parametro de tipo Provided especificando nombre y valor. 3. Si hay parametros que el hub no puede proveer y que son requeridos para poder firmar el mensaje y mandar la transaccion se agregan a la tabla ```light_client_protocol_message_parameter``` como parametro de tipo Required especificando unicamente el nombre. ## Tercer Enfoque Ahora asumiremos lo contrario del primer enfoque, que modificar la base es dificil y que no podemos realizar muchos cambios. Nos vemos obligados a minimizar el cambio en las tablas y tratar de adaptar la estructura actual para que cumpla con un esquema de mensajes mas generalizado. Para esto proponemos el siguiente esquema: ![](https://i.imgur.com/RFIe5vj.png) Como se puede ver es el mismo esquema que el original con algunas modificaciones en la tabla ```light_client_protocol_message```. Básicamente agregamos 3 nuevas columnas, **client_address**, **data**, **required_parameters**. A continuación explicamos que significan cada uno de ellos: * **client_address**: el address del client al que este mensaje esta asociado * **data**: es análogo al primer enfoque pero en este caso estaríamos guardando un JSON donde contendríamos los datos que el LC puede necesitar del lado del nodo, la forma del JSON podría ser: ```json { "some_field": "some value" } ``` Con esto cubrimos los casos como el de pagos, que por ejemplo necesitamos informar al LC cual es el pago entonces tendríamos algo como: ```json { "light_client_payment_id": "some id", } ``` * **required_parameters**: esto tambien es análogo al enfoque inicial pero aquí también tendríamos un JSON donde pondríamos los parámetros requeridos para que el LC los agregue al mensaje antes de firmar y mandar la transaccion. Este JSON sería algo distinto al anterior ya que sería mas bien una lista de parámetros nomas, algo así como el siguiente ejemplo: ```json { [ "parameter_name_1", "parameter_name_2", "parameter_name_3", ... ] } ``` #### Como funcionaría este enfoque? Si bien no es tan purista a nivel de base de datos, es decir estamos guardando datos en formato JSON en lugar de estructurarlos en la base dentro de tablas, el enfoque tiene menos impacto a nivel estructural propiamente dicho lo cual facilita que se pueda integrar como cambio en la mensajería del LC sin afectar lo que actualmente tenemos. La dinámica sería la siguiente: 1. Cuando tenemos un mensaje que requiere interacción del LC almacenamos dicho mensaje en la tabla ```light_client_protocol_message``` como se hace actualmente. 2. Si hay parámetros que pasarle al LC de parte del hub simplemente se almacenan en la columna **data** dentro de la misma tabla al momento de crear el mensaje. 3. Si hay parametros que el hub no puede proveer y que son requeridos para poder firmar el mensaje y mandar la transaccion se agregan a la columna **required_parameters** dentro de la misma tabla al momento de la creacion del mensaje. ## Cuarto enfoque En este caso quitaremos la relación con la tabla de pagos y reduciremos la cantidad de columnas, simplemente agregaremos la relacion con client y una columna para guardar la data: ![](https://i.imgur.com/SHWCjdO.png) La idea de este enfoque es guardar un JSON en la columna **message_data** que contendrá los parámetros requeridos asi como la data necesaria para el LC. El gran problema con este enfoque es que se quita la relacion con pagos y habría que hacer un chequeo manual de los mensajes cuando un pago se borra lo cual no es muy eficiente. Aun así puede ser una opcion. La idea en este caso sería que cuando se cree un mensaje para el LC dentro del mismo se guarde un JSON con la siguiente estructura: ```json { "required_parameters": [ "parameter_name_1", "parameter_name_2", ... ], "data": { "payment_id": "some id", "some other param": "some other value", ... } } ``` #### Como funcionaria este enfoque? Si bien es el peor enfoque de todos aun asi no afecta tanto la base de datos, básicamente solo quita una relación y añade unas columnas. El problema es que esta relación tiene que ser manejada manualmente. Estos serían los pasos: 1. Cuando tenemos un mensaje que requiere interaccion del LC almacenamos dicho mensaje en la tabla ```light_client_protocol_message``` como se hace actualmente. 2. Si hay parámetros que pasarle al LC de parte del hub simplemente se almacenan en la columna **message_data** dentro de la misma tabla al momento de crear el mensaje en el atributo data del JSON. 3. Si hay parámetros que el hub no puede proveer y que son requeridos para poder firmar el mensaje y mandar la transaccion se agregan al atributo required_parameters dentro del JSON en **message_data** antes de guardar el mensaje. 4. Si un pago se borra hay que agregar un chequeo para que se busquen todos los mensajes relacionados con ese pago. Dado que la relación se quita perdemos performance porque hay que buscar 1 a 1 los mensajes y chequear si en los atributos tenemos el payment id asociado. ## Modificaciones a nivel de código del nodo A nivel de nodo el código no se modifica mucho, tan solo hay que agregar un nuevo parametro a la consulta y modificar la salida dependiendo del enfoque elegido finalmente. Las modificaciones que deberiamos realizar a nivel de código están detalladas a continuación: 1. Modificar endpoint ```/light_client_messages``` para aceptar un nuevo parámetro que especifique el tipo de mensaje a obtener (para limitar aun más la consulta y hacerla mas performante). El nuevo parámetro seria **message_type** y seria algo opcional, en caso de no ser especificado se deberian traer todos los mensajes como se hace hoy en día utilizando el parametro actual. (Evaluar si el nuevo parametro de message_type tiene sentido de ser en el filtrado), en caso contrario los puntos 2 y 3 son ignorados). 2. Modificar el resource ```LightClientMessageResource``` para manejar el nuevo parámetro 3. Modificar **raiden/api/rest.py:2089** para adaptar la funcion al nuevo parámetro. 4. Modificar **raiden/lightclient/handlers/light_client_service.py:13** para adaptarlo a la nueva estructura si aplica (métodos con el parámetro nuevo para obtener y para guardar). 5. Modificar las consultas en **raiden/storage/sqlite.py** de la base para modificarlas segun el enfoque elegido, esto implicaría modificar los métodos ```get_light_client_messages``` y todos los que utilicen la tabla **light_client_protocol_message** en sus consultas. 6. Si el enfoque elegido es alguno de los que implican guardado y parseo de JSON's tenemos que agregar código extra para el manejo de dichos valores internamente. ## Modificaciones a nivel de LC 1. Adaptar la llamada de polling a la nueva forma (utilizando el message type cuando aplica). 2. Adaptar la respuesta del message segun la nueva estructura si aplica. 3. Generalizar dicho manejo para los mensajes que no implican pagos relacionados. ## Conclusiones Si bien los enfoques son variados podemos ver que algunos son menos performantes que otros, sobretodo aquellos que implican parseo de JSON's. Si tomamos en cuenta que no podemos realizar tantos cambios en la base y que tenemos que cumplir con el esquema actual por retrocompatibilidad, el enfoque que más aplicaría a dicho caso es el segundo; donde si bien realizamos cambios en la base nos limitamos a tan solo una tabla nueva manteniendo las relaciones actuales. A su vez el cambio en código no es tan grande si se piensa dado que es simplemente el manejo de la nueva tabla por parte del nodo y del LC en la respuesta. # Resumen Se tomo otro enfoque distinto a los presentados, basicamente usamos unsigned_message para guardar el JSON, agregamos a message la relacion con client y quitamos la relacion entre payment y client e invertimos la relacion entre payment y message para que un pago tenga mensajes y un client tenga mensajes. Refactorizamos el codigo para cumplir con el nuevo esquema: ![](https://i.imgur.com/0MNeW0d.png)