¿Cachear o no cachear?

Stéphane Bélanger | 2 de diciembre de 2022

OVERTURE

Cachear o no cachear, esa es la cuestión:
Si 'tis usuario en la mente a sufrir
Las hondas y ah-rows de espera indignante,
O tomar las armas contra un mar de filas
Y oponiéndose a ellas Morir, dormir, o no.

¿Cachear o no cachear?

VISIÓN GENERAL

He estado fascinado por las cachés desde mediados de la década de 2000, cuando trabajaba en un gran proyecto de middleware que sufría una UX insoportable debido a un controlador ODBC muy lento. Esto fue, por supuesto, antes de la llegada de las aplicaciones en la nube. La solución que se ideó fue introducir una capa de caché en el middleware entre la aplicación cliente y el servidor de bases de datos. Pasamos de un tiempo de respuesta de varios segundos a decenas de milisegundos, una mejora centuplicada. La caché utilizada (ehcache) y el middleware que la utilizaba siguen utilizándose hoy en día.

Cuando hablo de caché, no me refiero al uso normal de PXCache que se utiliza en los gráficos, sino al llamado mecanismo de caché Slot que se encuentra en la clase PX.Data.PXDatabase. Si hablas con algunos desarrolladores, muchos te dirán que "nunca hagas eso" o "que no es recomendable". No estoy de acuerdo con estas afirmaciones, sobre todo porque el código out of the box de Acumatica utiliza muchas de estas estrategias de almacenamiento en caché. Esto es aún más frecuente con el uso del PXSelectorAttribute.

QUÉ ALMACENAR EN CACHÉ Y QUÉ NO

Antes de pensar en almacenar datos en caché, es importante saber si conviene hacerlo o no. Las filas de datos no son todas iguales. Por lo tanto, vamos a discutir lo que son y si son buenos candidatos para ser almacenados en caché. He aquí algunos tipos de datos en los que podríamos pensar:

¿Cachear o no cachear?

RAZONES PARA ALMACENAR EN CACHÉ

  • Los datos se necesitan a menudo y no cambian mucho
  • Se necesita un pequeño subconjunto de campos para todas las filas de una tabla grande
  • Una gran lista de Clases (Tipos) son buscados en la lista de ensamblados y no cambiarán después del arranque del sistema. Ergo: la lista de PXGraphs o una lista de procesadores que implementan una interfaz particular.
  • No es fácil acceder a un gráfico o resulta demasiado costoso hacerlo para leer los datos
  • Cuando se necesiten varias lecturas para encontrar una coincidencia adecuada (basándose en un mapeo de varios campos)
  • Cuando se necesitan conjuntos de datos pequeños en el procesamiento de alta velocidad en milisegundos.

MARCO B2B/EDI

Durante el desarrollo de un marco B2B/EDI para uno de nuestros clientes de Acumatica, acabamos encontrando muchas áreas adecuadas que se beneficiarían del almacenamiento en caché. El framework que desarrollamos se utiliza para sincronizar datos y documentos de fuentes externas a Acumatica o viceversa. Utiliza muchos datos de configuración que, una vez configurados, lo más probable es que no cambien en mucho tiempo. El enfoque que adoptamos nos permitió crear muchos pequeños procesadores de mensajes/datos, todos llamados en una secuencia determinada, y estos numerosos procesadores suelen estar especializados en hacer una cosa y sólo una cosa, de forma similar a los chefs en una cocina, donde uno se encarga de asar, otro de la salsa, otro de las ensaladas, otro de la repostería, otro de cortar la carne, etc. Los procesadores no tienen estado y se instalan (mediante Reflection discovery) durante la publicación de la personalización y están vinculados a una pequeña fila de configuración. En el contexto del procesamiento de mensajes, también utilizamos numerosas transformaciones y mecanismos de conversión de datos, todos ellos configurables por el usuario.

Dado el enorme volumen de transacciones y transformaciones, necesitábamos una forma de llamar a esos procesadores/transformaciones en rápida sucesión sin incurrir en el coste de las consultas a la base de datos. Además, para las definiciones de plantillas de mensajes salientes, necesitábamos extraer las distintas tablas y campos utilizados por los gráficos del sistema para poder leerlos rápidamente y generar una plantilla saliente, que es el motor clave de las transacciones salientes.

EL MECANISMO DE RANURA

El mecanismo de almacenamiento en caché en este caso es el que se encuentra en la clase PX.Data.PXDatabase. Qué tiene de especial el mecanismo de almacenamiento en caché de ranuras:

  • Almacena tus datos en diccionarios a prueba de hilos
  • Stores los datos por su clave definida para que pueda restringir quién accede a sus datos almacenados en caché
  • Puede almacenar y recuperar datos de forma selectiva utilizando un Parámetro (una clase/tipo para representar y acceder a un subconjunto de los datos).
  • Almacena los datos por empresa (cada una tiene su propia caché)
  • Llama automáticamente a un delegado Prefetch en el primer acceso a los datos almacenados en caché.
  • Supervisa automáticamente las tablas dependientes (que usted configura) y restablece la caché cuando alguien ha actualizado una de las tablas dependientes.
  • Maneja automáticamente el modo cluster (usos de tablas dependientes entre clusters).

Veamos los distintos métodos utilizados en este caso concreto en nuestro código:

GIST: https://gist.github.com/ste-bel/45a9e58f89054a70a76520137e825322

MESAS IMPLICADAS

En la siguiente sección, echaremos un vistazo a algunas de las tablas implicadas en el almacenamiento en caché del marco B2B.

¿Cachear o no cachear?

EL CÓDIGO

Un POCO para almacenar/organizar mis datos

First, I create a POCO to store my cached data.  Acumatica does not recommend creating constructors in DACs and they are quite heavy in nature.  Therefore, I prefer to use my own lightweight POCOs.  I also implement IEquatable<> and override GetHashCode() so that my searches are more efficient.

GIST: https://gist.github.com/ste-bel/cd07f0bc9810b11ead1aafa818606765

Una clase auxiliar para almacenar y buscar datos

En segundo lugar, creo una clase auxiliar para simplificar la carga de código y la búsqueda de los datos. También puedo encapsular y mejorar mi algoritmo de búsqueda sin molestar a otro código. Puedes ver este trozo de código como un cubo de datos. También puede utilizar una jerarquía de ayudantes / cubos con el fin de burbujear las búsquedas y reducir su acoplamiento global del código utilizando el ayudante inicial.

GIST: https://gist.github.com/ste-bel/26088249b9b584fce94af07631c7393c

El cargador de datos de ranuras

La última pieza de código que crear una clase que implementa IPrefetchable para leer los datos mediante el uso de los métodos PXDatabase sin necesidad de un gráfico. También uso un diccionario para almacenar mis helpers/buckets y subdividir mis datos en este caso por alguna clave como un ConversionID o un ClassID.

En mi método Prefetch, borro mi diccionario de ayudantes, luego leo los datos y lleno mi diccionario con todos los ayudantes.

Después, proporciono algunos métodos estáticos para recuperar helpers por su ID y utilizo el helper para buscar datos.

GIST: https://gist.github.com/ste-bel/c3d8a380c88d5ff71fbc6102d4d9539a

El uso del cargador de datos

Para utilizar el cargador de datos, sólo tiene que hablar con su cargador para obtener el ayudante y, a continuación, utilizar el ayudante para convertir los datos.

GIST: https://gist.github.com/ste-bel/96f6b77a93c14ed8cd70db21b4bfab92

CONCLUSIÓN

Al principio, puede parecer mucho trabajo cachear y recuperar datos. Sin embargo, una vez que te acostumbras, puedes hacer este tipo de código en media hora y la mejora de la velocidad es considerable. Utilizando las clases Yaql (Yet another Query Language), también puedes leer más de una tabla y utilizar condiciones complicadas. 

Consulte este GIST para más detalles: https://gist.github.com/ste-bel/a27c51dd4234d88c7d098b161b3d9386

Te deseo lo mejor en tus proyectos de caching y ¡feliz codificación!

Autor del blog

La carrera de Stéphane, que abarca más de 25 años, comenzó como desarrollador de ERP en un lenguaje 4GL llamado Miracle. Al cabo de unos años, le enviaron a Filadelfia para trabajar con Weyerhaeuser con un contrato de 10 días que duró 4 años, donde ayudó a rediseñar los módulos de Transporte y EDI. Creó, entre otras, casi una docena de nuevas transacciones EDI. A continuación, se adentró en el desierto de las personalizaciones y el middleware Java en busca de su ERP Graal. En 2016, fue contratado por el estudio de videojuegos Behaviour Interactive, donde seleccionó, implementó e integró un ERP con otras aplicaciones en la nube. ¿Qué ERP? Acumatica por supuesto. En 2018, decidió volver a sus raíces como desarrollador de ERP y empezó a trabajar para partners Gold de Acumatica de primer nivel para compartir sus conocimientos, su pasión y sus ideas. Ha sido feliz desde entonces.

Reciba las actualizaciones del blog en su bandeja de entrada.