O erro
Tentando excluir tuplas contendo carimbos de data / hora inválidos com
DELETE FROM comments WHERE date > '1 Jan 9999' OR date < '1 Jan 2000' OR date_found > '1 Jan 9999' OR date_found < '1 Jan 2000';
termina em
ERROR: attempted to delete invisible tuple
Há uma lista de discussão de 2009 discutindo exatamente a mesma mensagem de erro, onde a OP a corrigiu, mas não encontro explicações sobre como ele fez ou o que poderia ter levado a esse erro.
Estou desamparado devido à falta de acessos no Google e ao meu conhecimento limitado do PostgreSQL.
O que levou à corrupção
Eu tive um servidor PostgreSQL 9.5.5 ( ~ 4 TB de dados, todas as configurações padrão, exceto limites de memória aumentados) ) rodando no Debian 8, quando o kernel do sistema operacional entrou em pânico - provavelmente durante a reconstrução de / dev / md1 onde o swap estava localizado. Antes disso, o PostgreSQL consumia quase todo o espaço em disco com um arquivo de log de 400 GB. O sistema operacional nunca inicializou novamente, as verificações de disco estavam OK, então eu inicializei a partir de um LiveCD e fiz o backup de cada dispositivo de bloco em imagens, apenas por precaução. Eu reconstruí com êxito / diretório de / dev / md2, o fsck mostrou um sistema de arquivos limpo e fiz backup da pasta PGDATA em um disco rígido externo.
O que eu fiz para tentar recuperar
Depois de formatar os dispositivos md e reinstalar o SO com um novo postgresql-9.5, parei o servidor PostgreSQL, movi e joguei a pasta PGDATA para o usuário do postgres e iniciei o servidor - tudo parecia bem, não havia erros.
Assim que comecei pg_dumpall
, morreu com
Error message from server: ERROR: timestamp out of range
Naturalmente, tentei excluir as tuplas ofensivas, apenas para acabar com o mesmo invisible tuple
erro repetidamente.
Coisas que tentei
Primeiro, as consultas DELETE falharam devido a páginas danificadas, por isso defini as seguintes configurações:
zero_damaged_pages = on
ignore_system_indexes = on
enable_indexscan = off
enable_bitmapscan = off
enable_indexonlyscan = off
Agora, notei que, quando executo as mesmas consultas novamente, o servidor zera as mesmas páginas repetidamente, sem ter certeza do que isso significa:
invalid page in block 92800 of relation base/16385/16443; zeroing out page
Eu tentei seguir em uma ordem indefinida:
pg_resetxlog -D $PGDATA
fez o seu trabalho sem erros ou mensagens- Excluídos todos os índices, incluindo restrições de pkey
CREATE TABLE aaa AS (SELECT * FROM comments);
leva aSegmentation fault
emheap_deform_tuple (tuple=tuple@entry=0x7f0d1be29b08, tupleDesc=tupleDesc@entry=0x7f0d1a35abe0, values=values@entry=0x7ffd57a5beb0, isnull=isnull@entry=0x7ffd57a65af0 "\001\001")
É reproduzível e deixa um núcleo de ~ 9 GB.SELECT COUNT(*) from comments;
permitidoVACUUM comments;
concluir, o mesmo truque não funciona em outras tabelas.SELECT COUNT(*) from photos;
eVACUUM photos;
agora morre comERROR: MultiXactId 302740528 has not been created yet -- apparent wraparound
- este assombra todas as tabelas, onde outros erros não aparecem mais.
Pensamentos
O DB estava sendo martelado por muitas gravações ( possivelmente duplicadas ) com aDB estava fazendo umON CONFLICT
cláusulaVACUUM
quando ocorreu o pânico do kernel, acredito que é o que resta dele que está causando problemasnonexistent MultiXactIds
einvisible tuple
- Os dados foram coletados com o rastreador ao longo de mais de 2 anos e estou totalmente de acordo em perder alguns deles
- Agora eu faço backups
- Não havia restrições relacionais entre tabelas nem gatilhos
Aqui está a saída pg_controldata a partir de agora:
pg_control version number: 942
Catalog version number: 201510051
Database system identifier: 6330224129664261958
Database cluster state: in production
pg_control last modified: Thu 08 Dec 2016 01:06:22 AM EET
Latest checkpoint location: 1562/8F9F8A8
Prior checkpoint location: 1562/8F7F460
Latest checkpoint's REDO location: 1562/8F9F8A8
Latest checkpoint's REDO WAL file: 000000010000156200000008
Latest checkpoint's TimeLineID: 1
Latest checkpoint's PrevTimeLineID: 1
Latest checkpoint's full_page_writes: on
Latest checkpoint's NextXID: 0/40781255
Latest checkpoint's NextOID: 67798231
Latest checkpoint's NextMultiXactId: 1
Latest checkpoint's NextMultiOffset: 0
Latest checkpoint's oldestXID: 615
Latest checkpoint's oldestXID's DB: 1
Latest checkpoint's oldestActiveXID: 0
Latest checkpoint's oldestMultiXid: 1
Latest checkpoint's oldestMulti's DB: 1
Latest checkpoint's oldestCommitTsXid:0
Latest checkpoint's newestCommitTsXid:0
Time of latest checkpoint: Thu 08 Dec 2016 01:06:22 AM EET
Fake LSN counter for unlogged rels: 0/1
Minimum recovery ending location: 0/0
Min recovery ending loc's timeline: 0
Backup start location: 0/0
Backup end location: 0/0
End-of-backup record required: no
wal_level setting: minimal
wal_log_hints setting: off
max_connections setting: 100
max_worker_processes setting: 8
max_prepared_xacts setting: 0
max_locks_per_xact setting: 64
track_commit_timestamp setting: off
Maximum data alignment: 8
Database block size: 8192
Blocks per segment of large relation: 131072
WAL block size: 8192
Bytes per WAL segment: 16777216
Maximum length of identifiers: 64
Maximum columns in an index: 32
Maximum size of a TOAST chunk: 1996
Size of a large-object chunk: 2048
Date/time type storage: 64-bit integers
Float4 argument passing: by value
Float8 argument passing: by value
Data page checksum version: 0
Atualizações
- ( 9 de dezembro de 2016 ) Ao ler sobre MultiXactIds inexistentes , lembrei-me de que meu banco de dados não estava sob carga operacional no momento do travamento, mas estava processando uma
VACUUM
solicitação manual . Coloquei os servidores da Web e os rastreadores offline depois que percebi que havia apenas 3% de espaço restante nos discos. Eu deveria ter verificado se/var/log
há arquivos grandes, mas culpei o PostgreSQL por engano e tenteiVACUUM FULL
, apenas para encontrá-lo abortado devido ao pouco espaço restante no dispositivo. Então eu comecei o VACUUM comum e deixei por isso mesmo. - ( 14 de dezembro de 2016 ) Baixou uma ramificação 9.5 das fontes do PostgreSQL do Github, comentou os blocos no heapam.c e no multixact.c e o compilou com a esperança de que não gerasse esses erros. Mas o servidor não foi iniciado, pois precisava ser configurado com os mesmos sinalizadores usados em um que eu tinha do APT. Havia cerca de 47 bandeiras, cada uma exigindo uma dependência com nome não óbvio, então desisti dessa ideia.
( 16 de dezembro de 2016 ) Encontrei uma maneira de livrar-se de tuplas com timestamps inválidos zerando as páginas relevantes. Primeiro, defino as seguintes opções em
psql
:\set FETCH_COUNT 1 \pset pager off
Eu faço então
SELECT ctid, * FROM comments;
. Dessa forma, ele cospectid
uma tupla incorreta antes que a consulta morra. Em seguida, prossigo para preencher essa página com zeros:dd if=/dev/zero of=/var/lib/postgresql/9.5/main/base/16385/16443 bs=8K seek=92803 count=1 conv=notrunc
mas cada página, zerada dessa maneira, quebra a página anterior, resultando em uma página16442
agora com uma tupla com carimbo de data / hora inválido. Não tenho certeza do que estou fazendo de errado aqui.( 16 de dezembro de 2016 ) A tentativa
pg_dump -Fc --table photos vw > photos.bak
resulta em falha de segmentação após a gravação de 1,3 GB ( provavelmente de 800 GB ). Aqui está o log do servidor:2016-12-16 18:48:05 EET [19337-2] LOG: server process (PID 29088) was terminated by signal 11: Segmentation fault 2016-12-16 18:48:05 EET [19337-3] DETAIL: Failed process was running: COPY public.photos (id, owner_id, width, height, text, date, link, thumb, album_id, time_found, user_id, lat, long) TO stdout; 2016-12-16 18:48:05 EET [19337-4] LOG: terminating any other active server processes 2016-12-16 18:48:05 EET [19342-2] WARNING: terminating connection because of crash of another server process 2016-12-16 18:48:05 EET [19342-3] DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. 2016-12-16 18:48:05 EET [19342-4] HINT: In a moment you should be able to reconnect to the database and repeat your command. 2016-12-16 18:48:05 EET [19337-5] LOG: all server processes terminated; reinitializing 2016-12-16 18:48:06 EET [29135-1] LOG: database system was interrupted; last known up at 2016-12-14 22:58:59 EET 2016-12-16 18:48:07 EET [29135-2] LOG: database system was not properly shut down; automatic recovery in progress 2016-12-16 18:48:07 EET [29135-3] LOG: invalid record length at 1562/A302F878 2016-12-16 18:48:07 EET [29135-4] LOG: redo is not required 2016-12-16 18:48:07 EET [29135-5] LOG: MultiXact member wraparound protections are now enabled 2016-12-16 18:48:07 EET [19337-6] LOG: database system is ready to accept connections 2016-12-16 18:48:07 EET [29139-1] LOG: autovacuum launcher started
Aqui está um pequeno stacktrace:
#0 pglz_decompress (source=source@entry=0x7fbfb6b99b13 "32;00/0ag4d/Jnz\027QI\003Jh3A.jpg", slen=<optimized out>, dest=dest@entry=0x7fbf74a0b044 "", rawsize=926905132) #1 0x00007fc1bf120c12 in toast_decompress_datum (attr=0x7fbfb6b99b0b) #2 0x00007fc1bf423c83 in text_to_cstring (t=0x7fbfb6b99b0b)
Não faço ideia de como contornar isso.
( 29 de dezembro de 2016 ) Eu escrevi um utilitário que faz isso
SELECT * FROM tablename LIMIT 10000 OFFSET 0
, aumentando o deslocamento e diminuindo as tuplas mortas, e ele duplicou com êxito os dados na minha máquina local, exceto as tuplas ( espero que as únicas ) que corrompi manualmente. Também deve esperar se o servidor reiniciar. No entanto, eu não tinha espaço suficiente no meu RAID e criei um espaçoslowdisk
de tabela em um disco rígido de 8 TB. Quando tentoCREATE DATABASE vwslow WITH TABLESPACE slowdisk
, isso não ocorre com erros:2016-12-29 02:34:13 EET [29983-1] LOG: request to flush past end of generated WAL; request 950412DE/114D59, currpos 1562/A3030C70 2016-12-29 02:34:13 EET [29983-2] CONTEXT: writing block 58368001 of relation base/16385/16473 2016-12-29 02:34:13 EET [29983-3] ERROR: xlog flush request 950412DE/114D59 is not satisfied --- flushed only to 1562/A3030C70 2016-12-29 02:34:13 EET [29983-4] CONTEXT: writing block 58368001 of relation base/16385/16473 2016-12-29 02:34:13 EET [30005-44212] postgres@vw ERROR: checkpoint request failed 2016-12-29 02:34:13 EET [30005-44213] postgres@vw HINT: Consult recent messages in the server log for details. 2016-12-29 02:34:13 EET [30005-44214] postgres@vw STATEMENT: CREATE DATABASE vwslow WITH TABLESPACE slowdisk;
Manual
CHECKPOINT
resultou nos mesmos erros.Uma reinicialização do servidor fez com que o erro do ponto de verificação desaparecesse e deixe-me executar minha ferramenta. Responderei à minha pergunta e publicará o código, se funcionar.
Respostas:
Bem, eu consegui automatizar o processo de recuperação
SELECT
eINSERT INTO
, pulando intervalos e aguardando se o servidor travasse. Eu o codifiquei primeiro no Node - ele extraiu dados não danificadoscomments
e ainda continua.Ontem decidi experimentar o Golang, e aqui está um repositório com o código Go: https://github.com/kaivi/pg_ripper Atualizá-lo-emos em breve para que ele realmente funcione em torno de tuplas ruins e não desista de todo intervalo contendo um.
fonte