Écrire des scripts d’administration idempotents

Dans de nombreux environnements, il est important d’exécuter des scripts d’administration E-Maj idempotents, c’est à dire capables de construire ou de mettre à jour un environnement E-Maj, quel que soit son état initial. Un environnement E-Maj se compose d’un jeu de paramètres et surtout d’un ensemble de groupes de tables, dont il faut définir les tables et séquences qui les composent et qu’il faut ensuite gérer (démarrage, arrêt, pose de marque,…).

Jeu de paramètres

Deux approches sont possibles.

Gestion d’une configuration globale du jeu de paramètres

Il s’agit de charger un jeu de paramètres en format JSON, stocké dans un fichier ou dans une colonne de tables, en utilisant la fonction emaj_import_parameters_configuration(), avec le second paramètre valorisé à TRUE pour réinitialiser d’éventuels paramètres E-Maj absents de la configuration JSON.

SELECT emaj.emaj_import_parameters_configuration (configuration.JSON, TRUE);

La configuration JSON à charger peut avoir été construite manuellement ou à l’aide de la fonction emaj_export_parameters_configuration().

Gestion unitaire des paramètres

Il est également possible d’exécuter un script SQL qui enchaine, dans une même transaction, la valorisation de tous les paramètres E-Maj, une valeur NULL étant fournie pour les paramètres gardant leur valeur par défaut

BEGIN;
  SELECT emaj.emaj_set_param(clé.paramètre 1, valeur.paramètre 1);
  SELECT emaj.emaj_set_param(clé.paramètre 2, valeur.paramètre 2);
  ...
COMMIT;

Constitution des groupes de tables

Là aussi, deux approches sont possibles.

Gestion d’une configuration globale des groupes

Comme pour les paramètres, la configuration de l’ensemble des groupes de tables peut être définie dans une structure en format JSON, stockée dans un fichier ou dans une colonne de tables. La fonction emaj_import_groups_configuration() permet de « charger » une telle configuration. Les groupes absents sont créés et les groupes dont le contenu diffère sont modifiés automatiquement. Pour que l’opération soit idempotente, il faut :

  • importer en une seule fois tous les groupes de la configuration, avec le 2ème paramètre valorisé à NULL (ou avec la liste exhaustive de tous les groupes) ;

  • autoriser la modification des groupes démarrés, avec le 3ème paramètre valorisé à TRUE ;

  • supprimer les éventuels groupes de tables existants mais absents de la configuration, avec le 5ème paramètre valorisé à TRUE.

SELECT emaj.emaj_import_groups_configuration (configuration.JSON, NULL, TRUE, '<marque>', TRUE);

La configuration JSON à charger peut avoir été construite manuellement ou à l’aide de la fonction emaj_export_groups_configuration().

Configuration élémentaire des groupes

L’alternative consiste à écrire un script contenant, au sein d’une unique transaction, les actions élémentaires permettant de créer, garnir, modifier voire supprimer les groupes de tables, et tenant compte de l’existant.

Pour créer les groupes de table manquants :

SELECT emaj.emaj_create_group ('monGroupe1', ...)
  WHERE NOT emaj_does_exist_group('monGroupe1');
SELECT emaj.emaj_create_group ('monGroupe2', ...)
  WHERE NOT emaj_does_exist_group('monGroupe2');
...

Pour supprimer, une fois arrêtés, les groupes de table obsolètes :

SELECT emaj.emaj_drop_group (group_name)
      FROM unnest (emaj.emaj_get_groups () ) AS group_name
  WHERE group_name NOT IN ('monGroupe1', 'monGroupe2', ...);

Pour assigner la table sch1.tbl1 ou la séquence sch1.seq1 au groupe de tables grp1, si elles ne le sont pas encore :

SELECT CASE
        WHEN emaj_get_assigned_group_table('sch1', 'tbl1') IS NULL
             THEN emaj.emaj_assign_table('sch1', 'tbl1', 'grp1', ...)
        WHEN emaj_get_assigned_group_table('sch1', 'tbl1') <> 'grp1'
             THEN emaj.emaj_move_table('sch1', 'tbl1', 'grp1')
        ELSE CONTINUE
      END;

SELECT CASE
        WHEN emaj_get_assigned_group_sequence('sch1', 'seq1') IS NULL
                 THEN emaj.emaj_assign_sequence('sch1', 'seq1', 'grp1')
        WHEN emaj_get_assigned_group_sequence('sch1', 'seq1') <> 'grp1'
                 THEN emaj.emaj_move_sequence('sch1', 'seq1', 'grp1')
        ELSE CONTINUE
      END;

Par extension, pour assigner toutes les tables du schéma sch1 à un groupe de tables :

SELECT CASE
        WHEN emaj_get_assigned_group_table(nspname, relname) IS NULL
                 THEN emaj.emaj_assign_table(nspname, relname, 'grp1', options)
        WHEN emaj_get_assigned_group_table(nspname, relname) <> 'grp1'
                 THEN emaj.emaj_move_table(nspname, relname, 'grp1')
        ELSE CONTINUE
      END
      FROM pg_class
           JOIN pg_namespace ON (pg_namespace.oid = relnamespace)
      WHERE nspname = 'sch1' AND relkind = 'r';

Si les propriétés E-Maj des tables d’un groupe sont susceptibles d’avoir des valeurs différentes des valeurs par défaut, il faut aussi s’assurer de leur valeur cible, en utilisant les fonctions emaj_modify_table() et/ou emaj_modify_tables(). Dans la structure JSONB fournie en paramètre, les propriétés qui doivent garder leur valeur par défaut doivent être explicitement positionnées à null.

SELECT emaj.emaj_modify_tables ('sch1', '.*', null,
        '{ "priority" : null, "log_data_tablespace" : null, "log_index_tablespace" : null,
         "ignored_triggers" : null }'));

SELECT emaj.emaj_modify_table ('sch1', 'tbl1',
        '{ "priority" : 1, "ignored_triggers" : ["trg1"] }'));

État des groupes de tables

Il est possible de conditionner la pose d’une marque sur un groupe de tables par son état courant :

SELECT emaj.emaj_set_mark_group ('grp1', '<marque>')
      WHERE emaj.emaj_is_logging_group('grp1');

Pour démarrer ou arrêter tous les groupes de tables, quel que soit leur état courant :

SELECT emaj.emaj_start_groups (emaj.emaj_get_idle_groups(), '<marque_start>);

SELECT emaj.emaj_stop_groups (emaj.emaj_get_logging_groups());

De la même manière, on peut poser une marque commune à l’ensemble des groupes de tables démarrés, avec :

SELECT emaj.emaj_set_mark_groups (emaj.emaj_get_logging_groups(), '<marque>');

Rappelons que les fonctions emaj_get_groups(), emaj_get_logging_groups() et emaj_get_idle_groups() ont des paramètres qui permettent de filtrer les noms de groupe.

Enfin, les fonctions emaj_protect_group() et emaj_unprotect_group(), qui respectivement active ou désactive la protection d’un groupe de tables contre les rollbacks E-Maj, sont idempotentes par nature. On peut donc les appeler sans connaître le niveau de protection actuel des groupes.