De acordo com os documentos:
CONCORRENTEMENTE Atualize a visualização materializada sem bloquear seleções simultâneas na visualização materializada. (...)
... OUTROS CONTEÚDOS ...
Mesmo com esta opção, apenas um REFRESH de cada vez pode ser executado em qualquer visualização materializada .
Eu tinha uma função que verificava o tempo da última atualização para uma MATERIALIZED VIEW e, se tivessem passado mais de 60 segundos, seria para atualizá-la.
No entanto, o que aconteceria se eu tentasse atualizar uma visão materializada de dois processos separados ao mesmo tempo? eles enfileirariam ou gerariam um erro?
Existe uma maneira de detectar quando uma MATERIALIZED VIEW está sendo atualizada e, portanto, evitar tocá-la?
Atualmente, recorri a preencher um registro de tabela antes de atualizar (definir refreshing
como true
) e, em seguida, defini-lo para false
quando o processo for concluído.
EXECUTE 'INSERT INTO refresh_status (last_update, refreshing)
VALUES (clock_timestamp(), true) RETURNING id') INTO refresh_id;
EXECUTE 'REFRESH MATERIALIZED VIEW CONCURRENTLY my_mat_view';
EXECUTE 'UPDATE refresh_status SET refreshing=false WHERE id=$1' USING refresh_id;
Então, sempre que chamo esse procedimento, verifico o mais recente last_update
e seu refreshing
valor. Se refreshing
for true, não tente atualizar a visualização materializada.
EXECUTE 'SELECT
extract(epoch FROM now() - (last_update))::integer,
refreshing
FROM refresh_status
ORDER BY last_update DESC
LIMIT 1' INTO update_seconds_ago, refreshing;
IF(updated_seconds_ago > 60 AND refreshing = FALSE) THEN
-- the refresh block above
END IF;
No entanto, não tenho certeza se o sinalizador de atualização está sendo atualizado de forma síncrona (quer dizer, ele realmente espera que a atualização seja realmente concluída)
Essa abordagem é racional ou estou perdendo alguma coisa aqui?
Como mencionado nesta resposta , "
REFRESH MATERIALIZED VIEW CONCURRENTLY
pega umEXCLUSIVE
bloqueio" na mesa. Seguindo a trilha de migalhas para a documentação , podemos ler que umEXCLUSIVE
bloqueio em uma tabela "permite apenasACCESS SHARE
bloqueios simultâneos, ou seja, apenas leituras da tabela podem prosseguir". No mesmo parágrafo podemos ver que "EXCLUSIVE
conflita com ...EXCLUSIVE
", significando que outraREFRESH MATERIALIZED VIEW CONCURRENTLY
instrução, que solicita o mesmoEXCLUSIVE
bloqueio, terá que esperar até que oEXCLUSIVE
bloqueio anterior seja liberado.Se você quiser evitar esperar por esse bloqueio por um período indefinido, você pode querer definir a variável de sessão
lock_timeout
para um valor sensato.Conforme observado por mustaccio , essa questão se sobrepõe significativamente aos bloqueios de exibição materializados de atualização do Postgres .
No entanto, embora a resposta aceita para essa pergunta tenha um link que responda a essa, a resposta a essa pergunta não está diretamente incluída nessa.
Então, para ser específico: De acordo com a página de manual do PostgreSQL sobre bloqueio explícito (o link é para a página da versão atual, para o PostGres 10),
REFRESH MATERIALIZED VIEW CONCURRENTLY
leva umEXCLUSIVE
bloqueio. OEXCLUSIVE
bloqueio parece bloquear todos os outros bloqueios, excetoACCESS SHARE
- que inclui outrosEXCLUSIVE
bloqueios.Assim, uma segunda
REFRESH MATERIALIZED VIEW CONCURRENTLY
requisição na mesma view irá aguardar a liberação do lock obtido pela primeira.Graças às respostas de mustaccio e RDFozz , finalmente entendi que
REFRESH ... CONCURRENTLY
fazer um bloqueio exclusivo é o motivo pelo qual a documentação do PostgreSQL diz :Eu estava com medo de que isso significasse que qualquer tentativa de fazer uma atualização simultânea geraria um erro , mas à luz de suas respostas, não há nenhum erro especial envolvido. É apenas uma questão de bloqueios que enfileirarão tentativas simultâneas. Portanto, a documentação poderia ser interpretada como: