memoire-master/chapters/réalisation.typ

147 lines
24 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#import "../template.typ" : *
#pagebreak(weak: true)
= Réalisation
== Introduction
Je présente dans cette section le travail réalisé durant les neuf mois au sein de l'entreprise Unit Solutions. Portant sur le sujet d'une migration de base de données dans le but de maintenir les technologies de l'application à jour, je présente tout d'abord les différents éléments qui justifient le choix d'effectuer cette migration de base de données, par la suite, j'expose l'analyse préliminaire réalisée pour s'assurer que la migration reste pertinente, j'aborde plus tard les changements réalisés dans le code et enfin, je présente les résultats obtenus post-migration.
== Pertinence et philosophie
Lorsque le sujet de mettre à jour le SGBDR a été présenté, il n'a pas été décidé de remplacer PostgreSQL. Comme expliqué précédemment, PostgreSQL excelle dans la gestion de données géographiques et attributaires, il est complet et surtout très bien maintenu par ses développeurs. Ces différentes raisons en font un candidat parfait pour l'environnement InfSuite plus que tout autre système proposé par la concurrence.\
Maintenir PostgreSQL en tant SGBDR de l'application écarte déjà un bon nombre de solutions niveau logiciel. Je peux dores et déjà écarter les logiciels payants et long d'expertises autres que PgAdmin pour effectuer cette migration qui reste le logiciel le plus adéquat puisque nous ne cherchons pas à changer d'environnement.
L'application InfSuite souhaite utiliser une fonctionnalité important de PostgreSQL: Les ``` timestamps```, ou horodatages en français.
En effet, InfSuite utilise un système de version basé sur de l'horodatage. Quand un utilisateur met à jour les informations d'un document dans l'application (inspection, restauration, etc.) alors, il créait une nouvelle version du document.\
Si deux utilisateurs récupèrent la même version d'un document, qu'ils effectuent tous deux des mises à jour sur ce document, que l'utilisateur A sauvegarde ses changements en premier, que l'utilisateur B essaye de sauvegarder le document avec ses changements après que la version ait changé, alors le serveur lui signalera qu'il ne possède pas la dernière version du document qui a été modifiée entre temps et donc qu'il ne peut pas sauvegarder ses modifications sans mettre à jour la version de son document.\
Pour assurer ce suivi de version le document contient une version en base, qui est un ``` timestamp``` sans fuseau horaire. Un problème peut alors apparaître si deux utilisateurs, sur des fuseaux horaires différents, effectuent des modifications au même instant.\
Le serveur ne pourra pas vérifier l'information de fuseau horaire puisqu'elle n'est pas sauvegardée en base et donc si l'utilisateur effectue une sauvegarde même avec une ancienne version du document, cette dernière écrasera les derniers changements.
Dans le schéma @file-versionning-schema, deux utilisateurs génèrent des versions de fichiers différentes. Un utilisateur enregistre ses changements en premier et génère la version ``` AB``` du document. Quand le second utilisateur souhaite enregistrer ses changements ``` C```, un conflit de fichier l'en empêche. ``` C``` a comme origine la version ``` A``` et non pas ``` AB```, l'utilisateur doit donc récupérer les changements de la version ``` AB``` avant d'enregistrer ses changements.
#figure(
image("../assets/images/file-versionning.png"),
caption: [Schéma d'un conflit de version entre deux version d'un document]
)<file-versionning-schema>
De plus, un point important pour lequel le souhait de mettre à jour la base de données a été exprimé, est de pouvoir profiter des améliorations du #ref-glossary(term: "SGBDR")[SGBDR] PostgreSQL. Il y a par exemple des mises à jour de sécurité du système. Mettre à jour une application et ses différents composants permet de profiter des mises à jour de sécurité afin d'éviter des vulnérabilités qui pourraient mettre en péril les données des clients et l'intégrité de l'entreprise.\
Les vulnérabilités sont connues sous le nom de Common Vulnerabilities and Exposures et en prenant pour exemple la mise à jour pour corriger la faille ``` CVE-2023-5869```, PostgreSQL résout un problème permettant d'écrire des octets arbitraires en mémoire, qui facilitait l'exécution de code arbitraire. Normalement, les ORM permettent de faire automatiquement de la paramétrisation de requêtes pour prévenir d'injections potentielles. Mais il se peut que des failles supplémentaires se cachent dans le code et permettent donc, malgré tout, d'exploiter ces failles. Les corriger sur le système permet alors d'éliminer en grande partie le risque d'attaque.\
En complément des vulnérabilités, les mises à jour apportent des améliorations de performances cruciales. En théorie les ORM jouent un rôle essentiel dans les performances d'une application, car ils utilisent un cache pour éviter d'effectuer des requêtes vers la base de donnée à trop haute fréquence.\
Dans le cas de la base PostgreSQL, elle est aussi utilisée pour le système d'information GIS, il y a donc beaucoup de données à délivrer et avec un grand nombre de requêtes. Améliorer les performances permet de réduire la latence ressentie par l'utilisateur lorsqu'il cherche à charger des données dans l'application. Sur la version 16 de PostgreSQL, il est promis une amélioration de 10% des performances du #ref-glossary(term: "SGBDR")[SGBDR]. Un tel gain de performance premer d'améliorer grandement l'experience utilisateur.
Il faut maintenant déterminer quel est le type de migration le plus adéquat dans ce cas de figure pour éviter les coûts supplémentaires qui peuvent être facilement évités.\
Je dois dans un premier temps chercher les technologies utilisées dans l'application et m'assurer que ces dernières soient compatibles avec la nouvelle base. Par la suite, je vais devoir valider la compatibilité entre la version de la base source et de la base cible. Avec cette confirmation, je pourrais alors chercher la meilleure manière d'effectuer cette migration parmi les différentes options disponibles que j'ai pu exposer précédemment.
=== Analyse préliminaire
En regardant côté architecture, je cherche quelles sont les applications qui risquent de rencontrer un problème lors de la migration. Les seuls systèmes qui établissent une relation directe avec la base de données sont côté #ref-glossary(term: "Back-End")[back-end]. On limite donc l'impact sur l'application globale s'il y a des changements et des mises à jour à réaliser pour permettre à l'application de continuer à fonctionner avec la base de données.
Le projet #ref-glossary(term: "Back-End")[back-end] de l'application est développé en C\# avec .NET qui gère la partie web de l'application. Un certain nombre de librairies sont utilisées par le projet InfSuite pour permettre d'étendre les fonctionnalités de l'application sans devoir dépenser de temps de développement.
Il existe des paquets alimentés par Microsoft par exemple comme .NET, #ref-glossary(term: "EF")[#display-def("EF")], ... et d'autres librairies qui sont elles développés par des développeurs tiers qui ont souhaité ajouter une nouvelle fonctionnalité à l'écosystème.\
Quand on souhaite effectuer une mise à jour de l'application, l'environnement Microsoft nous propose d'effectuer la mise à jour de ces librairies nommées NuGet Packages dans l'écosystème C\# via NuGet, le gestionnaire de dépôts associé.
Il permet de faire du versionnement des librairies, sur le même principe que PostgreSQL qui utilise le concept de versionnement pour livrer ses mises-à-jour.\
Chaque version d'une librairie peut notifier des incompatibilités avec certaines versions d'autres librairies. Je dois donc étudier les composants qui permettent de faire la liaison à la base de données et également les librairies dont peut ou qui peuvent dépendre de ces composants.\
Il faut prêter attention à chaque composant mis à jour indépendamment qui pourrait causer d'autres problèmes de compatibilité avec l'application et casser des fonctionnalités.\
Un projet C\# dans l'environnement Microsoft est composé d'une solution, de sous-solutions et de projets.\
Les sous-solutions d'un projet sont des solutions indépendantes intégrées au projet. Un peu à la manière de librairies, je peux créer du code qui pourra être réutilisé dans différents projets indépendamment, sans devoir le dupliquer.\
Chaque projet possède ses propres NuGet packages, mais la solution peut gérer des dépendances, c'est-à-dire que le package sera ajouté à la liste des dépendances globales et sera installé dans tous les projets de la solution.
Dans mon cas, la solution #ref-glossary(term: "Back-End")[back-end] d'InfSuite est composé de 65 projets. Il y a le projet "WebApi" qui gère les routes et les interactions avec l'application #ref-glossary(term: "Front-End")[front-end] qui est disponible pour le client et qui dépend du projet "Server". Le projet "Server", lui, contient la logique de certaines interactions simples, par exemple la recherche d'IO via la barre de recherche dans l'application. Le projet "Server" dépend du projet "Core" qui gère certaines fonctionnalités plus poussées de l'application autant pour la partie web que pour d'autres composants. Il existe de nombreuses autres dépendances dans l'application qui complexifient la gestion des packages. Par exemple dans le diagramme @ik-webapi-dependencies, on remarque rapidement que les dépendances internes au projet peuvent être relativement complexes.
#figure(
image("../assets/images/ik-webapi-dependencies.png"),
caption: [Arbre de dépendances du projet WebApi d'InfSuite]
)<ik-webapi-dependencies>
Si je souhaite par exemple mettre à jour un package dans le projet "Geometry", tous les projets dépendants sont impactés par cette mise à jour, il faut donc s'assurer que rien ne vient casser le bon fonctionnement de l'application en profondeur. Je m'intéresse donc aux liens entre les différents projets ainsi qu'aux librairies que je souhaite mettre à jour pour m'assurer de leur compatibilité pour toute l'application.
=== Compatibilité et décision
La première étape est de valider la compatibilité entre la version 14 et 16 de PostgreSQL. Une première manière de valider cela est d'aller sur les notes de mise à jour sur le site officiel et de chercher les changements qui peuvent affecter la compatibilité entre les deux bases.\
Après avoir analysé en détail les changements il y a trois grandes catégories. Il y a l'impact sur l'administration système, celui sur les requêtes SQL et celui sur l'incompatibilité des données. La partie administration système n'est pas gérée par les développeurs d'InfSuite, elle est gérée par d'autres employés de l'entreprise, je peux donc me concentrer sur la partie requêtes et données.\
Pour la partie des données stockées en analysant les données actuelles je peux relever quelques changements qu'il faut surveiller:
- Les jsonb peuvent être utilisé par les systèmes d'informations géographiques pour stocker les informations géographiques et attributaires. Il faut donc surveiller le comportement de l'application à ce niveau.
- Les uuid qui ont subits des optimisations pour mieux gérer les identifiants uniques universels et qui est utilisé comme identifiants uniques pour les tables dans le projet.
- Les types numeric qui sont également utilisés dans l'application qui subisse des optimisations de performances.
Il n'existe pas d'autre changement majeur qui pourrait porter atteinte à l'intégrité des données lors de la migration.
Une fois la première étape validée pour l'intégrité théorique des données, il faut valider la seconde étape qui est la réalisation de la migration. Le seul moyen de s'assurer de la complète compatibilité entre les deux systèmes est d'effectuer une première migration dite "à blanc". Elle a pour but de mettre en place un système séparé de l'environnement de production, pour s'assurer de ne pas impacter l'application accessible par les clients.\
L'objectif de ce système est d'avoir une base de tests sur laquelle je peux m'assurer que l'application fonctionne toujours après la migration de données et que si des problèmes s'annoncent, il n'y ait aucun autre impact que de les constater.
Avec la validation obtenue précédemment, sur la compatibilité des deux systèmes, j'ai pu valider, sous l'approbation de mon chef de projet et de l'architecte d'application, que la migration la plus adaptée dans notre cas, serait une migration hybride.\
Le but de la migration hybride est de combiner deux types de migration de données que j'ai pu citer précédemment dans ce document. Comme je veux ajouter un fuseau horaire aux horodatages qui servent de versions, je dois modifier les données de la base source. Cependant, cela implique de modifier les données et les modèle de données associés.\
Pour éviter de devoir utiliser un logiciel tel que PgAdmin et de passer du temps à manipuler le modèle de données qui pourrait causer des problèmes, je préfère utiliser des outils bien maîtrisés et intégrés sur le projet InfSuite.
Pour l'import de données, cette tâche a été réalisée par un administrateur système de l'entreprise. Il a utilisé la méthode d'import d'un fichier #ref-glossary(term: "Dump")[dump] de la base source dans la nouvelle base. Je l'ai présenté ci-dessus, n'ayant aucun soucis de compatibilité sur les types de données, l'intégration de ces données s'est faite sans accros dans la base de test.
Pour mettre à jour le modèle de données de manière synchrone avec les mises à jour de l'application et éviter des soucis de compatibilités, l'environnement InfSuite utilise un système de fichiers scripts. Rédigés en SQL, ces scripts viennent mettre à jour le schéma de base de données et sont exécutés automatiquement par le serveur lors de la mise à jour de l'environnement ciblé (dev, staging, production, etc.).
Pour l'exemple des horodatages, je rédige un script SQL que je pourrais exécuter manuellement sur l'environnement de développement avec la nouvelle version de PostgreSQL et effectuer mes tests. Cependant, si je veux intégrer mes modifications sur les autres environnements, il faut que je sauvegarde mes changements sur Azure Devops, je n'ai pas la permission de faire ces changements manuellement.
Enfin, pour mettre à jour les données dans la base une fois le schéma de base modifié, je décide de faire cette migration en temps réel. La compatibilité entre les types de données ``` timestamp``` et ```timestamptz``` permet de garder en base l'ancien format de version, de mettre à jour le schéma de base puis de mettre à jour les données en y rajoutant le fuseau horaire. Comme la colonne "version" est utilisée à de nombreux endroits, créer un type de donnée côté serveur qui va s'occuper de les traiter rend la tâche moins complexe et chronophage. Avant de passer sur la réalisation, je dois m'assurer de la compatibilité des dépendances externes pour effectuer la migration.
Ce qui m'intéresse particulièrement ici, est de mettre à jour le package Npgsql car il permet de communiquer avec la base de données. Dans une application .NET, il y a la possibilité d'utiliser un #ref-glossary(term: "ORM")[#display-def("ORM")] pour simplifier les interactions avec les bases de données. Le projet InfSuite utilise historiquement l'#ref-glossary(term: "ORM")[ORM] #ref-glossary(term: "EF")[EntityFramework]. Il faut bien différencier #ref-glossary(term: "EF")[EntityFramework] qui n'est plus activement développé, de son successeur EntityFrameworkCore qui offre de nouvelles fonctionnalités qui ne seront plus implémentées dans #ref-glossary(term: "EF")[EntityFramework].
#ref-glossary(term: "EF")[EntityFramework] utilise le connecteur Npgsql pour effectuer les requêtes vers la base de données. En recherchant les versions disponibles de Npgsql disponibles pour EntityFramework, je me suis rendu compte qu'il fallait prêter attention à une fonctionnalité importante de PostgreSQL que le projet InfSuite veut maintenant exploiter : les horodatages avec des fuseaux horaires.\
La version post mise à jour de Npgsql était la version ``` 4.1.13``` et la dernière version disponible qui utilise les horodatages avec fuseau horaire est la version ``` 8.0.3```. En essayant de le mettre à jour avec cette version, je me suis rendu compte qu'#ref-glossary(term: "EF")[EF] n'était pas compatible avec la version ``` 8.0.3```. La dernière version du paquet Npgsql disponible pour #ref-glossary(term: "EF")[EF] étant la version ``` 6.4.3``` je dois donc me contenter de cette version.
Avant de valider l'utilisation de ce dernier dans toute l'application, je dois valider qu'elle supporte les horodatages avec fuseau horaire et qu'elle est également compatible avec le reste de l'application. En se référant à la documentation fournie, la première version à intégrer les horodatages avec fuseau horaire est bien la version ``` 6.X.X``` d'Npgsql, elle est donc compatible avec le besoin de sauvegarder les fuseaux horaires pour les versions.
Si on souhaite passer sur la nouvelle version du paquet Npgsql, la version ``` 8.X.X```, il n'y a pas d'autre choix que de passer d'EF à EFCore. Faire le choix de mettre à jour l'application EFCore permettrait de compléter l'objectif de maintenir l'application à jour et saine. En effet, pour rappel, EF n'est maintenant plus mis à jour régulièrement.\
L'étude d'impact des couts et des changements impliqués par une telle mise à jour a déjà été réalisé à priori de mon analyse et il en est ressorti que pour l'année courrante, un tel budget ne pouvait pas être accordé pour cette mise à jour. Si je veux mettre à jour l'application pour utiliser les dernières fonctionnalités disponibles sans passer par une mise à jour d'EF, le seul choix qu'il reste est de mettre à jour Npgsql vers la version ``` 6.X.X```.\
Le schéma @npgsql-versions-incompatibilty ci-dessous résume la situation d'incompatibilités entre les versions des trois éléments à savoir respectivement : à gauche Entity Framework, au centre le paquet Npgsql et à droite la base de données PostgreSQL.
#figure(
image("../assets/images/npgsql-versions-incompatibilty.png"),
caption: [Problématique d'incompatibilité entre les différents éléments permettant au serveur et à la BDD d'échanger des informations.]
)<npgsql-versions-incompatibilty>
=== Tests de performance
La dernière étape permettant de valider cette étape de migration est de s'assurer que la nouvelle version de PostgreSQL installée à de meilleures performances que la version remplacée. Pour valider cela, il a été développé un système en interne de test de performance. L'architecture complexe de la base et la relation avec des données géographiques et attributaires peut rendre les requêtes parfois plus longues d'exécution.
Pour effectuer un test de performance, le système utilise une fonctionnalité gourmande en ressource, les groupes. Dans l'environnement InfSuite, il est possible d'afficher sur la carte les objets d'infrastructure. Par défaut tous les objets d'un dataowner sont affichés. Pour permettre d'axer son travail sur des objets d'infrastructures particuliers, alors il est possible d'appliquer un filtre à toutes ces entités. Le mode "Groupe" de l'application permet de créer alors deux types différents de filtres.
Le premier type de filtres est un filtre statique. Peu gourmand en ressource et très simple d'utilisation, on peut y ajouter des entités manuellement et cela va permettre de n'afficher que les objets sélectionnés dans l'application. Fonctionnellement parlant ce n'est qu'une relation de base de données.
Le second type de filtre, plus complexe, est le filtre dynamique. Les filtres dynamiques permettent de gérer automatiquement les entités qui seront incluses ou exclues de l'affichage. Pour ce type de filtre, il est possible de créer des règles de filtrage avancées pour permettre à l'utilisateur plus de flexibilité.\
Un peu à la manière d'une requête SQL, il est possible de construire une requête qui va être exécutée pour permettre de retrouver les entités à partir de critères. Il est par exemple possible de demander de filtrer les ouvrages par position géographique en ne prenant en compte que ceux au-dessus d'une certaine latitude et de rajouter à ce filtre uniquement les ouvrages qui contiennent un certain numéro dans leur nom.\
Il est possible de créer des règles avec chaque propriété d'un objet d'infrastructure, d'appliquer un ordre pour les conditions, de faire des agrégats, des jointures,... Les utilisateurs ont la liberté de créer leur propre requête, une requête pouvant devenir rapidement complexe, elle peut donc prendre plus de temps à filtrer les objets d'infrastructures pour les retourner à l'utilisateur.\
Étant utilisé par les services GIS de l'application, s'assurer de la performance de ce système est primordial.
Ce que l'outil permet de tester est ce système de groupe/filtres. Il va chercher les différents filtres existants en base de données, peu importe le client qui a pu les créer, et les exécuter.\
Cette méthode permet de connaître les performances réelles de la base dans un cas pratique spécifique à l'application et non pas d'avoir des performances théoriques fournies par les développeurs du #ref-glossary(term: "SGBDR")[SGBDR] qui peuvent être tournées en leur faveur. Nous cherchons donc à savoir si le chiffre de 10% en gain de performances est réel ou non.
L'objectif est donc de comparer les performances de la base de production encore sous PostgreSQL 14, avec les performances d'une base de développement installée pour l'occasion, elle, sous PostgreSQL 16. Après avoir exécuté l'outil de benchmark sur les deux bases, je récupère les données brutes en sortie de programme pour les analyser. Ci-dessous un exemple de données de sorties fournies par le programme après execution.
```csv
eruid,description
batman,uses technology
superman,flies through the air
spiderman,uses a web
ghostrider, rides a motorcycle
#GROUP_OBJECT_PROFILE#accessgroupGroupProfile
cn,description
daredevil,this group represents daredevils
superhero,this group represents superheroes
#GROUP_OBJECT_PROFILE#aixaccessgroupGroupProfile
aixgroupadminlist,ibm-aixprojectnamelist
eadmins,eadmingroup
eguests,eguestgroup
```
Les données sont au format CSV, je peux ainsi les importer dans un logiciel tableur tel qu'Excel pour les manipuler et les comparer. L'application de production étant accessibles aux clients en continu, les données stockées peuvent changer très rapidement et présenter un delta avec les données sauvegardées à posteriori lors la mise en place de la base de développement.\
Cette différence doit être prise en compte dans la comparaison des données puisque je dois alors utiliser des fonctions plus poussées d'Excel pour retrouver les données similaires entre les deux tables et en exploiter les résultats.
#pagebreak(weak: true)
== Adaptation du code
Dans l'application, 24 projets au total utilisent le paquet Npgsql. Ces projets ne sont pas uniquement dédiés à la partie #ref-glossary(term: "Back-End")[back-end] de l'application, mais sont également des outils tiers développés pour des besoins spécifiques. Je peux par exemple citer l'outil IkCoordToRvg que j'ai pu aborder dans mon mémoire de licence, qui était le projet sur lequel j'ai pu développer durant ma troisième année de licence. Comme pour tout autre projet, il utilise des dépendances au projet Core d'InfSuite qui nécessite lui-même le paquet Npgsql. Lorsque j'ai tenté une première fois de mettre à jour le paque, jee me suis retrouvé confronté à de nombreuses erreurs
#pagebreak(weak: true)
== Résultats
=== Performances finales
=== Problèmes rencontrés