Amélioration performance des développements ERP
- 1 Introduction
- 1.1 RecordSQL
- 1.1.1 RecordSQL <Join01> vs <LeftJoin>
- 1.1.2 RecordSQL Case
- 1.1.2.1 Exemple :
- 1.2 XHQL
- 1.3 Collate
- 1.4 Mode liste du zoom
- 1.4.1 Risque d'incompatibilité
- 1.5 XRecordSQLCheck
- 1.6 XPerf
- 1.7 Pool de tâches
- 1.8 Index conditionnels du serveur de base de données
- 1.1 RecordSQL
Introduction
De nombreux leviers permettent d'améliorer les performances des applications développées pour Harmony Power Foundation. Certains ne nécessitent aucun développement particulier, tels le pool de tâches ou l'utilisation des index conditionnels natifs de la base de données, d'autres sont des outils de diagnostic des causes de performances dégradées, tels les utilitaires XRecordSQLcheck et Xperf, d'autres enfin sont des techniques de programmation à appliquer pour bénéficier au maximum de la puissance des moteurs de base de données.
Ce document présente ces différents leviers. Leur mise en oeuvre améliore les performances, notamment lors de migration à partir d'anciennes versions.
Les manuels de référence d'Harmony et de programmation contiennent une description exhaustive des points abordés ci-dessous.
RecordSQL
Le RecordSql est l'objet qu'il convient de mettre en oeuvre pour accéder à la base de données avec des performances optimales. En effet, les fonctions « classiques » de gestion de fichiers (Hread, Hseek, etc.) accèdent à la base de manière beaucoup moins performante que les accès offerts par les méthodes des RecordSQL.
Il s'agit d'une manière fondamentalement différente d'envisager l'accès aux données :
L'accès par les fonctions de gestion de fichiers concerne une seule table à la fois et nécessite autant de requêtes vers le serveur qu'il y a de tables à traiter. Dans la plupart des cas, le filtrage des informations s'effectue côté client.
Le RecordSQL, par la mécanique des jointures, n'effectue qu'un seul accès au serveur pour récupérer les informations de toutes les tables concernées. De manière très simple, le filtrage est réalisé sur le serveur et seules les informations strictement nécessaires sont rapatriées vers le client.
Autre avantage, cette méthode a pour corolaire une programmation beaucoup plus simple et plus lisible de l'accès aux données.
Un RecordSQL comporte deux aspects :
C'est une structure de données, utilisable dans les programmes Diva et les masques de la même
manière que les Record.Il inclut des méthodes d'accès à la base de données. Par exemple, Facture.Select(), où Facture représente une instance du RecordSQL de la facture.
Les RecordSQL sont décrits dans des dictionnaires de RecordSQL. Les sources des dictionnaires ont l'extension .dhsq. Les dictionnaires sont compilés et les objets résultants ont l'extension .dhoq.
Le source d'un dictionnaire est décrit dans un langage à balises.
Chaque RecordSQL a la structure générale d'un ordre Select du langage SQL et l'on y retrouve les balises <Select>, <From>, <Join>, <Where>, <GroupBy>, <Having>, <Orderby>.
Le RecordSQL permet de lire les données (Select), de les modifier (Update), de créer (Insert) ou de supprimer (Delete) des lignes.
RecordSQL <Join01> vs <LeftJoin>
La balise <Join01>, massivement utilisée dans les premières versions des RecordSQL de l'ERP, a été remplacée par des balises <LeftJoin> dans la version 7.2a lorsque cela était possible (le fonctionnement des deux balises n'étant pas strictement équivalent).
Contrairement à une jointure classique, <Join01> génère une sous-requête pour chaque champ sélectionné dans la balise <Select>. Elle est donc moins performante.
RecordSQL Case
La syntaxe de certaines jointures a également été revue pour utiliser le mot clé Case en remplacement d'une expression avec l'opérateur OR. La nouvelle expression s'exécute ainsi beaucoup plus rapidement sur le serveur SQL. De manière générale, il vaut mieux utiliser un Case plutôt que l'opérateur or.
Exemple :
(T000.TabTyp(16) = 1 AND Langue.Dos = PRO.Dos) OR (T000.TabTyp(16) = 2 AND Langue.Dos = 999)
devient
Langue.Dos = CASE T000.TabTyp(16) WHEN 2 THEN 999 ELSE PRO.Dos END
XHQL
Le moteur XHQL permet d'accéder à la base de données par un RecordSQL, ce qui améliore très sensiblement les performances grâce à la gestion des jointures (qui remplacent les recherches dans le module de traitement) et des tris.
Pour cela, l'instance du RecordSQL définie dans le module de traitement doit porter le nom du paramètre d'appel (HQL.Enreg) précédé de _.
Exemple : Paramètre d'appel = ART RecordSQL = _ART
L'instance du RecordSql Article doit être nommée _ART et doit être passée en paramètre dans hql.enreg avec la valeur "ART".
Dans ce cas, le moteur ne lit plus le fichier par des HRead, mais effectue un ReaderSelect puis parcourt le RecordSQL par la méthode ReaderNext.
Collate
Une série de fonctions, préfixées par Collate, réalisent l'interclassement de RecordSQL. Elles ont pour effet d'améliorer les performances de façon très significative dans un certain nombre de cas.
L'algorithme de l'édition du grand livre des stocks en est un exemple emblématique et illustre l'usage des fonctions d'interclassement et le gain de performance qu'elles peuvent apporter.
En effet, le grand livre des stocks imprime, pour chaque article, le stock et sa valorisation pendant une période. Pour cela, il doit connaitre le stock à ce jour, les entrées-sorties de stock, ainsi que les entrées forcées jusqu'à la période concernée.
L'algorithme sans les fonctions d'interclassement consiste à parcourir la table des articles puis, pour chaque article, à déterminer le stock à ce jour, les entrées-sorties et les entrées forcées de la période.
Supposons que la table article comporte 10 000 articles. Avec cet algorithme, il faudra donc exécuter :
un select sur la table article
pour chaque article :
-un select pour déterminer le stock à ce jour ;
-un select pour déterminer les entrées-sorties ;
-un select pour déterminer les entrées forcées.
Soit au total 30 001 select (1+ 10 000 + 10 000 + 10 000).
L'algorithme avec les fonctions d'interclassement consiste à déterminer le stock à ce jour, les
entrées-sorties et les entrées forcées de la période respectivement par 3 select, puis d'interclasser les résultats obtenus. Les 3 select seront obligatoirement triés selon le même critère.
Il faut donc au total 3 select pour obtenir le même résultat.
L'application de cet algorithme sur une base de test a permis de passer de 1h 50 mn à 4 mn pour obtenir la même édition.
Mode liste du zoom
Une amélioration très importante du zoom a consisté à découpler la requête exécutée pour garnir le tableau du mode liste de celle exécutée pour garnir la fiche. Pour la fiche, il est en effet nécessaire de disposer de l'ensemble des informations, alors que pour la liste, seules les informations visibles par l'utilisateur sont nécessaires (à part éventuellement quelques informations techniques).
La génération de la requête du mode liste tient compte des champs choisis par l'utilisateur pour garnir son tableau.
Il fallait cependant continuer à utiliser le même RecordSQL pour le mode liste et pour la fiche. C'est pourquoi celui-ci est devenu « à géométrie variable » et permet désormais de désactiver (ou d'activer) des colonnes de la sélection, ainsi que des jointures.
Cette optimisation est entièrement prise en compte par le zoom, à condition que le RecordSQL soit paramétré en conséquence. Pour assurer la compatibilité avec l'existant, le zoom fonctionne dorénavant selon deux modes :
Le mode « compatible », pour lequel rien ne change par rapport aux versions précédentes (mais qui ne bénéficie pas des nouvelles performances).
Le mode « optimize » qui met en oeuvre les nouvelles fonctionnalités. C'est l'attribut ZoomOptimize de la balise RecordSQL qui indique au zoom que le RecordSQL a été revu pour être utilisé dans ce nouveau mode.
Exemple : <RecordSql Name=Article Comment='Articles' ZoomOptimize=yes>
Remarque : Cette optimisation est également prise en compte par le Zoom Liste (nom de code ComboZoom) dans les même conditions que le zoom standard.
Risque d'incompatibilité
Les principaux Zooms de l'ERP Divalto sont passés dans le mode « Optimize ». Dans ce mode, le moteur du zoom, lors de la phase de remplissage de la liste, n'appelle plus la procédure ZoomApresRead mais ZoomApresReadListe si elle existe.
En conséquence, si vous avez surchargé ZoomApresRead, il faut impérativement adapter vos surcharges pour éventuellement reporter des traitements dans la procédure ZoomApresReadListe. On lira avec profit le chapitre « Optimisation d'un RecordSql pour le ZoomSql » de la documentation du développeur.
XRecordSQLCheck
L'utilitaire XRecordSQLcheck.exe est un outil indispensable pour le développeur de RecordSQL. Il permet de : -Contrôler la syntaxe SQL des requêtes. -Vérifier que les expressions des jointures utilisent les colonnes correspondant à un index de la table jointe, ce qui a une influence énorme sur les performances.
L'outil permet de vérifier : -Tous les dictionnaires d'un répertoire. -Tous les RecordSQL d'un dictionnaire. -Un RecordSQL particulier d'un dictionnaire. -Une requête particulière.
Contrôle de la syntaxe SQL
La compilation d'un dictionnaire de RecordSQL ne vérifie pas la syntaxe du langage SQL. Celle-ci n'est contrôlée qu'au moment de l'exécution de la requête. Un dictionnaire de RecordSQL comporte généralement de nombreux RecordSQL qui correspondent eux-mêmes à de nombreuses requêtes (chaque condition et chaque ordre de tri correspondent à une requête différente). L'outil permet de vérifier toutes les requêtes d'un RecordSQL. Pour cela, il se connecte au moteur de la base de données cible.
Vérification des expressions de jointures
Le langage de requêtes SQL est très permissif, en particulier au niveau de l'expression des conditions de jointure des tables. Cette expression peut avoir des conséquences calamiteuses sur les temps de réponse (de plusieurs dizaines de secondes avec une expression inadaptée à quelques millisecondes avec une expression correcte).
Dans la description relationnelle des tables, une clé étrangère d'une table doit correspondre à la clé primaire de la table liée. Cependant, le langage SQL n'effectue aucune vérification. L'utilitaire XRecordSQLCheck permet d'effectuer cette vérification.
Intégration de xRecordSqlCheck dans Xwin7
XrecordSqlCheck est intégré dans Xwin7. Au clic droit sur un dictionnaire de RecordSql, le choix «
Vérifier la syntaxe SQL » apparaît :
Le résultat de la vérification garnit la liste des erreurs. Un double-clic sur l'erreur se positionne sur la ligne concernée du RecordSQL.
Pour effectuer la vérification, Xwin utilise les implicites SQL pour se connecter au serveur de base de données. L'utilisation des environnements dans le profil du projet permet de s'assurer que la vérification se connecte à la bonne base de données.
Cas de la surcharge :
La vérification teste automatiquement la base et sa surcharge. La base étant réputée correcte, on lancera la vérification à partir du dictionnaire de surcharge.
Remarque : Si cette vérification est lancée à partir du dictionnaire de base et que le dictionnaire de surcharge comporte des erreurs, le double-clic sur l'erreur ne fonctionne pas car il se positionne sur le bon numéro de ligne du mauvais fichier.
XPerf
Xperf est un puissant outil permettant d'analyser puis d'améliorer les performances d'un programme. L'expérience montre qu'il est difficile de trouver rapidement les causes de la lenteur d'un programme et de pointer les fonctions qui ont consommé beaucoup de temps. Comme Xperf nous donne le temps passé dans chaque fonction du programme, l'identification des problèmes de performances devient chose aisée.
L'analyse d'un programme se fait en deux temps :
-le programme est exécuté tout à fait normalement mais sous contrôle de l'analyseur. A la fin du programme, un rapport est créé dans un fichier.
-les résultats sont visualisés avec le programme Xperf.dhop. Ce programme affiche le temps passé dans chaque fonction, avec la possibilité de consulter la pile et la liste des fonctions appelées par une fonction.
Pool de tâches
Pour réduire le temps de chargement des programmes, Harmony gère un pool de tâches (xrtdiva.exe côté serveur et xwpf.exe côté client) qui restent résidentes.
Harmony anticipe un chargement ultérieur et conserve ainsi toujours au moins une tâche libre en avance dans le pool.
Exemple : -Le chargement du menu Divalto crée une tâche supplémentaire dans le pool, qui sera utilisée par le premier choix du menu lancé.
-Le chargement d'un zoom à partir du menu Divalto utilise cette tâche disponible du pool. Il constate qu'il utilise la dernière tâche disponible et crée une nouvelle tâche dans le pool.
-A la sortie du zoom, xrtdiva.exe reste chargé en mémoire, en attente d'une nouvelle demande d'exécution.
-Un nouvel appel d'un zoom à partir du menu (le même, un autre ou même un autre programme) vérifie s'il existe une tâche disponible dans le pool.
L'appel d'un zoom lorsqu'une tâche est disponible dans le pool est ainsi beaucoup plus rapide que le tout premier appel.
La création d'un pool est liée à l'appel d'un menu. Tous les programmes ou zooms appelés depuis ce menu sont rattachés à ce pool. Le pool est identifié par un identifiant unique et contient au maximum 2 tâches en attente. Xconsole permet de visualiser les tâches inactives (si l'option « Filtrer les tâches inactives des pools » est décochée) :
La colonne « Pool Id » indique le numéro du pool. La colonne « Pool en attente » contient « A » pour les tâches du pool en attente. Dans l'exemple ci-dessus, la tâche 53 est en attente. Elle a servi précédemment au zoom « Articles ».
Index conditionnels du serveur de base de données
Dans le cadre de l'optimisation des accès à la base, Harmony et XLAN utilisent désormais les index conditionnels de la base de données (fonctionnalité introduite par Microsoft à partir de SQL Server 2008).
Pour des raisons de compatibilité, cette fonctionnalité est mise en oeuvre par le choix « Mise à jour index conditionnels » de l'utilitaire Xpsql ou par un pilotage de ce dernier.
Cette mise à jour effectue les opérations suivantes :
-Suppression des tables liées aux index conditionnels.
-Suppression des triggers permettant la mise à jour de ces tables.
-Création des nouveaux index conditionnels directement sur la table cible.
Tant que cette mise à jour n'est pas effectuée, XLAN continue à utiliser la table des index conditionnels.