Servicio de Transacciones distribuidas ¿Cómo usarlo en Plataforma?

Servicio de Transacciones distribuidas ¿Cómo usarlo en Plataforma?

Esta funcionalidad está disponible a partir de la versión 1.6.0.

Introducción

Definimos el concepto de transacción como un conjunto de operaciones sobre una base de datos que se deben ejecutar como una unidad, ya que hay ocasiones en las que es necesario que varias operaciones sobre una base de datos se realicen en bloque, es decir, que se ejecuten o todas o ninguna, pero no que se realicen unas sí y otras no.

Por lo que el soporte transaccional permite a las aplicaciones asegurarse de que:

  • Se ejecutan todas las operaciones a realizar.

  • Cuando falla una de las operaciones de una transacción, todas las demás operaciones realizadas hasta el momento se revierten.

Integración con el Digital Broker

Se han añadido tres operaciones nuevas:

En todas ellas es necesario añadir en la petición la cabecera “Authorization” con la sessionKey obtenida anteriormente con el método “Join”.

  • Start: Operación GET que inicia una transacción. Como resultado de esta operación, obtendrás el transactionId que te servirá como identificador único de la transacción.

  • Commit: operación GET que realiza el commit de la transacción, es decir, ejecuta todas las operaciones asignadas a la transacción. En este caso es necesario pasarle el transactionId, de la transacción que se quiere ejecutar, y el parámetro lockOntologies, que te permite decidir si quieres bloquear las ontologías afectadas en la transacción.

  • Rollback: Borra una transacción y revierte todas las operaciones ya ejecutadas de la misma. Este servicio recibe como argumento el transactionId.

Además se han extendido todos los métodos INSERT, UPDATE, DELETE para que admitan una nueva cabecera opcional TransactionId, de tal forma que si se añade esta cabecera, la operación se añadirá a la transacción, mientras que, si se omite, se realizará la operación de forma normal.

Integración con el Cliente Java

Por ahora sólo están soportadas las transacciones para operaciones REST. En el futuro se soportarán también protocolo MQTT. Se recomienda la lectura del tutorialhttps://onesaitplatform.atlassian.net/wiki/spaces/OP/pages/975306753para entender cómo funciona esta librería y cómo hay que configurarla.

Se ha extendido la librería del cliente Java para soportar las transacciones sobre plataforma.

Para ello se ha creado una clase Transaction. Esta clase cuenta con los métodos INSERT, UPDATE y DELETE y además añade los métodos siguientes:

  • ConfigureConnection: es el encargado de abrir la conexión con la plataforma y recibe como parámetros de entrada:

    • ConnectionType: se trata de un Enum que puede ser REST o MQTT (Recuerda que por ahora sólo se soporta REST).

    • Properties: objeto que almacena los parámetros necesarios para establecer la conexión. Para hacerlo lo más estándar posible. se ha definido además una clase Enum RestProperty con el listado de los posibles parámetros necesarios para estables dicha conexión.

  • Start: crea una nueva transacción y recibe como parámetros de entrada:

    • Device: nombre del Device definido en plataforma.

    • Token: un token cálido asociado al Device definido a plataforma.

    • DeviceTemplate: identificador elegido por el usuario para identificar la conexión.

  • Commit: realiza las operaciones asociadas a una transacción y recibe como parámetro de entrada:

    • LockOntologies: boolean que indica si se quiere que las ontologías se bloqueen o no.

A continuación se muestra un ejemplo práctico de uso:

Hay que usar al menos la versión 1.3.8 de la librería.

 

public static void main(String[] args) throws SSAPConnectionException { final String token = "e7ef0742d09d4de5a3687f0cfdf7f626"; final String deviceTemplate = "TicketingApp"; final String device = "TicketMachine1"; final String ontology = "Ticket"; final ObjectMapper mapper = new ObjectMapper(); Properties prop = new Properties(); prop.put(Transaction.CONNECTION_TYPE, Transaction.ConnectionType.REST.name()); prop.put(Transaction.DIGITAL_BROKER_REST_ENDPOINT, "http://localhost:19000/iot-broker"); Transaction tx = new Transaction(); tx.configureConnection(prop); try { log.info("Example with transaction:"); log.info("1. Starting transaction."); String transactionId = tx.start(token, deviceTemplate, device); log.info("...Started transaction. Transaction id: {}", transactionId); String instance = "{\"Ticket\":{\"identification\":\"WithoutTransaction\",\"status\":\"DONE\",\"email\":\"iex@email.com\",\"name\":\"Alberto\",\"response_via\":\"email\",\"file\":{\"data\":\"\",\"media\":{\"name\":\"\",\"storageArea\":\"SERIALIZED\",\"binaryEncoding\":\"Base64\",\"mime\":\"application/pdf\"}},\"coordinates\":{\"coordinates\":{\"latitude\":45.456,\"longitude\":-41.283},\"type\":\"Point\"}}}"; log.info("2. Inserting one instance with transaction id {}: {}", transactionId, instance); String idInsertTx = tx.insert(ontology, mapper.readTree(instance).toString()); log.info("...Inserted instance in transaction {} with id {}", transactionId, idInsertTx); instance = "{\"Ticket\":{\"identification\":\"ticket_updated\",\"status\":\"DONE\",\"email\":\"iex@email.com\",\"name\":\"Alberto\",\"response_via\":\"email\",\"file\":{\"data\":\"\",\"media\":{\"name\":\"\",\"storageArea\":\"SERIALIZED\",\"binaryEncoding\":\"Base64\",\"mime\":\"application/pdf\"}},\"coordinates\":{\"coordinates\":{\"latitude\":45.456,\"longitude\":-41.283},\"type\":\"Point\"}}}"; log.info("3. Inserting one instance with transaction id {}: {}", transactionId, instance); idInsertTx = tx.insert(ontology, mapper.readTree(instance).toString()); log.info("...Inserted instance in transaction {} with id {}", transactionId, idInsertTx); log.info("4. Commit transaction with transaction id {}", transactionId); tx.commit(true); log.info("..Transaction commited with id {}", transactionId); log.info("Example with transaction OK!!!"); } catch (final Exception e) { log.error("Error in process", e); tx.rollback(); } }

Es importante fijarse en algunos detalles:

  1. (Línea 8) Añadimos al objeto Properties el parámetro CONNECTION_TYPE. En este caso vamos a realizar la conexión vía REST por lo que vamos a tener que añadir también el parámetro DIGITAL_BROKER_REST_ENDPOINT.

  2. (Línea 11) Se abre la conexión con la plataforma.

  3. (Línea 16) Aquí se crea la transacción. El método start nos devuelve el transactionId pero este parámetro es meramente informativo, ya que la librería internamente se ocupa de gestionar la transacción. Es decir, todas las operaciones asignadas al mismo objeto Transaction van a pertenecer a la misma transacción.

  4. (Líneas 21 y 26) Se añaden dos operaciones insert a la transacción.

  5. (Línea 30) Se realiza el commit de la transacción.

  6. (Línea 37) En el caso de cualquiera de las operaciones lance una excepción se lanza la operación rollback.

Integración con el Cliente IoTClient4SpringBoot

Se recomienda la lectura del tutorial https://onesaitplatform.atlassian.net/wiki/spaces/DOC/pages/2215909434 para una total comprensión de dicha librería.

Se ha extendido también la librería IoTClient4SpringBoot para soportar las operaciones dentro de una transacción.

Para ello se ha creado una nueva etiqueta @IoTBrokerTransaction que se asignará a nivel de método, de tal forma que cuando se ejecute un método marcado con esta etiqueta se seguirá el siguiente flujo:

  1. Nada mas entrar al método y antes de realizar ninguna operación del mismo, se abrirá una transacción nueva.

  2. Todas las operaciones INSERT, UPDATE y DELETE que se definan dentro de dicho método irán asignadas a la transacción.

  3. Al salir del método:

    1. Si todo ha ido bien, se realiza el commit de la transacción.

    2. Si se lanza alguna excepción, se realiza rollback de todas las operaciones y se elimina la transacción.

En el caso de que en el mismo flujo de la aplicación se encuentre más de una etiqueta IoTBrokerTransaction, se tomará cómo válida la primera del primer método, siendo ignoradas las etiquetas posteriores.

A continuación se muestra un ejemplo práctico de cómo utilizar esta funcionalidad:

Hay que usar al menos la versión 1.3.8 de la librería.

 

@IoTBrokerTransaction(lockOntologies = true) public void insertInstances() throws Exception { log.info("Ticket is going to be inserted."); CitizenTicket ticket = new CitizenTicket(); ticket.setEmail("onesaitplatform@indra.es"); ticket.setStatus("PENDING"); ticket.setName("Onesait Platform"); ticket.setIdentification("Ticket creado desde el check..."); CitizenTicketOntology ticketOnt = new CitizenTicketOntology(); ticketOnt.setCitizenTicket(ticket); String result = ticketRepository.insertTicket(ticketOnt); log.info("Result insert 1: {}", result); }

Cabe destacar:

  1. (Línea 1) se añade la etiqueta @IoTBrokerTransaction y se le añade el parámetro lockOntologies = true. Este parámetro es utilizado a la hora de hacer el commit de la transacción e indica si las ontologías van a ser bloqueadas durante la transacción o no. Si no se indica este parámetro, se tomará como valor por defecto false.

  2. (Línea 13) A la hora de realizar una inserción, se realiza de la misma forma que se han realizado los inserts fuera de las transacciones. Internamente, la librería se encarga de añadir esta operación a la transacción por encontrarse dentro de un método con la etiqueta @IoTBrokerTransaction.