Introducción

Bienvenidos a otro artículo en «Hola Windev». Hoy vamos a abordar un tema crucial para cualquier empresa: cómo implementar un sistema de respaldos en tiempo real para bases de datos HFSQL.

Desarrollo

Servidor WebSocket

Para la configuración y el desarrollo del servidor de WebSockets debemos tener claro como funciona este tipo de servicios.

Por defecto vamos a tener algunos eventos disponibles que tenemos que tener en cuenta. Estos son:

  • Evento “Bucle”
    • Este evento se ejecuta eternamente, es un loop infinito. Todo lo que este dentro de este evento se va a ejecutar una y otra vez sin parar.
  • Evento “Nueva conexión”
    • Este evento se ejecuta cuando se realiza una nueva conexión por parte de un cliente.
  • Evento “Mensaje recibido”
    • Este evento se ejecuta cuando se recibe un mensaje.
  • Evento “Desconexión de un cliente”
    • Este evento se ejecuta cuando un cliente se desconecta
  • Evento “Finalización del servicio”
    • Este evento se ejecuta cuando se finaliza el servicio.

Vamos paso a paso viendo que es lo que ejecutamos en cada evento.

En el Evento “Nueva conexión” llamamos al procedimiento “onConnection”. Es un procedimiento muy sencillo que crea y asigna información para el cliente que se acaba de conectar

PROCEDURE onConnection( Client is websocketClient )

// Generamos un ID único al cliente
jsClient is JSON
jsClient.id		= GetUUID()

// Guardamos en el campo "Note" del tipo websocketClient los datos del mismo
Client.Note		= jsClient

jsNotification is JSON
jsNotification.eventType	= "socket_connected"
jsNotification.socket_data	= Client.Note

// Enviamos el mensaje con el estado de la conexión
Client.Send(jsNotification)

En el Evento “Recepción de un mensaje” llamamos a los procedimientos “onReceivedMessage” y “sendAll”.

PROCEDURE onReceivedMessage( Client is websocketClient, jsMessage is JSON )

// Leemos la información del cliente
jsClient is JSON
jsClient = Client.Note

SWITCH jsMessage.eventType
	
	CASE "disconnect"
		
		jsClient.socket_data	= jsMessage.socket_data
		Client.Note				= jsClient
		
		jsNotification is JSON
		jsNotification.eventType			= "socket_disconnect"
		jsNotification.socket_data			= Client.Note		
		
	//Registramos al cliente
	CASE "register"
		
		jsClient.socket_data	= jsMessage.socket_data
		Client.Note				= jsClient
		
		jsNotification is JSON
		jsNotification.eventType		= "socket_added"
		jsNotification.socket_data		= Client.Note		
		
		//Lo enviamos a todos los clientes conectados... por ahora para hacer un debug
		sendAll(jsNotification)
		
	//keep alive
	CASE "ping"
		
		jsPong is JSON
		jsPong.eventType = "pong"			
		Client.Send(jsPong)
		
		
END
PROCEDURE sendAll( jsMessage is JSON )

//Recorremos el array de clientes conectados y a cada uno de ellos
//le enviamos el mensaje
Client is websocketClient
FOR EACH Client OF WebSocketListConnectedClient()
	Client.Send(jsMessage)		
END

En los eventos “Desconexión” desconectamos al cliente y en “Finalización del servicio” enviamos un mensaje usando el procedimiento sendAll.

Ahora, hay un agregado en esta configuración del websocket que estamos configurando. Vamos a crear un proceso de supervisión para la tabla que va a ser afectada por el Webhook. Para esto usamos la función HTrack

Y lo haremos de esta manera:

HTrack(respaldos,newWebhookEvent,hRecNumAll,hsAdd)

Se podría crear un evento directo en el Webhook también, a efectos de mostrar el funcionamiento de HTrack es que vamos a utilizar esta alternativa. Si se desea, en lugar de HTrack, se podría enviar el mensaje directamente desde la API Rest al momento de recibir el POST.

El deploy de nuestro servidor de WebSockets se hace como cualquier otra aplicación que subimos a un WAS. Una vez hecho esto podríamos testear con Postman que este todo ok de esta manera:

Como vimos anteriormente, al conectarnos se ejecuta el procedimiento onConnection y se envía un mensaje con el estado de la conexión que remarco en la imagen.

Con esto último ya tenemos nuestro servidor de WebSockets funcionando y testeado.

Notificación vía Webhook

Antes de comenzar a programar el WebHook vamos a ver la ayuda de HFSQL respecto al uso de WebHooks por parte del servidor de base de datos.

La ayuda nos muestra los datos que vamos a recibir al momento de finalizar una tarea de respaldo.

Teniendo esto en claro vamos a generar un archivo de datos en nuestro análisis:

A los campos que nos va a devolver el HFSQL le sumo un campo rawContent en el cual guardaremos el JSON tal cual lo recibimos, el serverIP para guardar la IP del servidor y un campo timeStamp para tener el dato de cuando fue creado el registro.

Con esto ya comenzamos a programar el Webhook (o gancho web según la ayuda de PCSoft 😉 )

Para programar el Webhook podemos hacerlo de la manera sencilla o de la complicada, como estoy corto de tiempo prefiero la sencilla …

Para esto tenemos que ubicar la descripción de nuestro webservice, hacer click derecho y agregar nuevo punto de entrada (Endpoint).

Esto nos abre una ventana en la cual tenemos que elegir Puntos de entrada para acceder a un archivo de datos, seleccionamos el archivo que acabamos de crear y avanzamos hasta que nos solicite los puntos de entrada. En este caso yo voy a dejar solamente los GET y el POST. No me interesa, de momento, el DEL ni el PUT.

Nos quedaría algo así

PCSoft crea automáticamente la clase, si esto usa POO a full y los métodos necesarios para lograr que funcione todo a la perfección. Estos métodos son 100% modificables, así que si los queremos cambiar podemos, de hecho es lo que vamos a hacer a continuación.

Si miramos el metodo “Creation” vamos a ver esto:

El primer bloque nos muestra un metodo de autenticación, a efectos de no desvirtuar mucho la idea de este post vamos a dejarlo por defecto (viene vacío por lo cual no valida nada). Si quieren se puede hacer la validación que gusten, quizás les interese el artículo que hay en Windev Uruguay al respecto:

Webservices Rest – Autenticación – Windev Uruguay

En el segundo bloque de código tenemos la creación del registro. Ellos, (PCSoft), realizan unos pasajes entre metodos pero en resumen lo que hacen es un reset del archivo “respaldos” y luego una deserialización del JSON que estamos recibiendo en el POST.

La modificación que sugiero en este caso es la siguiente:

Si recuerdan bien tenemos dos campos mas para agregar aparte de los que nos envía el HFSQL en cada tarea de respaldos. De este modo es que los asignamos a cada campo de nuestro archivo de datos.

Con esto terminamos la programación de nuestro Webhook. Podemos testearlo en Postman y ver como reacciona la conexión que tenemos abierta a nuestro servidor de websockets:

Ya tenemos todo listo, ahora vamos a programar el cliente Windows desde el cual inicializaremos la tarea de respaldo desde una ubicación remota y recibiremos las alertas de nuestro servicio.

Aplicación Windows para Respaldos

Ahora si, vamos a programar nuestra aplicación cliente para gestionar los respaldos.

Esta es la ventana, ya se es fea, vamos que estamos practicando!

Como ven en esta ventana tenemos dos botones y una tabla.

La tabla se llena con una query que trae todos los registros de la base de datos. Y los botones uno se usa para enviar mensajes por sockets para poder testear que este todo ok y el otro genera un respaldo.

Vamos por partes. En el código de inicio de la ventana tenemos que programar algunas cosas para comenzar.

PROCEDURE MyWindow()
gs_SessionID	is string	= "ID_UNICO_PARA_EL_CLIENTE"
gs_Server		is string	= "192.168.200.164"

IF SocketExist(gs_SessionID) = False THEN
	WebSocketClientConnect(gs_SessionID,SocketConnect_Callback,gs_Server,80,"/websockets_testing")
END

	INTERNAL PROCEDURE SocketConnect_Callback( skt_Event, skt_Message )
		json_Mensaje	is JSON	= skt_Message
		
		SWITCH skt_Event
			CASE SocketOpening 
				//Evento keepalive
				ThreadExecute("Timer",threadSecure,Timer_Callback)
				
					INTERNAL PROCEDURE Timer_Callback()
						jsPing is JSON
						jsPing.event = "ping"
						SocketWrite(gs_SessionID, jsPing)
						ThreadPause(5 s)
					END
				
				
			//Al recibir un mensaje del websocket evaluamos el event al callback
			CASE SocketMessage
				SWITCH json_Mensaje.eventType
				
				//
				//	Inicia bloque para manejo de los eventos socket
				//
				
					CASE "backup"
						RequestRefreshUI()
						
					CASE "socket_connected"
						js_Register is JSON
						js_Register.event		= "register"
						js_Register.socket_data	= gs_SessionID
						SocketWrite(gs_SessionID,js_Register)
						
					CASE "pong"
						
					OTHER CASE
						Trace("Evento Socket no controlado: " + json_Mensaje.eventType)
						Trace(json_Mensaje.data)
				END
				
			//Error 
			CASE SocketError	
				Trace("Ha ocurrido un error: ([%skt_Message%])")
				
			//Cierre del servidor 
			CASE SocketClosing	
				Trace("La conexión con el servidor de Sockets ha sido interrumpida: ([%skt_Message%])")
				
			OTHER CASE
				Trace("Error sin especificar: ([%skt_Message%])")
		END
	END

Lo que tenemos aquí es el proceso de conexión al websocket y usamos un callback para capturar las respuestas de forma asincrona.

Basicamente lo que tenemos es el manejo de los eventos que vamos a estar recibiendo desde el servidor de websockets. Lo novedoso es que, si se fijan, tenemos programado un evento keepalive (un ping) para mantener la conexión activa.

Luego tenemos un evento “backup” que lo que va a hacer es refrescar la tabla de forma asíncrona usando el proceso RequestRefreshUI(), esta es una función que yo utilizo mucho (muchísimo en realidad). Al llamar a esta función nuestro sistema nos va a dirigir a un evento de la ventana, “Solicitud para actualizar la visualización”.

De este modo cuando se reciba un evento “backup” desde el websocket se va a actualizar la tabla de forma automática.

Vamos a ver los botones ahora.

En el botón “Enviar mensaje” tenemos este código muy sencillo:

Simplemente envía un mensaje por socket a nuestro servidor.

Y ahora si, la parte final. El botón “Backup”.

En este vamos a ver como se realiza un respaldo de forma remota de un servidor HFSQL.

Voy a mostrarles el código y vamos a analizarlo juntos:

En la primer sección tenemos la declaración de la tarea de respaldo. El código de esta parte del procedimiento es bastante claro. Se crea una descripción del bakcup y se ejecuta. Dentro de esta definición tenemos la propiedad WebhookAfter, ahi es donde ingresamos la URL de nuestro Webhook, el que programamos en el paso anterior.

El segundo bloque revisa que el respaldo haya terminado, lo hice de manera sencilla y se puede (mejor dicho debe) mejorar, entre otras cosas meterlo dentro de un thread para que no bloquee la aplicación.

Y el tercer bloque lo que hace es descargar el ZIP con el respaldo.

No tiene mucho misterio vieron?

Ahora vamos a verlo en acción.

Conclusión

Espero que este artículo te sea de utilidad y te ayude a entender cómo puedes implementar un sistema robusto de respaldos en tiempo real utilizando exclusivamente tecnologías de PCSoft.

Invitación

Si este artículo te ha resultado útil, no olvides unirte a nuestro servidor de Discord «Hola Windev» para interactuar con otros apasionados del desarrollo en PCSoft.