F.1. amcheck — herramientas para verificar la consistencia de tablas e índices #

F.1.1. Funciones
F.1.2. Verificación opcional heapallindexed
F.1.3. Uso efectivo de amcheck
F.1.4. Reparación de la corrupción

El módulo amcheck proporciona funciones que te permiten verificar la consistencia lógica de la estructura de las relaciones.

Las funciones de comprobación de B-Tree verifican varios invariantes en la estructura de la representación de relaciones particulares. La corrección de las funciones del método de acceso detrás de los escaneos de índices y otras operaciones importantes depende de que estos invariantes se cumplan siempre. Por ejemplo, ciertas funciones verifican, entre otras cosas, que todas las páginas B-Tree tengan elementos en orden lógico (por ejemplo, para índices B-Tree en text, las tuplas del índice deben estar en orden léxico intercalado). Si ese invariante particular de alguna manera no se cumple, podemos esperar que las búsquedas binarias en la página afectada guíen incorrectamente los escaneos de índices, lo que resulta en respuestas erróneas a las consultas SQL. Si la estructura parece ser válida, no se produce ningún error. Mientras se ejecutan estas funciones de comprobación, el search_path se cambia temporalmente a pg_catalog, pg_temp.

La verificación se realiza utilizando los mismos procedimientos que los utilizados por los propios escaneos de índices, que pueden ser código de clase de operador definido por el usuario. Por ejemplo, la verificación del índice B-Tree se basa en las comparaciones realizadas con una o más rutinas de la función de soporte 1 de B-Tree. Consulta el Section 36.16.3 para obtener detalles sobre las funciones de soporte de la clase de operador.

A diferencia de las funciones de comprobación de B-Tree que informan de la corrupción mediante la generación de errores, la función de comprobación de montón (heap) verify_heapam comprueba una tabla e intenta devolver un conjunto de filas, una fila por cada corrupción detectada. A pesar de esto, si las utilidades de las que depende verify_heapam están corruptas, la función puede no ser capaz de continuar y en su lugar puede generar un error.

El permiso para ejecutar funciones de amcheck se puede otorgar a no superusuarios, pero antes de otorgar dichos permisos se debe prestar cuidadosa atención a los problemas de seguridad y privacidad de los datos. Aunque los informes de corrupción generados por estas funciones no se centran tanto en el contenido de los datos corruptos sino en la estructura de esos datos y la naturaleza de las corrupciones encontradas, un atacante que obtenga permiso para ejecutar estas funciones, especialmente si el atacante también puede inducir la corrupción, podría deducir algo de los propios datos a partir de tales mensajes.

F.1.1. Funciones #

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

bt_index_check comprueba que su objetivo, un índice B-Tree, respete una variedad de invariantes. Ejemplo de uso:

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'
-- No comprobar tablas temporales, que pueden ser de otra sesión:
AND c.relpersistence != 't'
-- La función puede arrojar un error cuando se omite esto:
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)

Este ejemplo muestra una sesión que realiza la verificación de los 10 índices de catálogo más grandes en la base de datos test. Se solicita la verificación de la presencia de tuplas de montón como tuplas de índice para el subconjunto que son índices únicos. Dado que no se genera ningún error, todos los índices probados parecen ser lógicamente consistentes. Naturalmente, esta consulta podría cambiarse fácilmente para llamar a bt_index_check para cada índice de la base de datos donde se admita la verificación.

bt_index_check adquiere un bloqueo AccessShareLock en el índice objetivo y en la relación de montón a la que pertenece. Este modo de bloqueo es el mismo adquirido en las relaciones por sentencias SELECT simples. bt_index_check no verifica invariantes que abarquen relaciones hijo/padre, pero verificará la presencia de todas las tuplas de montón como tuplas de índice dentro del índice cuando heapallindexed sea true. Cuando checkunique sea true, bt_index_check comprobará que no sea visible más de una de las entradas duplicadas en el índice único. Cuando se requiere una comprobación rutinaria y ligera de corrupción en un entorno de producción en vivo, el uso de bt_index_check a menudo proporciona el mejor equilibrio entre la rigurosidad de la verificación y la limitación del impacto en el rendimiento y la disponibilidad de la aplicación.

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

bt_index_parent_check comprueba que su objetivo, un índice B-Tree, respete una variedad de invariantes. Opcionalmente, cuando el argumento heapallindexed es true, la función verifica la presencia de todas las tuplas de montón que deberían encontrarse dentro del índice. Cuando checkunique es true, bt_index_parent_check comprobará que no sea visible más de una de las entradas duplicadas en el índice único. Cuando el argumento opcional rootdescend es true, la verificación vuelve a buscar tuplas en el nivel de hoja realizando una nueva búsqueda desde la página raíz para cada tupla. Las comprobaciones que puede realizar bt_index_parent_check son un superconjunto de las comprobaciones que puede realizar bt_index_check. bt_index_parent_check puede considerarse como una variante más rigurosa de bt_index_check: a diferencia de bt_index_check, bt_index_parent_check también comprueba invariantes que abarcan relaciones padre/hijo, incluyendo la comprobación de que no falten enlaces descendentes (downlinks) en la estructura del índice. bt_index_parent_check sigue la convención general de generar un error si encuentra una inconsistencia lógica u otro problema.

bt_index_parent_check requiere un bloqueo ShareLock en el índice objetivo (también se adquiere un ShareLock en la relación de montón). Estos bloqueos evitan la modificación concurrente de datos por parte de comandos INSERT, UPDATE y DELETE. Los bloqueos también evitan que la relación subyacente sea procesada concurrentemente por VACUUM, así como por todos los demás comandos de utilidad. Ten en cuenta que la función mantiene los bloqueos solo mientras se ejecuta, no durante toda la transacción.

La verificación adicional de bt_index_parent_check tiene más probabilidades de detectar varios casos patológicos. Estos casos pueden involucrar una clase de operador B-Tree implementada incorrectamente utilizada por el índice comprobado, o, hipotéticamente, errores no descubiertos en el código del método de acceso al índice B-Tree subyacente. Ten en cuenta que bt_index_parent_check no se puede utilizar cuando el modo hot standby está habilitado (es decir, en réplicas físicas de solo lectura), a diferencia de bt_index_check.

gin_index_check(index regclass) returns void

gin_index_check comprueba que su índice GIN objetivo tenga relaciones consistentes de tuplas padre-hijo (ninguna tupla padre requiere ajuste de tupla) y que el grafo de páginas respete los invariantes de árbol equilibrado (las páginas internas hacen referencia solo a páginas hoja o solo a páginas internas).

Tip

Tanto bt_index_check como bt_index_parent_check emiten mensajes de registro sobre el proceso de verificación a niveles de gravedad DEBUG1 y DEBUG2. Estos mensajes proporcionan información detallada sobre el proceso de verificación que puede ser de interés para los desarrolladores de PostgreSQL. Los usuarios avanzados también pueden encontrar útil esta información, ya que proporciona contexto adicional en caso de que la verificación realmente detecte una inconsistencia. Ejecutar:

SET client_min_messages = DEBUG1;

en una sesión interactiva de psql antes de ejecutar una consulta de verificación mostrará mensajes sobre el progreso de la verificación con un nivel de detalle manejable.

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

Comprueba una tabla, secuencia o vista materializada en busca de corrupción estructural, donde las páginas de la relación contienen datos con un formato no válido, y en busca de corrupción lógica, donde las páginas son estructuralmente válidas pero inconsistentes con el resto del clúster de la base de datos.

Se admiten los siguientes argumentos opcionales:

on_error_stop

Si es true, la comprobación de corrupción se detiene al final del primer bloque en el que se encuentre cualquier corrupción.

Por defecto es false.

check_toast

Si es true, los valores TOAST se comprueban con la tabla TOAST de la relación objetivo.

Se sabe que esta opción es lenta. Además, si la tabla TOAST o su índice están corruptos, comprobarla con los valores TOAST podría provocar la caída del servidor, aunque en muchos casos esto solo produciría un error.

Por defecto es false.

skip

Si no es none, la comprobación de corrupción omite los bloques marcados como all-visible (completamente visibles) o all-frozen (completamente congelados), según se especifique. Las opciones válidas son all-visible, all-frozen y none.

Por defecto es none.

startblock

Si se especifica, la comprobación de corrupción comienza en el bloque especificado, omitiendo todos los bloques anteriores. Es un error especificar un startblock fuera del rango de bloques en la tabla objetivo.

Por defecto, la comprobación comienza en el primer bloque.

endblock

Si se especifica, la comprobación de corrupción finaliza en el bloque especificado, omitiendo todos los bloques restantes. Es un error especificar un endblock fuera del rango de bloques en la tabla objetivo.

Por defecto, se comprueban todos los bloques.

Para cada corrupción detectada, verify_heapam devuelve una fila con las siguientes columnas:

blkno

El número del bloque que contiene la página corrupta.

offnum

El OffsetNumber de la tupla corrupta.

attnum

El número de atributo de la columna corrupta en la tupla, si la corrupción es específica de una columna y no de la tupla en su conjunto.

msg

Un mensaje que describe el problema detectado.

F.1.2. Verificación opcional heapallindexed #

Cuando el argumento heapallindexed de las funciones de verificación de B-Tree es true, se realiza una fase adicional de verificación contra la tabla asociada con la relación del índice objetivo. Esto consiste en una operación ficticia (dummy) de CREATE INDEX CONCURRENTLY, que comprueba la presencia de todas las hipotéticas nuevas tuplas de índice frente a una estructura de resumen temporal en memoria (esta se construye cuando es necesario durante la primera fase básica de verificación). La estructura de resumen registra la huella digital (fingerprint) de cada tupla encontrada dentro del índice objetivo. El principio de alto nivel detrás de la verificación heapallindexed es que un nuevo índice que es equivalente al índice objetivo existente solo debe tener entradas que se puedan encontrar en la estructura existente.

La fase adicional heapallindexed añade una sobrecarga significativa: la verificación normalmente tardará varias veces más. Sin embargo, no hay cambios en los bloqueos a nivel de relación adquiridos cuando se realiza la verificación heapallindexed.

La estructura de resumen está limitada en tamaño por maintenance_work_mem. Para garantizar que no haya más de un 2% de probabilidad de no detectar una inconsistencia por cada tupla de montón que debería estar representada en el índice, se necesitan aproximadamente 2 bytes de memoria por tupla. A medida que se dispone de menos memoria por tupla, la probabilidad de pasar por alto una inconsistencia aumenta lentamente. Este enfoque limita significativamente la sobrecarga de la verificación, al tiempo que reduce solo ligeramente la probabilidad de detectar un problema, especialmente para las instalaciones donde la verificación se trata como una tarea de mantenimiento rutinaria. Cualquier tupla ausente o mal formada tiene una nueva oportunidad de ser detectada con cada nuevo intento de verificación.

F.1.3. Uso efectivo de amcheck #

amcheck puede ser eficaz para detectar varios tipos de modos de fallo que las sumas de comprobación de datos (data checksums) no lograrán capturar. Estos incluyen:

  • Inconsistencias estructurales causadas por implementaciones incorrectas de clases de operadores.

    Esto incluye problemas causados por cambios en las reglas de comparación de las colaciones del sistema operativo. Las comparaciones de datos de un tipo con colación (como text) deben ser inmutables (al igual que todas las comparaciones utilizadas para los escaneos de índices B-Tree deben ser inmutables), lo que implica que las reglas de colación del sistema operativo nunca deben cambiar. Aunque es poco común, las actualizaciones de las reglas de colación del sistema operativo pueden causar estos problemas. Más comúnmente, se implica una inconsistencia en el orden de colación entre un servidor primario y un servidor en espera (standby), posiblemente porque la versión mayor del sistema operativo en uso es inconsistente. Tales inconsistencias generalmente solo surgirán en servidores en espera y, por lo tanto, generalmente solo se pueden detectar en servidores en espera.

    Si surge un problema como este, es posible que no afecte a cada índice individual ordenado utilizando una colación afectada, simplemente porque los valores indexados podrían tener el mismo orden absoluto independientemente de la inconsistencia de comportamiento. Consulta el Section 23.1 y el Section 23.2 para obtener más detalles sobre cómo PostgreSQL utiliza los locales y las colaciones del sistema operativo.

  • Inconsistencias estructurales entre los índices y las relaciones de montón que están indexadas (cuando se realiza la verificación heapallindexed).

    No hay una comprobación cruzada de los índices con su relación de montón durante el funcionamiento normal. Los síntomas de la corrupción del montón pueden ser sutiles.

  • Corrupción causada por errores hipotéticos no descubiertos en el código del método de acceso, código de ordenamiento o código de gestión de transacciones subyacente de PostgreSQL.

    La verificación automática de la integridad estructural de los índices juega un papel en las pruebas generales de las características nuevas o propuestas de PostgreSQL que podrían permitir de manera plausible que se introduzca una inconsistencia lógica. La verificación de la estructura de la tabla y la información asociada sobre la visibilidad y el estado de las transacciones juega un papel similar. Una estrategia de prueba obvia es llamar a las funciones de amcheck continuamente cuando se ejecutan las pruebas de regresión estándar. Consulta el Section 31.1 para obtener detalles sobre cómo ejecutar las pruebas.

  • Fallos del sistema de archivos o del subsistema de almacenamiento cuando las sumas de comprobación de datos están deshabilitadas.

    Ten en cuenta que amcheck examina una página tal como se representa en algún búfer de memoria compartida en el momento de la verificación si solo hay un acierto de búfer compartido al acceder al bloque. Por consiguiente, amcheck no examina necesariamente los datos leídos del sistema de archivos en el momento de la verificación. Ten en cuenta que cuando las sumas de comprobación están habilitadas, amcheck puede generar un error debido a un fallo de suma de comprobación cuando se lee un bloque corrupto en un búfer.

  • Corrupción causada por RAM defectuosa, o por el subsistema de memoria en general.

    PostgreSQL no protege contra errores de memoria corregibles y se asume que operarás utilizando RAM que utilice códigos de corrección de errores (ECC) estándar de la industria o una protección mejor. Sin embargo, la memoria ECC normalmente solo es inmune a errores de un solo bit, y no debe asumirse que proporciona una protección absoluta contra fallos que resulten en la corrupción de la memoria.

    Cuando se realiza la verificación heapallindexed, generalmente hay una probabilidad mucho mayor de detectar errores de un solo bit, ya que se prueba la igualdad binaria estricta y se prueban los atributos indexados dentro del montón.

La corrupción estructural puede ocurrir debido a hardware de almacenamiento defectuoso, o a que los archivos de relación sean sobrescritos o modificados por software no relacionado. Este tipo de corrupción también se puede detectar con las sumas de comprobación de páginas de datos (data page checksums).

Las páginas de relación que están correctamente formateadas, son internamente consistentes y correctas con respecto a sus propias sumas de comprobación internas aún pueden contener corrupción lógica. Como tal, este tipo de corrupción no se puede detectar con las sumas de comprobación. Los ejemplos incluyen valores TOAST en la tabla principal que carecen de una entrada correspondiente en la tabla TOAST, y tuplas en la tabla principal con un ID de transacción que es anterior al ID de transacción válido más antiguo en la base de datos o clúster.

Se han observado múltiples causas de corrupción lógica en sistemas de producción, incluyendo errores en el software del servidor PostgreSQL, herramientas de copia de seguridad y restauración defectuosas y mal concebidas, y errores del usuario.

Las relaciones corruptas son de lo más preocupante en los entornos de producción en vivo, precisamente los mismos entornos donde las actividades de alto riesgo son menos bienvenidas. Por esta razón, verify_heapam ha sido diseñada para diagnosticar la corrupción sin riesgos indebidos. No puede proteger contra todas las causas de caídas del backend, ya que incluso ejecutar la consulta que la llama podría ser inseguro en un sistema gravemente corrupto. El acceso a las tablas de catálogo se realiza y podría ser problemático si los propios catálogos están corruptos.

En general, amcheck solo puede probar la presencia de corrupción; no puede probar su ausencia.

F.1.4. Reparación de la corrupción #

Ningún error relativo a corrupción detectado por amcheck debería ser jamás un falso positivo. amcheck genera errores en caso de condiciones que, por definición, nunca deberían ocurrir, por lo que a menudo se requiere un análisis cuidadoso de los errores de amcheck.

No existe un método general para reparar los problemas que detecta amcheck. Se debe buscar una explicación de la causa raíz de la violación del invariante. El módulo pageinspect puede desempeñar un papel útil en el diagnóstico de la corrupción que detecta amcheck. Un comando REINDEX puede no ser eficaz para reparar la corrupción.