SURTURE
Cacher ou ne pas cacher, telle est la question :
S'il est utile à l'esprit de souffrir
Les frondes et les ah-rows de l'attente scandaleuse,
Ou de prendre les armes contre une mer de rangs
Et en s'opposant les cache. Mourir, dormir, ou pas.

VUE D'ENSEMBLE
Je suis passionné par les caches depuis le milieu des années 2000, lorsque je travaillais sur un grand projet de middleware dont l'expérience utilisateur était insupportable en raison d'un pilote ODBC extrêmement lent. C'était bien sûr avant l'avènement des applications cloud. La solution mise au point consistait à introduire une couche de cache dans le middleware, placée entre l'application cliente et le serveur de base de données.
Nous sommes passés d'un temps de réponse de plusieurs secondes à quelques dizaines de millisecondes, soit une amélioration d'un facteur cent. Le cache utilisé (Ehcache) et le middleware qui l'exploite sont toujours en service aujourd'hui.
Lorsque je parle de mise en cache, je ne fais pas référence à l'utilisation normale de PXCache utilisée dans les graphiques, mais plutôt au mécanisme de mise en cache dit " Slot" qui se trouve dans la classe PX.Data.PXDatabase. Si vous parlez à certains développeurs, beaucoup vous diront "ne faites jamais ça" ou "ce n'est pas recommandé". Je ne suis pas d'accord avec ces affirmations, d'autant plus que le code Acumatica prêt à l'emploi utilise beaucoup de ces stratégies de mise en cache. C'est encore plus souvent le cas avec l'utilisation du PXSelectorAttribute.
CE QU'IL FAUT METTRE EN CACHE ET CE QU'IL NE FAUT PAS METTRE EN CACHE
Avant d'envisager la mise en cache des données, il est important de savoir s'il est approprié de le faire ou non. Les lignes de données ne sont pas toutes identiques. Nous allons donc voir ce qu'elles sont et si elles sont de bonnes candidates à la mise en cache. Voici quelques types de données auxquels nous pourrions penser :

RAISONS DE METTRE EN CACHE
- Les données sont souvent nécessaires et ne changent pas beaucoup
- Un petit sous-ensemble de champs est nécessaire pour toutes les lignes d'un grand tableau.
- Une grande liste de classes (types) est recherchée dans la liste des assemblages et ne changera pas après le démarrage du système. Par exemple : la liste des PXGraphes ou la liste des processeurs implémentant une interface particulière.
- Pas d'accès facile à un graphique ou trop coûteux pour lire les données
- Lorsque plusieurs lectures sont nécessaires pour trouver une correspondance appropriée (sur la base d'une cartographie multi-champs)
- Lorsque de petits ensembles de données sont nécessaires dans le cadre d'un traitement à haut débit à la milliseconde.
CADRE B2B/EDI
Au cours du développement d'une infrastructure B2B/EDI pour l'un de nos clients Acumatica, nous avons identifié de nombreux cas de figure où la mise en cache s'avérerait utile. L'infrastructure que nous avons développée sert à synchroniser des données et des documents provenant de sources externes vers Acumatica, ou inversement. Elle utilise de nombreuses données de configuration qui, une fois définies, ne sont généralement pas modifiées avant longtemps.
L'approche que nous avons adoptée nous a permis de créer de nombreux petits processeurs de messages/données, tous appelés dans un ordre précis. Ces nombreux processeurs sont généralement spécialisés dans une seule et unique tâche, à l'image des chefs dans une cuisine, où l'un est chargé des grillades, l'autre des sauces, un autre des salades, un autre des pâtisseries, un autre encore de la découpe de la viande, etc. Les processeurs sont sans état et sont installés (à l'aide de la découverte par réflexion) lors de la publication de la personnalisation ; ils sont liés à une petite ligne de configuration.
Dans le cadre du traitement des messages, nous utilisons également de nombreuses transformations et mécanismes de conversion des données, qui sont tous configurables par l'utilisateur.
Compte tenu du volume considérable de transactions et de transformations, nous avions besoin d'un moyen d'appeler ces processeurs/conversions à un rythme soutenu sans avoir à supporter le coût des requêtes sur la base de données.
De plus, pour les définitions des modèles de messages sortants, nous avons dû extraire les différents tableaux et champs utilisés par les graphiques du système afin de pouvoir les analyser rapidement et générer un modèle de message sortant, qui constitue le moteur principal des transactions sortantes.
LE MÉCANISME DE LA FENTE
Dans ce cas, le mécanisme de mise en cache est celui de la classe PX.Data.PXDatabase. Quelle est la particularité du mécanisme de mise en cache des créneaux :
- Stocke vos données dans des dictionnaires sécurisés
- Sstocke les données en fonction de la clé que vous avez définie, ce qui vous permet de restreindre l'accès à vos données mises en cache.
- Peut stocker et extraire des données de manière sélective à l'aide d'un paramètre (une classe/un type pour représenter et accéder à un sous-ensemble de données).
- Stockage des données par entreprise (chaque entreprise a son propre cache)
- Appelle automatiquement un délégué Prefetch lors du premier accès aux données mises en cache
- Surveille automatiquement les tables dépendantes (que vous configurez) et réinitialise le cache lorsque quelqu'un a mis à jour l'une des tables dépendantes.
- Gère automatiquement le mode cluster (utilisation de tables dépendantes à travers les clusters).
Examinons les différentes méthodes utilisées dans ce cas précis dans notre code :
GIST : https://gist.github.com/ste-bel/45a9e58f89054a70a76520137e825322
TABLES CONCERNÉES
Dans la section suivante, nous examinerons certaines des tables impliquées dans la mise en cache du B2B Framework.

LE CODE
Un POCO pour stocker/organiser mes données
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
Une classe d'aide pour stocker et rechercher des données
Deuxièmement, je crée une classe d'aide pour simplifier le chargement du code et la recherche des données. Je peux également encapsuler et améliorer mon algorithme de recherche sans perturber le reste du code. Vous pouvez considérer ce morceau de code comme un seau de données. Vous pouvez également utiliser une hiérarchie d'aides/de seaux afin d'accélérer les recherches et de réduire votre couplage global avec le code utilisant l'aide initiale.
GIST : https://gist.github.com/ste-bel/26088249b9b584fce94af07631c7393c
Le chargeur de données des créneaux horaires
Le dernier morceau de code consiste à créer une classe implémentant IPrefetchable pour lire les données en utilisant les méthodes de PXDatabase sans avoir besoin d'un graphique. J'utilise également un dictionnaire pour stocker mes helpers/buckets et subdiviser mes données dans ce cas par une clé telle qu'un ConversionID ou un ClassID.
Dans ma méthode Prefetch, je vide mon dictionnaire d'aides, puis je lis les données et je remplis mon dictionnaire avec toutes les aides.
Ensuite, je fournis quelques méthodes statiques pour récupérer les aides par leur ID et j'utilise l'aide pour rechercher des données.
GIST : https://gist.github.com/ste-bel/c3d8a380c88d5ff71fbc6102d4d9539a
L'utilisation du chargeur de données
Pour utiliser le chargeur de données, il suffit de parler à votre chargeur pour obtenir l'aide, puis d'utiliser l'aide pour convertir les données.
GIST : https://gist.github.com/ste-bel/96f6b77a93c14ed8cd70db21b4bfab92
CONCLUSION
Au début, la mise en cache et la récupération des données peuvent sembler un travail considérable. Cependant, une fois que l'on s'y est habitué, on peut réaliser ce type de code en une demi-heure et l'amélioration de la vitesse est considérable. En utilisant les classes Yaql (Yet another Query Language), vous pouvez également lire plus d'une table et utiliser des conditions complexes.
Voir cette GIST pour plus de détails : https://gist.github.com/ste-bel/a27c51dd4234d88c7d098b161b3d9386
Je vous souhaite bonne chance dans vos efforts de mise en cache et bon codage !