9.4. Verificação da consistência dos dados no nível da aplicação

Uma vez que a leitura no PostgreSQL não bloqueia os dados, sem levar em consideração o nível de isolamento da transação, os dados lidos por uma transação podem ser sobrescritos por outra transação concorrente. Em outras palavras, se uma linha é retornada pelo comando SELECT, isto não significa que esta linha ainda é a linha corrente no instante em que é retornada (ou seja, algum tempo depois que o comando corrente começou). A linha pode ter sido modificada ou excluída por uma transação já efetivada, que efetuou esta efetivação após a transação ter começado. Mesmo que a linha ainda seja válida "agora", esta linha pode ser mudada ou excluída antes da transação corrente efetivar ou desfazer suas modificações.

Outra maneira de pensar em relação a isto é que cada transação enxerga um instantâneo do conteúdo do banco de dados, e as transações executando concorrentemente podem perfeitamente enxergar instantâneos diferentes. Portanto, o próprio conceito de "agora" é de alguma forma suspeito. Normalmente isto não é um grande problema quando as aplicações cliente estão isoladas uma das outras, mas se os clientes podem se comunicar por meio de canais por fora do banco de dados, então sérias confusões podem acontecer.

Para garantir a validade corrente de uma linha e protegê-la contra atualizações concorrentes, deve ser utilizado o comando SELECT FOR UPDATE ou uma declaração LOCK TABLE apropriada (o comando SELECT FOR UPDATE bloqueia apenas as linhas selecionadas contra atualizações concorrentes, enquanto o LOCK TABLE bloqueia toda a tabela). Isto deve ser levado em consideração ao portar aplicações de outros ambientes para o PostgreSQL.

Nota: Antes da versão 6.5 o PostgreSQL utilizava bloqueios de leitura e, portanto, as considerações acima também se aplicam quando é feita uma atualização de uma versão do PostgreSQL anterior a 6.5.

As verificações globais de validade requerem considerações extras sob o MVCC. Por exemplo, uma aplicação bancária pode desejar verificar se a soma de todos os créditos em uma tabela é igual a soma de todos os débitos em outra tabela, no momento em que as duas tabelas estão sendo ativamente atualizadas. Comparar os resultados de dois comando SELECT SUM(...) sucessivos não vai funcionar confiavelmente no modo Read Committed, porque o segundo comando, provavelmente, inclui resultados de transações que não apareciam no primeiro comando. Realizar as duas somas em uma mesma transação serializável fornece uma imagem precisa dos efeitos das transações efetivadas antes do início da transação serializável --- mas pode ser legitimamente questionado se a resposta ainda é relevante na hora que foi produzida. Se a própria transação serializável introduziu algumas mudanças antes de tentar efetuar a verificação de consistência, o valor prático da verificação fica ainda mais discutível, porque agora são incluídas algumas, mas não todas, as mudanças ocorridas após o início da transação. Em casos como este, uma pessoa cuidadosa pode desejar bloquear todas as tabelas necessárias para fazer a verificação, para obter uma imagem da situação atual acima de qualquer suspeita. Um bloqueio no modo SHARE (ou superior) garante não haver mudanças não efetivadas na tabela bloqueada, fora as mudanças efetuadas pela própria transação corrente.

Observe também que, quando a prevenção contra alterações concorrentes está baseada em bloqueios explícitos, deve ser utilizado o modo Read Committed, ou tomar-se o cuidado de obter os bloqueios antes de executar os comandos no modo serializável. Um bloqueio explícito obtido em uma transação serializável garante que nenhuma outra transação modificando a tabela está executando --- mas se o instantâneo enxergado pela transação for anterior à obtenção do bloqueio, pode ser que seja anterior a algumas mudanças na tabela que agora estão efetivadas. Um instantâneo de uma transação serializável é, na verdade, tirado no início do primeiro comando (SELECT, INSERT, UPDATE ou DELETE) sendo, portanto, possível obter o bloqueio explícito antes do instantâneo ser tirado.