Documentation PostgreSQL 14.22 » Annexes » Modules supplémentaires fournis » amcheck

Précédent | Niveau supérieur | Suivant
F.1. adminpack | Sommaire | F.3. auth_delay

## F.2. amcheck

Le module `amcheck` fournit des fonctions vous permettant de vérifier la cohérence logique de la structure des relations.

Les fonctions spécifiques aux B-Tree vérifient diverses propriétés invariantes dans la structure de la représentation de certaines relations. La justesse des fonctions d'accès aux données durant les parcours d'index et d'autres opérations importantes reposent sur le fait que ces propriétés invariantes sont toujours vraies. Par exemple, certaines fonctions vérifient, entre autres choses, que tous les blocs d'index B-Tree ont leurs éléments dans un ordre « logique » (par exemple, pour des index B-Tree sur un type `text`, les lignes de l'index devraient être triées dans l'ordre alphabétique défini par la collation). Si, pour une raison ou une autre, cette propriété invariante spécifique n'est plus vérifiée, il faut s'attendre à ce que des recherches binaires sur la page affectée renseignent de manière erronée les parcours d'index, avec pour conséquence des résultats de requêtes SQL erronés. Si la structure semble valide, aucune erreur n'est levée.

La vérification est réalisée en utilisant les mêmes procédures que celles utilisées par les parcours d'index eux-mêmes, qui peuvent très bien être du code pour une classe d'opérateurs utilisateur. Par exemple, la vérification d'index B-Tree s'appuie sur les comparaisons faites avec une ou plusieurs routines pour la fonction de support 1. Voir Section 38.16.3 pour plus de détails sur les fonctions de support des classes d'opérateurs.

À la différence des fonctions de vérification B-Tree qui rapportent la corruption en remontant des erreurs, la fonction de vérification de table `verify_heapam` vérifie une table et tente de renvoyer un ensemble de lignes, une par corruption détectée. Malgré tout, si des installations, se reposant sur la fonction `verify_heapam`, sont elles-mêmes corrompues, la fonction pourrait ne pas être capable de continuer et leverait une erreur.

Le droit d'exécuter les fonctions `amcheck` peut être accordé aux utilisateurs normaux, sans attribut `SUPERUSER`, mais avant de donner cette permission, de prudentes considérations doivent être prises concernant la sécurité des données et les problèmes de confidentialité. Bien que les rapports de corruption générés par ces fonctions ne se focalisent pas tant sur le contenu des données corrompues que sur la structure de ces données et la nature de la corruption détectée, un attaquant qui gagnerait le droit d'exécuter ces fonctions, tout particulièrement si l'attaquant peut aussi causer de la corruption, pourrait être capable de déduire quelque chose des données elles-mêmes dans les messages.

### F.2.1. Fonctions

`bt_index_check(index regclass, heapallindexed boolean) returns void`

  `bt_index_check` vérifie que sa cible, un index B-Tree, respecte un éventail de propriétés invariables. Exemple d'utilisation :

      test=# SELECT bt_index_check(index => c.oid, heapallindexed => i.indisunique),
                     c.relname,
                     c.relpages
      FROM pg_index i
      JOIN pg_opclass op ON i.indclass[0] = op.oid
      JOIN pg_am am ON op.opcmethod = am.oid
      JOIN pg_class c ON i.indexrelid = c.oid
      JOIN pg_namespace n ON c.relnamespace = n.oid
      WHERE am.amname = 'btree' AND n.nspname = 'pg_catalog'
      -- Ignore les tables temporaires, qui peuvent appartenir à d'autres sessions
      AND c.relpersistence != 't'
      -- La fonction peut renvoyer une erreur si cela est omis :
      AND c.relkind = 'i' AND i.indisready AND i.indisvalid
      ORDER BY c.relpages DESC LIMIT 10;

       bt_index_check |             relname             | relpages
      ----------------+---------------------------------+----------
                      | pg_depend_reference_index       |       43
                      | pg_depend_depender_index        |       40
                      | pg_proc_proname_args_nsp_index  |       31
                      | pg_description_o_c_o_index      |       21
                      | pg_attribute_relid_attnam_index |       14
                      | pg_proc_oid_index               |       10
                      | pg_attribute_relid_attnum_index |        9
                      | pg_amproc_fam_proc_index        |        5
                      | pg_amop_opr_fam_index           |        5
                      | pg_amop_fam_strat_index         |        5
      (10 rows)

  Cet exemple montre une session qui vérifie les 10 plus gros index du catalogue de la base de données « test ». La vérification de la présence des lignes de la table dans l'index est demandée pour le sous-ensemble des index uniques. Puisqu'aucune erreur n'est retournée, tous les index testés semblent être cohérents au niveau logique. Bien évidemment, cette requête pourrait être facilement modifiée pour appeler `bt_index_check` sur chacun des index présents dans la base de données pour lesquels la vérification est supportée.

  `bt_index_check` acquiert un `AccessShareLock` sur l'index cible ainsi que sur la relation à laquelle il appartient. Ce niveau de verrouillage est le même que celui acquis sur les relations lors d'une simple requête `SELECT`. `bt_index_check` ne vérifie pas les propriétés invariantes qui englobent les relations enfant/parent, mais vérifiera la présence de toutes les lignes de la table et des lignes d'index à l'intérieur de l'index quand `heapallindexed` vaut `true`. Quand il faut lancer un test de recherche de corruption, de routine et pas trop lourd, sur un environnement de production, l'utilisation de `bt_index_check` offre généralement le meilleur compromis entre vérification minutieuse et impact limité sur les performances et la disponibilité de l'application.

`bt_index_parent_check(index regclass, heapallindexed boolean, rootdescend boolean) returns void`

  `bt_index_parent_check` teste que sa cible, un index B-Tree, respecte un certain nombre de propriétés invariantes. En option, quand l'argument `heapallindexed` vaut `true`, la fonction vérifie la présence de toutes les lignes dans la table. Quand l'argument optionnel `rootdescend` vaut `true`, la vérification recherche de nouveau les lignes au niveau des feuilles en réalisant une nouvelle recherche à partir de la racine pour chaque ligne. Les vérifications réalisables par `bt_index_parent_check` sont un surensemble des vérifications réalisées par `bt_index_check`. On peut voir `bt_index_parent_check` comme une version plus minutieuse de `bt_index_check` : contrairement à `bt_index_check`, `bt_index_parent_check` vérifie également les propriétés invariantes qui englobent les relations parent/enfant, et la présence de tous les liens dans la structure de l'index. `bt_index_parent_check` respecte la convention habituelle qui consiste à renvoyer une erreur si une incohérence ou tout autre problème est détecté.

  Un verrou de type `ShareLock` est requis sur l'index ciblé par `bt_index_parent_check` (un `ShareLock` est également acquis sur la relation associée). Ces verrous empêchent des modifications concurrentes par des commandes `INSERT`, `UPDATE` et `DELETE`. Les verrous empêchent également la relation associée d'être traitée de manière concurrente par `VACUUM`, ainsi que toute autre commande d'administration. Il est à noter que cette fonction ne conserve les verrous que durant son exécution et non pas durant l'intégralité de la transaction.

  La vérification supplémentaire qu'opère `bt_index_parent_check` est plus apte à détecter différents cas pathologiques. Ces cas peuvent impliquer une classe d'opérateur B-Tree implémentée de manière incorrecte utilisée pour l'index vérifié, ou, hypothétiquement, des bugs non encore découverts dans le code de la méthode d'accès associée à cet index B-Tree. Il est à noter que `bt_index_parent_check` ne peut pas être utilisé lorsque le mode Hot Standby est activé (c'est-à-dire, sur un serveur secondaire disponible en lecture seule), contrairement à `bt_index_check`.

### Astuce

Les fonctions `bt_index_check` et `bt_index_parent_check` journalisent toutes les deux des messages sur la vérification, avec le niveau de sévérité `DEBUG1` et `DEBUG2`. Ces messages fournissent des informations détaillées sur la vérification, exploitables par les développeurs PostgreSQL. Les utilisateurs avancés peuvent aussi trouver ces informations utiles, car elles fournissent du contexte additionnel si jamais la vérification détecte une incohérence. Exécuter

    SET client_min_messages = DEBUG1;

dans une session interactive, psql, avant d'exécuter une requête de vérification, va afficher les messages sur sa progression avec un niveau de détail raisonnable.

`verify_heapam(relation regclass, on_error_stop boolean, check_toast boolean, skip text, startblock bigint, endblock bigint, blkno OUT bigint, offnum OUT integer, attnum OUT integer, msg OUT text) returns setof record`

  Vérifie une table sur des corruptions structurelles, où les pages de relation contiennent des données au format invalide, et sur des corruptions logiques, où les pages sont valides structurellement mais incohérentes avec le reste de l'instance de base de données.

  Les arguments optionnels suivants sont reconnus :

  `on_error_stop`

    Si true, la vérification de corruption s'arrête à la fin du premier bloc dans lequel de la corruption est trouvée.

    La valeur par défaut est false.

  `check_toast`

    Si true, les valeurs TOAST sont vérifiées sur la table TOAST de la relation cible.

    Cette option est connue pour être lente. Aussi, si une table TOAST ou un de ses index est corrompu, vérifier les valeurs TOAST peut, en théorie, faire tomber le serveur. Cependant dans la majorité des cas, cela produira juste une erreur.

    La valeur par défaut est false.

  `skip`

    Si ce paramètre n'est pas mis à `none`, la vérification de corruption saute les blocs qui sont marqués tous visibles ou tous gelés, comme définie par la valeur de l'option. Les options valides sont `all-visible`, `all-frozen` et `none`.

    La valeur par défaut est `none`.

  `startblock`

    Si indiquée, la vérification de corruption commence au bloc spécifié, sautant tous les blocs précédents. Si `startblock` est spécifiée à une valeur en dehors de la plage des blocs de la table cible, une erreur est renvoyée.

    Par défaut, la vérification débute au premier bloc.

  `endblock`

    Si indiquée, la vérification de corruption termine à ce bloc, ignorant tous les blocs restants. Spécifier un `endblock` en dehors de la plage des blocs d'une table cible renvoie une erreur.

    Par défaut, tous les blocs sont vérifiés.

  Pour chaque corruption détectée, `verify_heapam` renvoie une ligne avec les colonnes suivantes :

  `blkno`

    Le numéro de bloc contenant le bloc corrompu.

  `offnum`

    Le décalage pour la ligne corrompue.

  `attnum`

    Le numéro d'attribut de la colonne corrompue dans la ligne, si la corruption est spécifique à la colonne et non pas à la ligne en entier.

  `msg`

    Un message décrivant le problème détecté.

### F.2.2. Vérification optionnelle `heapallindexed`

Quand l'argument `heapallindexed` des fonctions de vérification des B-Tree est à `true`, une phase de vérification supplémentaire est opérée sur la table associée à l'index cible. Elle consiste en une opération `CREATE INDEX CONCURRENTLY` « bidon » qui vérifie la présence de tous les nouveaux enregistrements hypothétiques d'index dans une structure de récapitulation temporaire et en mémoire (elle est construite au besoin durant la première phase de la vérification). Cette structure de récapitulation prend l'« empreinte digitale » de chaque enregistrement rencontré dans l'index cible. Le principe directeur derrière la vérification `heapallindexed` est qu'un nouvel index équivalent à l'index cible existant ne doit avoir que des entrées que l'on peut trouver dans la structure existante.

La phase `heapallindexed` supplémentaire a un coût significatif : typiquement, la vérification peut être plusieurs fois plus longue. Cependant, il n'y a pas de changement quant aux verrous acquis au niveau de la table quand on opère une vérification `heapallindexed`.

La structure de récapitulation est limitée en taille par `maintenance_work_mem`. Pour s'assurer que la probabilité de rater une incohérence ne dépasse 2 % pour chaque enregistrement qui devrait être dans l'index, il faut environ 2 octets de mémoire par enregistrement. Quand moins de mémoire est disponible par enregistrement, la probabilité de manquer une incohérence augmente lentement. Cette approche limite significativement le coût de la vérification, tout en réduisant légèrement la probabilité de détecter un problème, particulièrement sur les installations où la vérification est traitée comme une opération de maintenance de routine. Tout enregistrement absent ou déformé a une nouvelle chance d'être détecté avec chaque lancement de la vérification.

### F.2.3. Utiliser `amcheck` efficacement

`amcheck` peut être efficace pour détecter différents types de modes d'échec que les sommes de contrôle n'arriveront jamais à détecter. Cela inclut :

- Les incohérences dans la structure causées par des implémentations incorrectes de classe d'opérateurs.

  Cela inclut également des problèmes causés par le changement des règles de comparaison des collations du système d'exploitation. Les comparaisons des données d'un type ayant une collation comme `text` doivent être immuables (tout comme toutes les autres comparaisons utilisées pour les parcours d'index B-Tree doivent être immuables), ce qui implique que les règles de collation du système d'exploitation ne doivent jamais changer. Bien que cela soit rare, des mises à jour des règles des collations du système d'exploitation peuvent causer ces problèmes. Le plus souvent, c'est une incohérence dans l'ordre de collation entre le serveur principal et son serveur de secours qui est en cause, généralement due au fait que les versions majeures du système d'exploitation utilisées ne sont pas les mêmes. Plus généralement, une incohérence dans l'ordre de collation entre un serveur primaire et son réplica est impliquée, peut-être parce que la version majeure du système d'exploitation utilisée est différente. De telles incohérences ne surviendront généralement que sur des serveurs secondaires, et ne pourront par conséquent être détectées que là.

  Si un tel problème survient, il se peut que cela n'affecte pas chaque index qui utilise le tri d'une collation affectée, tout simplement parce que les valeurs indexées pourraient avoir le même ordre de tri absolu indépendamment des incohérences comportementales. Voir Section 24.1 et Section 24.2 pour plus de détails sur comment PostgreSQL utilise les locales et collations du système d'exploitation.

- Les incohérences dans la structure entre les index et les tables indexées (lorsque la vérification `heapallindexed` est réalisée).

  Il n'y a pas de vérification avec la table initiale en temps normal. Les symptômes d'une corruption de la table peuvent être subtils.

- La corruption causée par un hypothétique bug non encore découvert dans le code de la méthode d'accès dans PostgreSQL, dans le code effectuant le tri ou le code de gestion transactionnelle.

  La vérification automatique de l'intégrité structurelle des index joue un rôle dans les tests généraux des fonctionnalités de PostgreSQL, nouvelles ou proposées, qui pourraient possiblement permettre l'introduction d'incohérences logiques. La vérification de la structure de la table et des informations de visibilité et de statut des transactions associées joue un rôle similaire. Une stratégie de test évidente est d'appeler les fonctions d'`amcheck` de manière continue en même temps que les tests de régression standards sont lancés. Voir Section 33.1 pour plus de détails sur comment lancer les tests.

- Les failles dans le système de fichiers ou dans le sous-système de stockage quand les sommes de contrôles ne sont pas activées.

  Il est à noter que `amcheck` n'examine une page que telle qu'elle se présente dans un tampon en mémoire partagée lors de la vérification, et qu'en accédant au bloc dans la mémoire partagée. Par conséquent, `amcheck` n'examine pas forcément les données lues depuis le système de fichiers au moment de la vérification. Notez que si les sommes de contrôles sont activées, `amcheck` peut lever une erreur de somme de contrôle incorrecte quand un bloc corrompu est lu vers un tampon.

- Les corruptions causées par une RAM défaillante, et plus largement le sous-système mémoire et le système d'exploitation.

  PostgreSQL ne protège pas contre les erreurs mémoire corrigibles, et il est supposé que vous utilisez de la mémoire RAM de standard industriel ECC (Error Correcting Codes) ou avec une meilleure protection. Cependant, la mémoire ECC n'est typiquement immunisée que contre les erreurs d'un seul bit, et il ne faut pas partir du principe que ce type de mémoire fournit une protection absolue contre les défaillances provoquant une corruption de la mémoire.

  Quand la vérification `heapallindexed` est réalisée, il y a une chance fortement accrue de détecter des erreurs d'un bit car l'égalité binaire stricte est testée et les attributs indexés de la table sont testés.

La corruption structurelle peut survenir par un stockage matériel défectueux, ou par des fichiers de relation réécrits ou modifiés par un logiciel sans rapport. Ce type de corruption peut aussi être détecté avec les sommes de contrôle des fichiers de données.

Des pages de relation qui sont correctement formatées, internalement cohérentes, et correctes par rapport à leurs propres sommes de contrôle internes, peuvent encore contenir de la corruption logique. Par conséquent, ce type de corruption ne peut pas être détecté avec checksums. Des exemples incluent les valeurs TOAST dans la table principale pour lesquelles il manque l'entrée correspondante dans la table TOAST, et les lignes dans la table principale avec un identifiant de transaction plus ancien que le plus ancien identifiant de transaction valide dans la base de données ou l'instance.

De multiples causes de corruption logique ont été observées sur des systèmes en production, incluant des bugs dans le logiciel serveur PostgreSQL, des erreurs ou de mauvaises conceptions dans des outils de sauvegarde et restauration, et des erreurs utilisateur.

Les relations corrompues sont plus préoccupantes sur des environnements de production en cours, plus particulièrement ceux où les risques d'activités fortes sont les moins tolérés. Pour cette raison, `verify_heapam` a été conçue pour diagnostiquer la corruption sans risque. Elle ne peut pas protéger contre toutes les causes de crash de processus serveur, car même exécuter la requête appelante pourrait ne pas être sûr sur un système notablement corrompu. L'accès au tables du catalogue est effectué et peut poser problème si le catalogue lui-même est corrompu.

De manière générale, `amcheck` ne peut que prouver la présence d'une corruption ; il ne peut pas en prouver l'absence.

### F.2.4. Réparer une corruption

Aucune erreur concernant une corruption remontée par `amcheck` ne devrait être un faux positif. `amcheck` remonte des erreurs dans le cas où des conditions, par définition, ne devraient jamais arriver, et par conséquent une analyse minutieuse des erreurs remontées par `amcheck` est souvent nécessaire.

Il n'y a pas de méthode générale pour réparer les problèmes que `amcheck` détecte. Une explication de la cause principale menant à ce que la propriété invariante soit violée devrait être étudiée. pageinspect peut jouer un rôle utile dans le diagnostic de la corruption qu'`amcheck` détecte. Un `REINDEX` peut échouer à réparer la corruption.