O que acontece se dois processos tentam ATUALIZAR A VISÃO MATERIALIZADA CONCORRENTE, ao mesmo tempo?

13

De acordo com os documentos:

Atualize CONCORRENTE a vista materializada sem bloquear as opções simultâneas na vista materializada. (...)

... OUTROS CONTEÚDOS ...

Mesmo com essa opção, apenas uma atualização por vez pode ser executada em qualquer visualização materializada .

Eu tinha uma função que verificava o último tempo de atualização para uma VISÃO MATERIALIZADA e, se tivessem passado mais de 60 segundos, seria necessário atualizá-la.

No entanto, o que aconteceria se eu tentasse atualizar uma exibição materializada de dois processos separados ao mesmo tempo? eles colocariam na fila ou gerariam um erro?

Existe uma maneira de detectar quando uma VISTA MATERIALIZADA está sendo atualizada e, portanto, evitar tocá-la?

Atualmente, recorri ao preenchimento de um registro da tabela antes de atualizar (configurando refreshingpara true) e depois defini-lo para falsequando o processo terminar.

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_updatee seu refreshingvalor. Se refreshingfor verdade, não tente atualizar a exibiçã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 (quero dizer, ele realmente aguarda a atualização ser concluída)

Essa abordagem é racional ou estou perdendo alguma coisa aqui?

ffflabs
fonte

Respostas:

13

Conforme mencionado na esta resposta , " REFRESH MATERIALIZED VIEW CONCURRENTLYleva uma EXCLUSIVEfechadura" em cima da mesa. Seguindo a trilha de migalhas para a documentação , podemos ler que um EXCLUSIVEbloqueio em uma tabela "permite apenas ACCESS SHAREbloqueios simultâneos , ou seja, somente leituras da tabela podem prosseguir". No mesmo parágrafo, podemos ver que " EXCLUSIVEentra em conflito com ... EXCLUSIVE", significando que outra REFRESH MATERIALIZED VIEW CONCURRENTLYinstrução, que solicita o mesmo EXCLUSIVEbloqueio, terá que esperar até que o EXCLUSIVEbloqueio anterior seja liberado.

Se você deseja evitar a espera desse bloqueio por um período indefinido, convém definir a variávellock_timeout da sessão para um valor razoável.

mustaccio
fonte
PS: você acha que faz sentido manter essa tabela auxiliar para contar tentativas simultâneas (sem trocadilhos) de que o MAT VIEW está ocupado e, portanto, deve ser deixado sozinho, mesmo que pareça precisar de uma atualização?
Ffflabs
É uma questão de opinião; se você acha que isso ajuda, é claro que pode manter sua lógica. Observe, no entanto, que sua função está sujeita às condições da corrida e, portanto, não é 100% confiável.
mustaccio
Você acha que é viável verificar pg_locks e ver se há uma referência à exibição de tapete?
Ffflabs 13/03/19
Mais uma vez, a condição de corrida é possível: há uma chance de que um bloqueio seja colocado entre você verificar pg_lockse iniciar a atualização. Uma maneira adequada de resolver conflitos de bloqueio é definir o tempo limite e manipular o erro.
mustaccio
3

Conforme observado pelo mustaccio , essa pergunta 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 pergunta, a resposta a essa pergunta não está diretamente incluída nessa.

Portanto, 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 CONCURRENTLYaceita um EXCLUSIVEbloqueio. O EXCLUSIVEbloqueio parece bloquear todos os outros bloqueios, exceto ACCESS SHARE - isso inclui outros EXCLUSIVEbloqueios.

Portanto, uma segunda REFRESH MATERIALIZED VIEW CONCURRENTLYsolicitação na mesma exibição aguardará a liberação do bloqueio obtido pelo primeiro.

RDFozz
fonte
Obrigado. Eu ainda marquei a resposta de @ mustaccio como aceita, pois ele editou o texto para ser mais específico à minha pergunta.
Ffflabs 13/03/19
0

Graças às respostas de mustaccio e RDFozz , finalmente entendi que REFRESH ... CONCURRENTLYusar um bloqueio exclusivo é a razão pela qual a documentação do PostgreSQL diz :

Mesmo com essa opção, apenas uma atualização por vez pode ser executada em qualquer visualização materializada .

Eu tinha 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 enfileiram tentativas simultâneas. Portanto, a documentação poderia ser interpretada como:

O bloqueio adquirido durante esta operação impedirá qualquer operação que não seja a leitura da VISTA MATERIALIZADA. Tentativas adicionais de atualizar a Visualização Materializada enquanto um REFRESH ... CONCURRENTLY estiver em execução serão colocadas na fila até o primeiro bloqueio ser liberado.

ffflabs
fonte