Estou criando um script de backup/restauração para um pequeno banco de dados postgres 12.2, rodando dentro de um container docker. O backup se parece com:
docker exec -e PGPASSWORD=${passwd} ${container} \
pg_dump -h ${host} -U ${username} ${db} \
-Fc > ${backupdir}${db}_$(date +"%Y-%m-%d-%H.%M.%S.%N").pgdump
Em seguida, crio um novo banco de dados e tento restaurar nele como:
docker exec -i ${container} pg_restore -Fc --verbose --clean \
--no-acl --no-owner -U ${username} \
-d ${testdb} < ${backupdir}${db}_ ...
Isso parece funcionar para várias tabelas e, em seguida, congela em uma tabela específica, todas as vezes. Nada parece acontecer, embora o Postgres esteja usando 80% da CPU. Deixei funcionando por 10 horas sem terminar.
A tabela em questão contém ~ 1E6 linhas, uma tabela 3 vezes maior restaura em cerca de 10 segundos. A tabela contém uma coluna gerada:
CREATE TABLE MIND.FILER
( DOKUMENT_UUID UUID NOT NULL -- document_uuid
, SIDNUMMER INTEGER NOT NULL -- page number
, FILTYP TEXT NOT NULL -- file type
, TILLSTAND SMALLINT NOT NULL -- state
, FILNAMN TEXT NOT NULL -- name of file
, FULLSTANDIGT_FILNAMN TEXT GENERATED ALWAYS AS -- complete file name including path
(mind.uuid_2_path(dokument_uuid) || rtrim(filnamn)) STORED
, CONSTRAINT PK_FILER PRIMARY KEY (DOKUMENT_UUID, SIDNUMMER, FILTYP)
, CONSTRAINT FK_SIDOR
FOREIGN KEY (DOKUMENT_UUID, SIDNUMMER)
REFERENCES MIND.SIDOR (DOKUMENT_UUID, SIDNUMMER)
ON DELETE CASCADE
ON UPDATE CASCADE
, CONSTRAINT FK_FILTYPER
FOREIGN KEY (FILTYP)
REFERENCES MIND.FILTYPER (FILTYP)
ON DELETE CASCADE
ON UPDATE CASCADE
);
mas tenho a impressão de que isso não deve afetar a restauração, pelo menos não nessa medida.
A função MIND.UUID_2_PATH localiza o ponto de montagem no sistema de arquivos, dependendo da versão do UUID utilizada.
CREATE OR REPLACE FUNCTION MIND.UUID_2_PATH(DUID UUID)
RETURNS TEXT AS $$
DECLARE s text;
DECLARE ss text;
DECLARE uuid_v int;
BEGIN
SELECT substr(DUID::text,15,1) into uuid_v;
IF uuid_v = 4 THEN
SELECT REPLACE(CAST(DUID AS TEXT),'-','') INTO s;
SELECT monteringspunkt
||SUBSTR(s,1,4)||'/'
||SUBSTR(s,5,4)||'/'
||SUBSTR(s,9,4)||'/'
||SUBSTR(s,13,4)||'/'
||SUBSTR(s,17,4)||'/'
||SUBSTR(s,21,4)||'/'
||SUBSTR(s,25,4)||'/'
||SUBSTR(s,29,4)||'/' INTO ss
FROM mind.filsystemsmonteringar
WHERE uuid_version = 4;
ELSE -- uuid_v = 3
SELECT lpad(dokument_id::text, 10,'0') into s FROM MIND.DOKUMENT where dokument_uuid = DUID;
SELECT monteringspunkt
||SUBSTR(s,1,3)||'/'
||SUBSTR(s,4,3)||'/'
||SUBSTR(s,7,2)||'/'
||s||'/' INTO ss
FROM mind.filsystemsmonteringar
WHERE uuid_version = 3;
END IF;
RETURN ss;
end;
$$
LANGUAGE plpgsql
IMMUTABLE
RETURNS NULL ON NULL INPUT;
Pode não ser a maneira mais eficiente de fazer isso, mas carregar milhões de linhas na tabela usando \copy tem um desempenho decente.
De vez em quando aparecem entradas como:
2020-10-06 12:36:17.078 UTC [27] LOGG: checkpoint starting: time
2020-10-06 12:38:47.123 UTC [27] LOGG: checkpoint complete: wrote 689 buffers (4.2%); 0 WAL file(s) added, 0 removed, 10 recycled; write=149.943 s, sync=0.000 s, total=150.045 s; sync files=79, longest=0.000 s, average=0.000 s; distance=163338 kB, estimate=163338 kB
2020-10-06 12:41:17.223 UTC [27] LOGG: checkpoint starting: time
2020-10-06 12:41:17.743 UTC [27] LOGG: checkpoint complete: wrote 5 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.503 s, sync=0.013 s, total=0.519 s; sync files=4, longest=0.012 s, average=0.003 s; distance=369 kB, estimate=147041 kB
no registro. Pelo que posso dizer, nada foi feito com a configuração do Postgres. Eu tive que aumentar o shm, mas fora isso é baunilha simples. Não tenho certeza de quais áreas/configurações de memória podem melhorar o desempenho do pg_restore. Como você pode ver, sou novo no postgres (ser um imbecil no docker também não parece ajudar ;-). Portanto, quaisquer dicas sobre o que pode causar esse "congelamento" ou onde procurar mais informações serão apreciadas.
EDIT: com a ajuda que @laurenz-albe forneceu, eu poderia cancelar uma restauração mais antiga e voila a restauração restante foi concluída em minutos.
Em seguida, larguei o banco de dados, criei-o novamente e verifiquei a seguinte consulta que resultou em 0 linhas:
select pid, application_name, backend_start, state_change, backend_type
from pg_stat_activity
where datname = 'testdb';
Em seguida, iniciei uma nova restauração como antes e verifiquei a consulta novamente. O resultado é 1 back-end de cliente e 2 trabalhadores paralelos, os trabalhadores parecem reiniciar de vez em quando, mas a restauração parece travada.
Olhando para a consulta:
select backend_type, query
from pg_stat_activity
where datname = 'testdb';
mostra que:
client backend | COPY mind.filer (dokument_uuid, sidnummer, filtyp, tillstand, filnamn) FROM stdin; +
|
parallel worker | SELECT lpad(dokument_id::text, 10,'0') FROM MIND.DOKUMENT where dokument_uuid = DUID
parallel worker | SELECT lpad(dokument_id::text, 10,'0') FROM MIND.DOKUMENT where dokument_uuid = DUID
então os trabalhadores estão executando uma consulta da função usada na coluna gerada. É possível que o pg_restore esteja se bloqueando devido à dependência de outra tabela?
É possível ter pg_restore virar a expressão gerada durante \copy? Fiquei com a impressão de que esse era o padrão afinal o valor já existe no dump.
Depois de matar os trabalhadores:
postgres=# select pg_cancel_backend(pid)
from pg_stat_activity
where datname = 'testdb' and backend_type = 'parallel worker';
pg_cancel_backend
-------------------
t
t
(2 rader)
A restauração continua, mas a tabela fica vazia após a restauração
Você está preso atrás de um bloqueio mantido por uma transação de longa duração. Examine
pg_stat_activity
para encontrar transações de longa duração e procurepg_locks
detalhes sobre quem bloqueia o quê.Se você não encontrar uma maneira melhor, mate as transações de bloqueio com a
pg_cancel_backend
função.Acho que o problema no centro disso é a função que gera a coluna, que seleciona de outra tabela. Isso parece estranho - isso não significa que a coluna gerada fica errada quando
filsystemsmonteringar
é modificada? De qualquer forma, posso facilmente imaginar um impasse entre ospg_restore
trabalhos se um selecionar de uma tabela que outroALTER
s.