Migraciones y Timezones

Había una vez dos aplicaciones, una de Contabilidad y otra de Costos, que convivían en una misma máquina y lo hicieron durante muchos años en armonía.

Ambas aplicaciones nacieron mucho antes de que GeneXus tuviera soporte para TimeZone.

Un día, los dioses de los microservicios decidieron que era una buena idea instalar las aplicaciones en diferentes equipos. Para ello, se creó una KB intermedia con servicios simples que permitieran transferir datos de una aplicación a la otra. Esta KB, llamada Servicios SOAP, leía datos de la base de datos de Contabilidad y los devolvía en una colección de SDT para que el sistema de Costos pudiera procesarlos.

La KB de Servicios SOAP y la de Costos estaban programadas en GeneXus, generando en .NET Framework, y convivieron muchos años transfiriendo asientos sin inconvenientes.

La KB de Servicios SOAP no grababa nada en la base de datos; solo leía y devolvía algunos servicios, por lo que no la considerábamos crítica. Todos los campos eran de tipo DATE y no DATETIME, por lo que no parecía riesgoso.

El servicio de CargoAsientos era trivial: un Data Provider expuesto como servicio SOAP que solo hacía lo siguiente:

MovContables
where &FchIni <= AsientoFecha and AsientoFecha <= &FchFin
{
MovContables_Item
{
AsientoNumero
AsientoFecha
Importe
}
}

Era un programa tan sencillo que no teníamos pruebas unitarias para él.

Sabiendo que el generador .NET Framework estaba pasado de moda y desaconsejado por Microsoft, decidimos migrar a .NET Core, que estará soportado por más tiempo. Para ello, creamos un nuevo Environment en la misma KB de Servicios, y por error, dejamos la propiedad DateTime storage timezone en su valor por defecto: GMT.

Hice algunas pruebas desde el sistema, viendo que lo que traía de Contabilidad era lo que estaba grabando y controlando los totales dentro del mes.
 
A partir de ese momento quedo asi


El servidor donde ejecutaba Servicios SOAP estaba en GMT-3 (Montevideo), así que cada vez que leía algo de la base de datos, pensando que estaba en GMT, lo convertía a GMT-3, restándole 3 horas. Por lo tanto, todos los asientos quedaban cargados con la fecha incorrecta.

Además, como todos los campos eran fechas, en el sistema de Costos se hacían operaciones del tipo:

&FechaInicial <= Fecha and Fecha <= &FechaFinal 

Como &FechaInicial era 01-01-2024, por ejemplo, no tomaba en cuenta el movimiento que quedaba como 31-12-2023.

También había un programa que borraba movimientos y los volvía a importar. Como había movimientos que quedaban con fecha del último día del mes a las 21:00:00, esos registros no se borraban y podían duplicarse.

En fin, un gran lío por un pequeño descuido.

Había programado controles para revisar si los movimientos en Contabilidad y Costos estaban correctos y no faltaba ninguno. El sistema recuperaba y grababa los movimientos de forma coherente, por lo que no daba diferencias, pero no era lo real. El problema era que si en un mes no había asientos el primer día o el último día, también los totales coincidían con lo que había en la base

Aprendizajes

  1. No hay migraciones sencillas; en todas hay que revisar mucho las propiedades.
  2. Siempre tener pruebas unitarias de tus servicios.
  3. Siempre tener pruebas de integración entre las aplicaciones.
  4. No hay migraciones fáciles (sí, está repetida, pero es para que lo recuerdes).
  5. El manejo de timezone siempre trae problemas, no importa cuando leas esto. 

Comentarios

Entradas más populares de este blog

La nefasta influencia del golero de Cacho Bochinche en el fútbol uruguayo

Aplicación monolítica o distribuida?

Funcionalidades de GeneXus que vale la pena conocer: DATE Constants.