← Volver al Blog
Backend & Architecture

Índices concurrentes de PostgreSQL: esperas y recuperación de construcciones fallidas

Un comando de creación concurrente de índices que está esperando y un índice no válido que queda tras un fallo requieren respuestas diferentes. Utiliza diagnósticos que tengan en cuenta la fase, comprobaciones del catálogo y el procedimiento de recuperación PACE para elegir una intervención y verificar el estado previsto de la migración.

Escrito por Hamza Diaz
27 de octubre de 202610 min de lectura19 vistas

Imagina un reintento hipotético de una migración que informa de que ya existe una relación. La migración continúa, pero nadie ha comprobado si el índice se puede utilizar. PostgreSQL puede dejar un índice no válido tras un fallo en la creación concurrente; IF NOT EXISTS no valida la definición existente. La documentación del comando explica ambos comportamientos.

Empieza por el proceso del servidor. ¿Sigue construyendo el índice o está esperando? Determina si la construcción ha terminado antes de plantearte la recuperación. Después, compara el índice real con la migración prevista. Un aviso de que el nombre ya existe es una comprobación insuficiente para dar el trabajo por terminado.

Esta guía aborda tablas de aplicación ordinarias, sin particiones. Las plantillas SQL se han revisado contrastándolas con las fuentes, pero no se han ejecutado. Adapta los nombres hipotéticos de public.orders y comprueba la base de datos y los privilegios necesarios. Consulta la documentación de la versión principal desplegada y ensaya los cambios en un entorno aislado.

Qué cambia CREATE INDEX CONCURRENTLY y qué no

La compatibilidad con las escrituras no implica ausencia de bloqueos ni de esperas

Un CREATE INDEX ordinario bloquea las escrituras en la tabla y permite las lecturas habituales. CREATE INDEX CONCURRENTLY permite las escrituras normales durante la construcción, pero sigue necesitando coordinación y esperas. Solo puede ejecutarse una construcción concurrente a la vez en una misma tabla. Debe ejecutarse fuera de un bloque de transacción. PostgreSQL documenta estas restricciones.

Dos recorridos, esperas de transacciones y la transición a la validez

La creación concurrente registra una entrada no válida en el catálogo antes de realizar dos recorridos de la tabla en transacciones separadas. Antes de los recorridos espera a las transacciones de modificación pertinentes y, después del segundo recorrido, a las instantáneas más antiguas. Esa entrada no válida puede pertenecer a una construcción que avanza correctamente. No la modifiques hasta saber lo contrario. Los recorridos y las esperas adicionales también pueden hacer que la creación concurrente tarde más. Consulta la secuencia de construcción.

Diagnostica las esperas de construcción de índices de PostgreSQL por fase y proceso del servidor

Consulta pg_stat_progress_create_index antes de estimar el progreso

Conéctate a la base de datos de destino. Esta plantilla de solo lectura filtra por tabla, conserva los identificadores originales y convierte en null los identificadores no disponibles cuyo valor es cero:

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');

Si el objeto de destino no existe, se obtiene null y no hay coincidencias. Verifica el identificador antes de interpretar un resultado vacío. Consulta la documentación de to_regclass.

Interpreta los contadores en el contexto de la fase indicada. Las definiciones de progreso de PostgreSQL distinguen los siguientes trabajos.

Fase indicadaSiguientes indicios que deben inspeccionarseLímites de la interpretación
waiting for writers before buildRecuentos de procesos que mantienen bloqueos, proceso bloqueador actual y responsable de la transacciónNo es un porcentaje de recorrido de la tabla
building index y sus subfasesContadores de bloques o tuplas en observaciones sucesivas y telemetría de recursosLa información disponible depende del método de acceso
waiting for writers before validationEscritores pertinentes y sus transaccionesUna espera de coordinación posterior, que no implica necesariamente un fallo
index validation: scanning index, sorting tuples o scanning tableNombre de la fase y contadores disponiblesLos recorridos del índice y de la tabla miden trabajos diferentes
waiting for old snapshotsProceso bloqueador actual y actividad relacionada con las instantáneasInvestiga el contexto de las transacciones antes de intervenir

En la fila de validación anterior, "sorting tuples" y "scanning table" también llevan el prefijo de fase completo "index validation:". La reindexación concurrente puede indicar "waiting for readers before marking dead" y "waiting for readers before dropping"; la misma referencia de progreso describe esas esperas.

Los totales de bloques describen la fase actual. No permiten determinar el progreso de principio a fin, y los totales con valor cero o no disponibles no permiten estimar un porcentaje.

Correlaciona el proceso del servidor que espera con los indicios de transacciones y bloqueos

Inspecciona los procesos de construcción junto con los procesos bloqueadores actuales indicados mediante esta otra plantilla de solo lectura.

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 informa de los procesos que causan bloqueos, pero no puede explicar todas las demoras relacionadas con las instantáneas. Un array vacío no descarta una espera. Del mismo modo, un xact_start o un backend_xmin antiguo requiere investigación; no identifica una sesión que deba terminarse. Coordínate con el responsable de la transacción. Esta consulta incluye el proceso de construcción y el proceso bloqueador actual indicado, no un árbol recursivo de bloqueadores. Investiga por separado los PID de los bloqueadores devueltos. La función puede devolver cero para un bloqueo causado por una transacción preparada; cero no es un proceso del servidor que deba terminarse. Consulta la misma referencia de la función.

Como comparación fuera de PostgreSQL, la guía de Optijara sobre observabilidad de la construcción en TensorRT examina cómo distinguir un trabajo prolongado de un fallo.

Interpreta con cautela las filas ausentes y la visibilidad parcial

La ausencia de una fila de progreso no permite concluir nada. Comprueba el PID del proceso del servidor que hayas conservado y el resultado del cliente; después, correlaciona los errores del servidor con el estado del catálogo. Confirma la selección de la base de datos y los permisos del rol de supervisión. Los detalles de otras sesiones pueden estar restringidos. Las transacciones de supervisión prolongadas también pueden dejar las observaciones desactualizadas; repítelas fuera de esa transacción. La documentación de supervisión de actividad explica ambas limitaciones.

Inspecciona los índices no válidos antes de reintentar con IF NOT EXISTS

Comprueba indisvalid, indisready, indislive y la definición real

Inspecciona el índice con ese nombre y la tabla a la que realmente pertenece. Un registro de migraciones no permite determinar ninguna de las dos cosas. Las uniones siguientes se basan en el catálogo de relaciones y el catálogo de espacios de nombres documentados.

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';

Compara la definición con la migración aprobada, incluidas la unicidad y el método de acceso. Comprueba el orden de las claves, las expresiones, las columnas incluidas y el predicado. pg_get_indexdef reconstruye esa definición.

En pg_index, indisvalid describe la validez para las consultas. indisready indica si el índice está preparado para mantenerse durante las inserciones y actualizaciones; indislive registra si sigue activo. Un indicador de actividad falso significa que el índice se está eliminando y debe ignorarse a todos los efectos. Un índice no válido puede seguir añadiendo sobrecarga a las escrituras. El indicador de no validez, por sí solo, no identifica una construcción concurrente fallida.

La construcción de índices únicos requiere especial cuidado. La aplicación de la unicidad frente a otras transacciones comienza cuando se inicia el segundo recorrido de la tabla, antes de que el índice sea válido. Un fallo durante ese recorrido puede dejar un índice no válido que siga rechazando escrituras duplicadas. El indicador de no validez tampoco permite certificar que los datos existentes satisfagan la unicidad. Las advertencias sobre la construcción concurrente explican estos tiempos.

Por qué la idempotencia basada en el nombre no equivale a una migración correcta

IF NOT EXISTS suprime un error cuando ya existe un nombre de relación. No reconstruye un índice no válido ni garantiza una definición equivalente. La incidencia #52583 de Rails describe la trampa de los tiempos de espera agotados y los reintentos que se deriva de ello. Ese informe público no demuestra que Rails disponga actualmente de una corrección. La referencia del comando de PostgreSQL define el comportamiento subyacente.

El procedimiento de recuperación PACE: fase, evaluación, elección y comprobación de la disponibilidad

Fase y evaluación: recopila indicios antes de cambiar el estado

PACE es un marco editorial que se presenta aquí. Antes de cambiar el estado, guarda el error original y la fase actual. Conserva la identidad y la definición del índice, junto con el contexto del responsable de la transacción.

flowchart TD P[Fase: identificar el trabajo activo y las esperas] --> A[Evaluación: inspeccionar la identidad y los indicios del fallo] A --> C{Elegir una vía aprobada} C --> W[Observar o coordinar el trabajo activo] C --> R[Corregir la causa y recuperar un índice apto] W --> E[Comprobar la disponibilidad: verificaciones del comando y del catálogo] R --> E

Antes de eliminar el índice con ese nombre, comprueba si alguna restricción lo utiliza.

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

pg_constraint.conindid identifica los índices que respaldan restricciones. Un resultado vacío deja pendientes la investigación de otras dependencias y de la propiedad. Añadir CASCADE no sustituye esa revisión.

Elección: esperar, eliminar y volver a crear, o reindexar

Elige una vía a partir del estado constatado. Un bucle de reintentos incondicional omite esa decisión.

Estado constatadoSiguiente acción aprobadaPrecaución necesaria
Construcción activa con el progreso esperadoObservarComparar indicios de observaciones sucesivas teniendo en cuenta la fase
Construcción activa en esperaInvestigar y coordinar la resoluciónConfirmar la transacción pertinente y su responsable
Construcción independiente fallida con la definición previstaCorregir la causa y, después, eliminar y volver a crear, o realizar una reindexación concurrente a nivel de índice si es aptoConstatar que la construcción ha terminado y que la recuperación está autorizada
Índice válido con una definición que no coincideDiseñar una migración separadaNo considerar el nombre existente como criterio de aceptación
Restos de una reindexación concurrente fallidaSeguir la vía documentada específica para esos restosInspeccionar la identidad, las dependencias y el índice válido que se conserva

Utiliza la siguiente plantilla de eliminación y recreación, que no se ha ejecutado, solo cuando haya terminado la construcción fallida y las comprobaciones de dependencias permitan la eliminación. Presupone que la definición aprobada es exactamente un índice B-tree no único sobre 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);

Envía comandos separados de nivel superior fuera de los bloques de transacción. Si la eliminación falla, detente e inspecciona de nuevo. CREATE INDEX no acepta un nombre de índice cualificado con un esquema; cualificar public.orders sitúa el índice en ese esquema. Comprueba las restricciones específicas de la creación y la eliminación concurrente.

Para un índice no válido que sea apto y cuya definición deba conservarse, la vía alternativa es la siguiente.

REINDEX INDEX CONCURRENTLY public.orders_customer_id_idx;

Resuelve primero la causa. La reindexación no puede corregir datos duplicados ni cambiar una definición incorrecta, y no elimina la carga de trabajo que causa el bloqueo. Indica el índice explícitamente: solo la forma a nivel de índice puede reconstruir de manera concurrente un índice no válido. No la sustituyas por una reindexación concurrente de toda la tabla. Comprueba el proceso y las exclusiones.

Una reindexación concurrente fallida puede dejar un sustituto _ccnew no válido, que la vía documentada elimina antes de reintentar. En cambio, un _ccold no válido puede ser la copia antigua que queda tras una reconstrucción correcta. Puede haber sufijos numéricos. Verifica el índice válido que se conserva y las dependencias antes de aprobar la eliminación. Nunca elimines índices en bloque basándote en el sufijo. PostgreSQL documenta las distintas vías de limpieza.

Comprobación de la disponibilidad: verifica las condiciones finales previstas

Tras la recuperación, exige que el comando finalice correctamente y repite la consulta al catálogo. Comprueba que el esquema y la tabla coinciden con la definición prevista; confirma los indicadores de validez, preparación y actividad. La validez no obliga al planificador a seleccionar el índice, y las transacciones más antiguas pueden retrasar su uso. La guía de Optijara para decidir sobre las sondas de Kubernetes explora la diferencia más amplia entre la salud de un proceso y su disponibilidad para el uso.

Mantén un registro de observaciones en lugar de inventar un umbral universal de tiempo transcurrido.

MediciónMomento de capturaDecisión que permite fundamentar
Fase y contadores aplicablesObservaciones sucesivas durante la construcciónEl trabajo avanza o requiere investigación
PID del proceso bloqueador, contexto de la transacción y eventos de esperaDurante una esperaResolución coordinada con el responsable
Resultado del comando, definición e indicadores del catálogoAntes de la recuperación y después de finalizarSe ha alcanzado o no la condición final prevista
CPU/E/S y latencia y errores de la aplicaciónAntes, durante y después de la operaciónImpacto en la carga de trabajo que requiere una respuesta operativa

Estas son mediciones propuestas, no resultados de pruebas de rendimiento. El JSON siguiente resume el marco y la regla para dar el trabajo por terminado.

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

Errores de los equipos al reintentar migraciones

Confundir un aviso de reintento correcto con un índice válido

Un aviso de reintento responde a una pregunta más limitada que la que necesita resolver la migración. Comprueba el catálogo y la definición por separado. Mantén esa comprobación de aceptación en los flujos de trabajo de CI/CD asistidos por IA, incluidas las migraciones propuestas por automatización.

Reintentar antes de corregir la causa de la construcción fallida

Otro intento no resuelve los datos duplicados ni una transacción abierta. Conserva el error inicial para que la recuperación aborde el fallo que realmente observaste. Utiliza la siguiente lista de comprobación antes de continuar.

Punto de controlIndicios conservados
Registrar la intenciónEsquema, tabla y definición completa del índice
Configurar la ejecuciónDDL concurrente fuera de las transacciones que lo engloban
Conservar el falloError original y resultado del comando
Inspeccionar antes de reintentarFase, proceso del servidor, catálogo y dependencias
Aprobar la recuperaciónResponsable identificado y vía apta
Aceptar la finalizaciónComando ejecutado correctamente e índice coincidente, válido, preparado y activo

Tratar la limpieza destructiva como idempotencia genérica

Mantén la limpieza destructiva fuera de la lógica genérica de reintentos. Exige aprobación humana y una nueva comprobación del estado antes de actuar sobre sesiones o índices no válidos. Las referencias de construcción concurrente y reindexación explican por qué los nombres y los indicadores intermedios no son motivos suficientes para eliminar un índice.

Advertencias: particiones, restricciones, acceso a la supervisión y coste operativo

Dónde termina el procedimiento para tablas ordinarias

La documentación citada no permite la creación concurrente sobre una tabla principal particionada. Construye de forma concurrente los índices de las particiones hoja y, después, planifica por separado el ensamblaje del índice principal. No des por hecho que esa operación sobre la tabla principal carece de bloqueos. PostgreSQL describe esta vía.

DROP INDEX CONCURRENTLY acepta un índice y prohíbe CASCADE. Debe ejecutarse fuera de un bloque de transacción. Excluye los índices de tablas particionadas y la eliminación directa de índices que respaldan restricciones UNIQUE o PRIMARY KEY. Comprueba si se permite la eliminación. La reindexación concurrente excluye los catálogos del sistema y los índices de restricciones de exclusión. Sus comprobaciones de aptitud son independientes.

Por qué la concurrencia implica un compromiso y no una garantía de rendimiento

Los recorridos adicionales consumen CPU y E/S. Reserva tiempo de ingeniería para los ensayos y la supervisión; después, define una ventana de observación y un plan de respuesta para la carga de trabajo. La documentación de construcción concurrente advierte sobre la carga operativa.

Protege el texto de las consultas y los detalles sensibles de la actividad al compartir indicios de un incidente. Ten en cuenta las restricciones de acceso a la supervisión. Estas plantillas no se ejecutaron en un entorno de prueba de PostgreSQL durante esta revisión. No se observó ninguna espera ni recuperación, y no se afirma ningún resultado de producción ni de pruebas de rendimiento. Ensaya la vía seleccionada antes de utilizarla en operaciones.

Puntos clave

  • 1Consulta la fase de construcción indicada antes de interpretar el tiempo transcurrido o los contadores de bloques como progreso.
  • 2Un índice no válido puede pertenecer a una construcción activa; constata el fallo antes de autorizar la limpieza.
  • 3IF NOT EXISTS comprueba la existencia del nombre, no la equivalencia de la definición ni la validez del índice.
  • 4Utiliza PACE para relacionar los indicios de la fase, la evaluación del catálogo, la recuperación aprobada y las comprobaciones de disponibilidad.
  • 5La reindexación concurrente es una opción para índices aptos, no una solución para datos duplicados ni para una definición errónea.
  • 6Verifica la finalización del comando y el índice previsto, válido, preparado y activo, respetando las advertencias sobre la visibilidad de las transacciones.

Conclusión

Empieza por la fase. Si la construcción ha fallado, determina qué queda y corrige la causa antes de elegir una vía de recuperación aprobada. Un aviso de reintento no permite certificar el índice. Exige que el comando finalice correctamente y verifica la definición prevista y el estado válido, preparado y activo, teniendo en cuenta los límites documentados de visibilidad de las transacciones. Si tu equipo quiere una segunda revisión de las medidas de protección de sus migraciones de base de datos, Optijara puede ayudar a evaluar el plan.

Preguntas frecuentes

¿Por qué está esperando CREATE INDEX CONCURRENTLY y evita todos los bloqueos?

Permite las escrituras normales en la tabla, pero sigue coordinándose con otras transacciones. Inspecciona `pg_stat_progress_create_index.phase`, el PID de construcción y el proceso bloqueador actual indicado. Un resultado vacío de `pg_blocking_pids` no descarta todas las esperas. Consulta los [informes de progreso](https://www.postgresql.org/docs/current/progress-reporting.html) y las [funciones de bloqueos](https://www.postgresql.org/docs/current/functions-info.html).

¿Cómo distingo una construcción activa de un índice no válido tras un fallo?

Correlaciona el progreso, la actividad del proceso del servidor, los errores del comando y el estado del catálogo. El estado no válido aparece durante la construcción, por lo que `indisvalid=false` por sí solo no demuestra un fallo ni autoriza la eliminación. Si no hay progreso visible, comprueba la selección de la base de datos, los permisos y el resultado del proceso del servidor. Consulta el [comportamiento de la construcción concurrente](https://www.postgresql.org/docs/current/sql-createindex.html) y la [visibilidad de la supervisión](https://www.postgresql.org/docs/current/monitoring-stats.html).

¿Reconstruye IF NOT EXISTS un índice de PostgreSQL no válido?

No. Suprime un error cuando ya existe un nombre de relación, sin validar la equivalencia ni reconstruir un índice no válido. Inspecciona la definición y el estado del catálogo, corrige la causa del fallo y elige una vía de recuperación aprobada. Consulta la [referencia de CREATE INDEX](https://www.postgresql.org/docs/current/sql-createindex.html).

¿Puedo recuperar un índice no válido sin eliminarlo primero?

Sí, `REINDEX INDEX CONCURRENTLY` puede reconstruir un índice no válido que sea apto y tenga la definición prevista. No puede corregir datos duplicados, una definición errónea ni la carga de trabajo que provoca la espera. Resuelve la causa, comprueba las restricciones y verifica las condiciones finales. Consulta [REINDEX](https://www.postgresql.org/docs/current/sql-reindex.html); [eliminar y volver a crear](https://www.postgresql.org/docs/current/sql-createindex.html) es otra opción documentada.

¿Puede un índice único no válido seguir rechazando escrituras duplicadas?

Sí. La aplicación de la unicidad frente a otras transacciones comienza cuando se inicia el segundo recorrido de la tabla, antes de que el índice sea válido. Si la construcción concurrente de un índice único falla durante el segundo recorrido, el índice no válido sigue imponiendo la unicidad después. El estado no válido no certifica que sea inocuo ni que todos los datos sean únicos. Consulta las [advertencias sobre la construcción concurrente de índices únicos](https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY).

Fuentes

Compartir este artículo

Hamza Diaz

Escrito por

Hamza Diaz

Hamza Diaz es el fundador de Optijara, donde crea agentes de IA prácticos, sistemas de automatización y flujos de trabajo de Copilot para empresas de servicios. Escribe sobre operaciones de IA, estrategia de agentes e implementación real para equipos que quieren sistemas útiles en lugar de promesas vacías.