← Retour au Blog
Backend & Architecture

Index concurrents PostgreSQL : attentes et reprise après un échec de construction

Une commande de création concurrente d’index en attente et un index invalide laissé après un échec nécessitent des réponses différentes. Utilisez des diagnostics tenant compte de la phase, des vérifications du catalogue et la procédure de reprise PACE pour choisir une intervention et vérifier l’état prévu par la migration.

Rédigé par Hamza Diaz
27 octobre 202610 min de lecture17 vues

Prenons l’exemple hypothétique d’une nouvelle tentative de migration qui signale une relation déjà existante. La migration se poursuit, mais personne n’a vérifié si l’index est utilisable. PostgreSQL peut laisser un index invalide après l’échec d’une création concurrente ; IF NOT EXISTS ne valide pas la définition existante. La documentation de la commande explique ces deux comportements.

Commencez par le processus serveur. Est-il encore en train de construire l’index, ou attend-il ? Établissez si la construction est terminée avant d’envisager une reprise. Comparez ensuite l’index réel à celui prévu par la migration. Un avis indiquant que le nom existe constitue une vérification d’achèvement trop faible.

Ce guide concerne les tables applicatives ordinaires, non partitionnées. Les modèles SQL ont fait l’objet d’une revue de leur code, mais n’ont pas été exécutés. Adaptez les noms hypothétiques public.orders et vérifiez la base de données ainsi que les privilèges requis. Consultez la documentation de la version majeure déployée et testez les modifications dans un environnement isolé.

Ce que CREATE INDEX CONCURRENTLY change, et ce qu’il ne change pas

La compatibilité avec les écritures n’exclut ni les verrous ni les attentes

La commande ordinaire CREATE INDEX bloque les écritures dans la table tout en autorisant les lectures ordinaires. CREATE INDEX CONCURRENTLY permet les écritures normales pendant la construction, mais nécessite toujours une coordination et des attentes. Une seule construction concurrente peut s’exécuter à la fois sur une table donnée. Elle doit s’exécuter en dehors d’un bloc de transaction. PostgreSQL documente ces restrictions.

Deux parcours, des attentes de transactions et le passage à l’état valide

La création concurrente enregistre une entrée invalide dans le catalogue avant d’effectuer deux parcours de la table dans des transactions distinctes. Elle attend la fin des transactions de modification concernées avant les parcours, puis celle des instantanés plus anciens après le second parcours. Cette entrée invalide peut correspondre à une construction qui se déroule normalement. N’y touchez pas tant que vous n’avez pas établi le contraire. Les parcours et les attentes supplémentaires signifient également que la création concurrente peut prendre plus de temps. Consultez la séquence de construction.

Diagnostiquer l’attente d’une construction d’index PostgreSQL selon la phase et le processus serveur

Lire pg_stat_progress_create_index avant d’estimer la progression

Connectez-vous à la base de données cible. Ce modèle en lecture seule filtre les résultats par table, conserve les identifiants bruts et convertit en null les identifiants indisponibles dont la valeur est zéro :

SELECT p.pid, p.command, p.phase, p.relid,
       NULLIF(p.index_relid, 0) AS index_relid,
       p.lockers_total, p.lockers_done,
       NULLIF(p.current_locker_pid, 0) AS current_locker_pid,
       p.blocks_total, p.blocks_done
FROM pg_stat_progress_create_index AS p
WHERE p.datname = current_database()
  AND p.relid = to_regclass('public.orders');

Une cible absente est résolue en null et ne produit aucune correspondance. Vérifiez l’identifiant avant d’interpréter un résultat vide. Consultez la documentation de to_regclass.

Interprétez les compteurs dans le contexte de la phase signalée. Les définitions de progression de PostgreSQL distinguent les opérations suivantes.

Phase signaléeÉléments à examiner ensuiteLimite d’interprétation
waiting for writers before buildNombre de détenteurs de verrous, détenteur actuel, responsable de la transactionCe n’est pas un pourcentage de parcours de la table
building index et ses sous-phasesRelevés successifs des compteurs de blocs ou de tuples, télémétrie des ressourcesLes informations disponibles dépendent de la méthode d’accès
waiting for writers before validationProcessus d’écriture concernés et leurs transactionsUne attente de coordination ultérieure, sans nécessairement indiquer un échec
index validation: scanning index, sorting tuples ou scanning tableNom de la phase et compteurs disponiblesLes parcours de l’index et de la table mesurent des opérations différentes
waiting for old snapshotsDétenteur de verrou actuel et activité liée aux instantanésExaminer le contexte transactionnel avant toute intervention

Dans la ligne de validation ci-dessus, « sorting tuples » et « scanning table » portent également le préfixe de phase complet « index validation: ». Une réindexation concurrente peut signaler « waiting for readers before marking dead » et « waiting for readers before dropping » ; la même documentation de progression décrit ces attentes.

Les totaux de blocs décrivent la phase en cours. Ils ne permettent pas d’établir la progression de bout en bout, et des totaux nuls ou indisponibles ne permettent pas d’estimer un pourcentage.

Recouper les informations du processus serveur en attente avec celles des transactions et des verrous

Examinez les processus serveurs de construction ainsi que les détenteurs de verrous actuels qu’ils signalent à l’aide de ce modèle distinct en lecture seule.

WITH progress AS (
  SELECT pid, current_locker_pid
  FROM pg_stat_progress_create_index
  WHERE datname = current_database()
    AND relid = to_regclass('public.orders')
), participants AS (
  SELECT pid FROM progress
  UNION
  SELECT current_locker_pid FROM progress
  WHERE current_locker_pid > 0
)
SELECT a.pid, a.state, a.xact_start, a.backend_xmin,
       a.wait_event_type, a.wait_event,
       pg_blocking_pids(a.pid) AS lock_blocking_pids
FROM pg_stat_activity AS a
JOIN participants AS t ON t.pid = a.pid;

pg_blocking_pids signale les processus bloquants liés aux verrous, mais ne peut pas expliquer tous les délais liés aux instantanés. Un tableau vide n’exclut pas une attente. De même, une valeur ancienne de xact_start ou de backend_xmin mérite une investigation ; elle ne désigne pas une session à terminer. Coordonnez-vous avec le responsable de la transaction. Cette requête inclut le processus serveur de construction et le détenteur de verrou actuel qu’il signale, sans établir un arbre récursif des processus bloquants. Examinez séparément les PID des processus bloquants renvoyés. La fonction peut renvoyer zéro lorsqu’une transaction préparée est bloquante ; zéro ne désigne pas un processus serveur à terminer. Consultez la même documentation de la fonction.

Pour une comparaison en dehors de PostgreSQL, le guide d’Optijara sur l’observabilité des constructions TensorRT examine comment distinguer une opération longue d’un échec.

Interpréter avec prudence les lignes absentes et la visibilité partielle

L’absence d’une ligne de progression ne permet aucune conclusion. Vérifiez le PID du processus serveur que vous avez conservé et le résultat côté client, puis recoupez les erreurs du serveur avec l’état du catalogue. Confirmez la base de données sélectionnée et les autorisations du rôle de supervision. Les détails des autres sessions peuvent être restreints. Les longues transactions de supervision peuvent également rendre les observations obsolètes ; répétez-les en dehors de cette transaction. La documentation sur la supervision de l’activité explique ces deux limites.

Examiner les index invalides avant de réessayer avec IF NOT EXISTS

Vérifier indisvalid, indisready, indislive et la définition réelle

Examinez l’index nommé et la table à laquelle il appartient réellement. Un registre des migrations ne permet d’établir ni l’un ni l’autre. Les jointures ci-dessous suivent la documentation du catalogue des relations et du catalogue des espaces de noms.

SELECT ns.nspname AS index_schema, ix.relname AS index_name,
       tn.nspname AS table_schema, t.relname AS table_name,
       i.indisunique, i.indisvalid, i.indisready, i.indislive,
       pg_get_indexdef(i.indexrelid) AS definition
FROM pg_index AS i
JOIN pg_class AS ix ON ix.oid = i.indexrelid
JOIN pg_namespace AS ns ON ns.oid = ix.relnamespace
JOIN pg_class AS t ON t.oid = i.indrelid
JOIN pg_namespace AS tn ON tn.oid = t.relnamespace
WHERE ns.nspname = 'public'
  AND ix.relname = 'orders_customer_id_idx';

Comparez la définition à la migration approuvée, y compris l’unicité et la méthode d’accès. Vérifiez l’ordre des clés, les expressions, les colonnes incluses et le prédicat. pg_get_indexdef reconstitue cette définition.

Dans pg_index, indisvalid décrit la validité pour les requêtes. indisready indique si l’index est prêt à être maintenu lors des insertions et des mises à jour ; indislive indique s’il est actif. Un indicateur d’activité à false signifie que l’index est en cours de suppression et doit être ignoré à toutes fins. Un index invalide peut encore entraîner un surcoût d’écriture. À lui seul, l’indicateur d’invalidité ne permet pas d’identifier une construction concurrente ayant échoué.

Les constructions d’index uniques exigent une attention particulière. L’application de l’unicité aux autres transactions commence au début du second parcours de la table, avant la validation. Un échec pendant ce parcours peut laisser un index invalide qui rejette encore les écritures en doublon. L’indicateur d’invalidité ne permet pas non plus de certifier que les données existantes respectent l’unicité. Les réserves concernant les constructions concurrentes expliquent ce calendrier.

Pourquoi l’idempotence fondée sur le nom ne signifie pas que la migration a réussi

IF NOT EXISTS supprime une erreur lorsqu’un nom de relation existe déjà. Il ne reconstruit pas un index invalide et ne garantit pas une définition équivalente. Le signalement Rails no 52583 décrit le piège qui en résulte lors des délais d’expiration et des nouvelles tentatives. Ce signalement public ne permet pas d’établir qu’un correctif existe actuellement dans Rails. La documentation de la commande PostgreSQL définit le comportement sous-jacent.

La procédure de reprise PACE : phase, évaluation, choix, vérification de l’aptitude à l’utilisation

Phase et évaluation : recueillir les éléments avant de modifier l’état

PACE est un cadre éditorial présenté dans cet article. Avant de modifier l’état, conservez l’erreur initiale et la phase en cours. Conservez l’identité et la définition de l’index, ainsi que le contexte relatif au responsable de la transaction.

flowchart TD P[Phase : identifier les opérations actives et les attentes] --> A[Évaluation : examiner l’identité et les éléments attestant un échec] A --> C{Choix : sélectionner une branche approuvée} C --> W[Observer les opérations actives ou les coordonner] C --> R[Corriger la cause et rétablir un index admissible] W --> E[Vérifier l’aptitude à l’utilisation : contrôles de la commande et du catalogue] R --> E

Avant toute suppression, vérifiez si une contrainte utilise l’index nommé.

SELECT conname, contype, conrelid::regclass AS owning_table
FROM pg_constraint
WHERE conindid = to_regclass('public.orders_customer_id_idx');

pg_constraint.conindid identifie les index qui prennent en charge des contraintes. Un résultat vide laisse encore les autres dépendances et la propriété à examiner. Ajouter CASCADE ne remplace pas cette vérification.

Choix : attendre, supprimer et recréer, ou réindexer

Choisissez une branche à partir de l’état établi. Une boucle de nouvelles tentatives inconditionnelle élude cette décision.

État établiAction suivante approuvéeCondition de contrôle requise
Construction active avec une progression attendueObserverComparer des observations répétées tenant compte de la phase
Construction active en attenteExaminer et coordonner la résolutionConfirmer la transaction concernée et son responsable
Échec d’une construction d’index autonome, définition conforme à l’intentionCorriger la cause, puis supprimer et recréer, ou effectuer une réindexation concurrente au niveau de l’index s’il est admissibleÉtablir que la construction est terminée et que la reprise est autorisée
Index valide, définition non conformeConcevoir une migration distincteNe pas considérer le nom existant comme un critère d’acceptation
Artefacts d’une réindexation concurrente ayant échouéSuivre la branche documentée propre à l’artefactExaminer l’identité, les dépendances et l’index valide restant

N’utilisez le modèle de suppression et de recréation non exécuté ci-dessous qu’après la fin de la construction ayant échoué et si les vérifications des dépendances autorisent la suppression. Il suppose que la définition approuvée correspond exactement à un index B-tree non unique sur public.orders(customer_id).

DROP INDEX CONCURRENTLY public.orders_customer_id_idx;
CREATE INDEX CONCURRENTLY orders_customer_id_idx
  ON public.orders USING btree (customer_id);

Soumettez des commandes distinctes de premier niveau en dehors des blocs de transaction. Si la suppression échoue, arrêtez-vous et examinez de nouveau l’état. CREATE INDEX n’accepte pas un nom d’index qualifié par un schéma ; qualifier public.orders place l’index dans ce schéma. Vérifiez les restrictions distinctes de la création et de la suppression concurrente.

Pour un index invalide admissible dont la définition doit être conservée, la branche alternative figure ci-dessous.

REINDEX INDEX CONCURRENTLY public.orders_customer_id_idx;

Résolvez d’abord la cause. La réindexation ne peut ni corriger des données en doublon, ni modifier une définition incorrecte, et elle ne supprime pas la charge de travail bloquante. Ciblez explicitement l’index : seule la forme au niveau de l’index peut reconstruire un index invalide de manière concurrente. Ne la remplacez pas par une réindexation concurrente de toute la table. Vérifiez le processus et les exclusions.

Une réindexation concurrente ayant échoué peut laisser un index de remplacement _ccnew invalide, que la branche documentée supprime avant une nouvelle tentative. Un index _ccold invalide peut, à l’inverse, être l’ancienne copie laissée après une reconstruction réussie. Des suffixes numériques sont possibles. Vérifiez l’index valide restant et les dépendances avant d’approuver la suppression. Ne supprimez jamais en masse sur la base d’un suffixe. PostgreSQL documente les différentes procédures de nettoyage.

Vérification de l’aptitude à l’utilisation : contrôler les postconditions prévues

Après la reprise, exigez la réussite de la commande et répétez la requête du catalogue. Vérifiez que le schéma et la table correspondent à la définition prévue ; confirmez les indicateurs de validité, de disponibilité pour la maintenance et d’activité. La validité n’impose pas la sélection de l’index par le planificateur, et des transactions plus anciennes peuvent retarder son utilisation. Le guide d’Optijara pour choisir les sondes Kubernetes explore la différence plus générale entre le bon fonctionnement d’un processus et son aptitude à servir.

Tenez un registre des observations au lieu d’inventer un seuil universel de durée écoulée.

MesureMoment du relevéDécision étayée
Phase et compteurs applicablesObservations répétées pendant la constructionProgression des opérations ou nécessité d’une investigation
PID du détenteur de verrou, contexte transactionnel, événements d’attentePendant une attenteRésolution coordonnée avec le responsable
Résultat de la commande, définition, indicateurs du catalogueAvant la reprise et après l’achèvementPostcondition prévue atteinte ou non
CPU, entrées-sorties, latence et erreurs de l’applicationAvant, pendant et après l’opérationImpact sur la charge de travail nécessitant une réponse opérationnelle

Il s’agit de mesures proposées, et non de résultats de tests de performance. Le JSON ci-dessous résume le cadre et la règle d’achèvement.

{
  "framework": "pace",
  "layers": ["phase", "assess", "choose", "establish_readiness"],
  "completionRequires": ["successful_command", "intended_definition", "valid_ready_live"],
  "automaticDestructiveRetry": false
}

Les erreurs des équipes lors des nouvelles tentatives de migration

Prendre un avis de nouvelle tentative réussie pour la preuve d’un index valide

Un avis lors d’une nouvelle tentative répond à une question plus restreinte que celle à laquelle la migration doit répondre. Vérifiez séparément le catalogue et la définition. Conservez ce contrôle d’acceptation dans les processus CI/CD assistés par l’IA, y compris pour les migrations proposées par des systèmes automatisés.

Réessayer avant de corriger la cause de l’échec de la construction

Une nouvelle tentative ne résout ni les données en doublon ni une transaction ouverte. Conservez l’erreur initiale pour que la reprise traite l’échec réellement observé. Utilisez la liste de contrôle suivante avant de poursuivre.

Point de contrôleÉléments conservés
Consigner l’intentionSchéma, table, définition complète de l’index
Configurer l’exécutionDDL concurrent en dehors de transactions englobantes
Conserver les informations sur l’échecErreur initiale et résultat de la commande
Examiner avant de réessayerPhase, processus serveur, catalogue, dépendances
Approuver la repriseResponsable désigné et branche admissible
Valider l’achèvementCommande réussie et index correspondant, valide, prêt à être maintenu et actif

Traiter le nettoyage destructif comme une idempotence générique

Excluez le nettoyage destructif de la logique générique de nouvelle tentative. Exigez une approbation humaine et une nouvelle vérification de l’état avant d’agir sur des sessions ou des index invalides. Les documentations de la construction concurrente et de la réindexation expliquent pourquoi les noms et les indicateurs intermédiaires ne constituent pas des motifs suffisants de suppression.

Réserves : partitions, contraintes, accès à la supervision et coût opérationnel

Les limites de la procédure pour les tables ordinaires

La documentation citée n’indique pas de prise en charge de la création concurrente sur une table parente partitionnée. Construisez les index des partitions feuilles de manière concurrente, puis planifiez séparément l’assemblage de l’index parent. Ne supposez pas que l’opération sur le parent s’effectue sans verrou. PostgreSQL décrit cette méthode.

DROP INDEX CONCURRENTLY accepte un seul index et interdit CASCADE. La commande doit s’exécuter en dehors d’un bloc de transaction. Elle exclut les index de tables partitionnées et la suppression directe d’index prenant en charge des contraintes UNIQUE ou PRIMARY KEY. Vérifiez l’admissibilité à la suppression. La réindexation concurrente exclut les catalogues système et les index de contraintes d’exclusion. Ses critères d’admissibilité sont distincts.

Pourquoi la concurrence est un compromis, pas une garantie de performance

Les parcours supplémentaires consomment du CPU et des ressources d’entrées-sorties. Prévoyez du temps d’ingénierie pour les essais préalables et la supervision, puis définissez une fenêtre d’observation et un plan d’intervention pour la charge de travail. La documentation de la construction concurrente avertit de la charge opérationnelle.

Protégez le texte des requêtes et les détails d’activité sensibles lorsque vous partagez des éléments liés à un incident. Tenez compte des restrictions d’accès à la supervision. Ces modèles n’ont pas été exécutés sur un environnement de test PostgreSQL dans le cadre de cette revue. Aucune attente ni reprise n’a été observée, et aucun résultat de production ou de test de performance n’est revendiqué. Testez la branche sélectionnée avant toute utilisation opérationnelle.

Points clés

  • 1Lisez la phase de construction signalée avant d’interpréter le temps écoulé ou les compteurs de blocs comme une mesure de progression.
  • 2Un index invalide peut correspondre à une construction active ; établissez l’échec avant d’autoriser le nettoyage.
  • 3IF NOT EXISTS vérifie l’existence du nom, sans vérifier l’équivalence de la définition ni la validité de l’index.
  • 4Utilisez PACE pour relier les observations de phase, l’évaluation du catalogue, la reprise approuvée et les vérifications de l’aptitude à l’utilisation.
  • 5La réindexation concurrente est une option pour les index admissibles, sans permettre de corriger des données en doublon ou une définition erronée.
  • 6Vérifiez l’achèvement de la commande et la présence de l’index prévu, valide, prêt à être maintenu et actif, tout en respectant les réserves relatives à la visibilité transactionnelle.

Conclusion

Commencez par la phase. Si la construction a échoué, établissez ce qui reste et corrigez la cause avant de choisir une procédure de reprise approuvée. Un avis de nouvelle tentative ne peut pas certifier l’index. Exigez la réussite de la commande et vérifiez la définition prévue ainsi que l’état valide, prêt à être maintenu et actif, en tenant compte des limites documentées de visibilité transactionnelle. Si votre équipe souhaite une seconde revue des garde-fous de ses migrations de base de données, Optijara peut aider à évaluer le plan.

Questions fréquentes

Pourquoi CREATE INDEX CONCURRENTLY attend-il, et évite-t-il tous les verrous ?

Il permet les écritures normales dans la table, mais nécessite toujours une coordination avec les autres transactions. Examinez `pg_stat_progress_create_index.phase`, le PID de construction et le détenteur de verrou actuel signalé. Un résultat vide de `pg_blocking_pids` n’exclut pas toutes les attentes. Consultez le [suivi de la progression](https://www.postgresql.org/docs/current/progress-reporting.html) et les [fonctions d’identification des blocages par verrou](https://www.postgresql.org/docs/current/functions-info.html).

Comment distinguer une construction active d’un index invalide après un échec ?

Recoupez la progression, l’activité du processus serveur, les erreurs de commande et l’état du catalogue. L’état invalide apparaît pendant la construction ; `indisvalid=false` ne prouve donc pas à lui seul un échec et n’autorise pas une suppression. En l’absence de progression, vérifiez la base de données sélectionnée, les autorisations et le résultat du processus serveur. Consultez le [comportement de la construction concurrente](https://www.postgresql.org/docs/current/sql-createindex.html) et la [visibilité de la supervision](https://www.postgresql.org/docs/current/monitoring-stats.html).

IF NOT EXISTS reconstruit-il un index PostgreSQL invalide ?

Non. Il supprime une erreur lorsqu’un nom de relation existe déjà, sans valider l’équivalence ni reconstruire un index invalide. Examinez la définition et l’état du catalogue, corrigez la cause de l’échec et choisissez une branche de reprise approuvée. Consultez la [documentation de CREATE INDEX](https://www.postgresql.org/docs/current/sql-createindex.html).

Puis-je rétablir un index invalide sans le supprimer au préalable ?

Oui, `REINDEX INDEX CONCURRENTLY` peut reconstruire un index invalide admissible avec la définition prévue. Il ne peut pas corriger des données en doublon, une définition erronée ou la charge de travail à l’origine de l’attente. Résolvez la cause, vérifiez les restrictions et contrôlez les postconditions. Consultez [REINDEX](https://www.postgresql.org/docs/current/sql-reindex.html) ; la [suppression suivie d’une recréation](https://www.postgresql.org/docs/current/sql-createindex.html) est une autre option documentée.

Un index unique invalide peut-il encore rejeter les écritures en doublon ?

Oui. L’application de l’unicité aux autres transactions commence au début du second parcours de la table, avant la validation. Si une construction concurrente d’index unique échoue pendant le second parcours, l’index invalide continue ensuite à imposer l’unicité. L’état invalide ne certifie ni l’absence d’effets indésirables ni l’unicité complète des données. Consultez les [réserves concernant la construction concurrente d’index uniques](https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY).

Sources

Partager cet article

Hamza Diaz

Rédigé par

Hamza Diaz

Hamza Diaz est le fondateur d’Optijara, où il conçoit des agents IA pratiques, des systèmes d’automatisation et des workflows Copilot pour les entreprises de services. Il écrit sur les opérations IA, la stratégie d’agents et la mise en œuvre concrète pour les équipes qui veulent des systèmes utiles plutôt que du battage médiatique.