Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Echec à la restauration en cas de schéma référencé créé par une extension #17

Closed
alhyss opened this issue Aug 9, 2024 · 3 comments
Assignees
Labels
bug Something isn't working
Milestone

Comments

@alhyss
Copy link
Collaborator

alhyss commented Aug 9, 2024

Résumé : La restauration d'une base sur laquelle Asgard est active est très susceptible d'échouer lorsqu'une autre extension active sur la base crée un ou plusieurs schémas qui sont référencés dans la table de gestion d'Asgard.

Cette anomalie, détaillée ci-dessous a été détectée lors de la restauration d'une base PostgreSQL 10 avec l'extension PlumePg sur PostgreSQL 16, néanmoins elle ne semble pas propre à une version spécifique de PostgreSQL et paraît pouvoir concerner d'autres extensions dès lors qu'elles créent des schémas.

La table z_asgard_admin.gestion_schema est marquée comme table de configuration de l'extension, ce qui fait que ses enregistrements sont inclus dans les fichiers de sauvegarde et restaurés avec le reste des données de la base. Ceci permet de préserver des informations qu'il n'est pas possible de déduire de l'état de la base. Notamment quels schémas sont ou non référencés par Asgard, leurs rôles lecteurs et éditeurs s'ils en ont, leur arborescence de classement.

Cette commande de restauration des données est susceptible d'échouer si les enregistrements qu'elle insère dans z_asgard_admin.gestion_schema ne respectent pas les contraintes définies sur la table. Notamment, elle échoue si l'un des enregistrements correspond à un schéma qui apparaitrait déjà dans la table. L'erreur - non respect de l'unicité du champ clé primaire nom_schema - est capturée en amont par le déclencheur asgard_on_modify_gestion_schema_before, qui renvoie un message de la forme suivante :

TB9. Saisie incorrecte (schéma z_plume). Un schéma de même nom est déjà répertorié dans la table de gestion.

Le déclencheur sur évènements asgard_on_create_schema est activé par les commandes CREATE SCHEMA et référence automatiquement les nouveaux schémas créés dans la table de gestion. Ceci n'engendre pas de conflit avec les commandes CREATE SCHEMA exécutées au cours du processus de restauration, car celui-ci restaure les schémas avant de restaurer les extensions. Autrement dit, le déclencheur asgard_on_create_schema n'intervient pas lors de la restauration des schémas, car, tant que la commande CREATE EXTENSION asgard n'a pas été lancée, il n'existe pas encore. Ainsi, les seuls enregistrements ajoutés à la table de gestion relativement à ces schémas le sont lors de la restauration des données des tables de configuration, et - hormis pour les OID qui, comme il se doit, seront automatiquement actualisés - les informations restaurées correspondent exactement à celles qui se trouvaient dans la table lors de la sauvegarde.

Dans le cas général, il n'y a donc pas d'erreur.

Mais les schémas créés par les extensions faussent la donne, car une extension quelconque est très susceptible d'être restaurée après Asgard, et non avant comme les schémas autonomes. Le risque est d'autant plus grand que le nom "Asgard" commence par la lettre "A" et a toutes les chances d'être l'une des premières extensions restaurées.

Au moment où le processus de restauration arrive à la commande CREATE EXTENSION, il exécute les commandes qui se trouvent dans le script de l'extension, incluant les éventuelles commandes CREATE SCHEMA. Si Asgard a alors déjà été restaurée, le déclencheur asgard_on_create_schema est actif et va référencer les schémas dans la table de gestion à mesure de leur création. Si ces mêmes schémas se trouvaient être déjà référencés dans la table de gestion sur la base sauvegardée, alors la commande de restauration des données de la table de configuration inclut des lignes pour ces schémas et va par conséquent échouer, car les enregistrements qu'elle tente de restaurer seront considérés comme des doublons des enregistrements qui viennent d'être insérés par le déclencheur.

Cet échec est lourd de conséquences, car il se traduit par un déréférencement de fait de tous les autres schémas, qui ne sont dès lors plus pris en compte par les mécanismes d'Asgard (mise en cohérence des propriétaires, etc.). Il reste possible de les re-référencer a posteriori avec les fonctions z_asgard_admin.asgard_initialisation_gestion_schema ou z_asgard.asgard_initialise_schema, mais au prix de la perte de toutes les informations qui n'apparaissaient que dans la table de gestion : éditeur, lecteur, valeurs des champs niv1, niv1_abr, etc.

Une anomalie annexe est que la restauration de la base va de fait silencieusement référencer dans la table de gestion d'Asgard tous les schémas créés par les extensions qui sont restaurées après Asgard, même si ces schémas n'étaient initialement pas référencés, et ce potentiellement pour d'excellentes raisons.

@alhyss alhyss self-assigned this Aug 9, 2024
@alhyss alhyss added the bug Something isn't working label Aug 9, 2024
@alhyss
Copy link
Collaborator Author

alhyss commented Aug 9, 2024

En attendant un correctif, ci-après deux méthodes pour éviter que l'erreur n'apparaisse. Elles compliquent un peu le processus de sauvegarde/restauration mais sont fiables et ne sont pas susceptibles de produire des effets de bord.

Méthode n°1, avec une restauration par étape :

  • Sauvegarder la base de départ.
  • Restaurer uniquement la structure sur la base d'arrivée.
  • Sur la base d'arrivée, déréférencer de la table de gestion d'Asgard les schémas créés par des extensions avec la fonction z_asgard_admin.asgard_sortie_gestion_schema. Par exemple, pour le schéma z_plume de l'extension PlumePg :
    SELECT z_asgard_admin.asgard_sortie_gestion_schema('z_plume') ;
  • Restaurer les données sur la base d'arrivée.

NB : Plusieurs procédés sont possibles pour séparer la restauration de la structure et des données selon le format de sauvegarde. Pour le format plain, il est nécessaire d'utiliser les paramètres -s / --schema-only et -a / --data-only de pg_dump pour obtenir deux scripts de restauration distincts à exécuter ensuite avec psql : la sauvegarde est faite en deux fois, d'abord avec pg_dump -s pour la structure puis pg_dump -a pour les données. Pour les autres formats, il possible d'utiliser la même méthode ou de ne créer qu'une seule archive au moment de la sauvegarde et d'utiliser ensuite les paramètres -s / --schema-only et -a / --data-only de pg_restore pour ne restaurer à chaque étape que la partie de l'archive qui doit l'être.

Méthode n°2, en intervenant avant la sauvegarde :

  • Sur la base de départ, déréférencer de la table de gestion d'Asgard les schémas créés par des extensions avec la fonction z_asgard_admin.asgard_sortie_gestion_schema. Par exemple, pour le schéma z_plume de l'extension PlumePg :
    SELECT z_asgard_admin.asgard_sortie_gestion_schema('z_plume') ;
  • Sauvegarder la base de départ.
  • Procéder à la restauration sur la base d'arrivée.
  • Sur la base d'arrivée, re-saisir manuellement dans la table de gestion les quelques informations relatives aux schémas des extensions qui auraient été perdues (lecteur, éditeur, etc.), et déréférencer - z_asgard_admin.asgard_sortie_gestion_schema - les schémas créés par des extensions qui n'avaient pas lieu d'être référencés.

La méthode n°1 est conseillée dans le cas général, car elle ne nécessite pas de reprise manuelle.

Pour connaître les schémas créés par des extensions sur une base :

SELECT 
    pg_namespace.nspname AS nom_schema, 
    pg_extension.extname AS nom_extension
    FROM pg_depend 
        INNER JOIN pg_extension ON pg_extension.oid = pg_depend.refobjid
        INNER JOIN pg_namespace ON pg_namespace.oid = pg_depend.objid
    WHERE pg_depend.deptype = 'e' 
        AND pg_depend.classid = 'pg_namespace'::regclass 
        AND pg_depend.refclassid = 'pg_extension'::regclass
        AND NOT pg_namespace.nspname IN ('z_asgard', 'z_asgard_admin') ;

Les schémas d'Asgard sont exclus de la commande ci-avant, car leur référencement éventuel ne pose pas de problème. Le script de l'extension assure qu'ils soient créés avant le déclencheur asgard_on_create_schema.

Plutôt que de déréférencer un par un les schémas concernés, il est possible d'inclure la fonction de référencement dans la requête précédente :

SELECT 
    pg_namespace.nspname AS nom_schema, 
    pg_extension.extname AS nom_extension,
    z_asgard_admin.asgard_sortie_gestion_schema(pg_namespace.nspname) AS dereferencement
    FROM pg_depend 
        INNER JOIN pg_extension ON pg_extension.oid = pg_depend.refobjid
        INNER JOIN pg_namespace ON pg_namespace.oid = pg_depend.objid
    WHERE pg_depend.deptype = 'e' 
        AND pg_depend.classid = 'pg_namespace'::regclass 
        AND pg_depend.refclassid = 'pg_extension'::regclass
        AND NOT pg_namespace.nspname IN ('z_asgard', 'z_asgard_admin') ;

La fonction indique __ DEREFERENCEMENT REUSSI. dès lors qu'il n'y a pas eu d'erreur et même si le schéma n'était pas référencé. Dans ce cas elle n'a juste aucun effet.

@alhyss alhyss added this to the ASGARD v1.4.1 milestone Aug 9, 2024
@alhyss
Copy link
Collaborator Author

alhyss commented Aug 9, 2024

Avertissements temporairement ajoutés dans la documentation - à retirer lorsque la version corrective aura été publiée :

J'en ai profité pour ajouter une remarque (pérenne) sur le fait qu'il est fortement déconseillé de désactiver les déclencheurs pendant la restauration.

alhyss added a commit that referenced this issue Sep 20, 2024
Script de déploiement d'Asgard v1.4.1 et mise à jour depuis la v1.4.0 :
- amendement de asgard_on_modify_gestion_schema_before pour
  éviter l'échec de la restauration de la table de gestion lorsque des
  extensions qui créent des schémas sont actives sur la base.
- changement du mot de passe initial de consult.defaut.
- ajout d'une contrainte sur gestion_schema et d'un contrôle dans
  asgard_on_modify_gestion_schema_before pour assurer que les
  schémas système ne puissent pas être référencés.
- ajout des fonctions utilitaires asgard_nettoyage_oids,
  asgard_cherche_lecteur, asgard_cherche_editeur et
  asgard_restaure_editeurs_lecteurs.

Mise à jour du README et des métadonnées du paquet Debian.

Refs: #13 #14 #15 #17
@alhyss alhyss mentioned this issue Sep 20, 2024
@alhyss
Copy link
Collaborator Author

alhyss commented Sep 20, 2024

Problème résolu par la version 1.4.1. La fonction z_asgard_admin.asgard_on_modify_gestion_schema_before appelée par le déclencheur asgard_on_modify_gestion_schema_before procède désormais à un dé-référencement préalable en cas de tentative d'insertion dans la table de gestion d'un schéma actif déjà référencé.

------- SCHEMA DEJA REFERENCE ------

Le déréférencement n'affectant pas les droits, cette manipulation est peu susceptible d'avoir des effets de bord gênants. Elle pourra échouer dans certains cas - notamment pour les schémas de la nomenclature nationale dont le dé-référencement n'est pas autorisé - mais devrait au moins permettre de gérer le cas des schémas créés par des extensions.

@alhyss alhyss closed this as completed Sep 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant