Limitar a espera certa
Com A a conservar o lock, B usa lock_timeout de 80 milissegundos e statement_timeout de cinco segundos. A tentativa de UPDATE falha com 55P03 e diagnóstico de lock timeout. O limite diz respeito à aquisição de um lock; não é um orçamento total para toda a transação. O guião configura valores curtos apenas nas sessões do exercício para tornar o comportamento observável. Não recomenda esses valores para produção. Num serviço real, associa limites ao orçamento de latência, à possibilidade de repetir e ao comportamento do cliente quando a tentativa termina sem executar a atualização.
O mesmo SQLSTATE pode ter causas operacionais distintas
A seguir, B usa statement_timeout de 80 milissegundos e lock_timeout de um segundo. O statement termina primeiro e devolve 57014 com mensagem de statement timeout. O cancelamento explícito anterior também produziu 57014, mas com outra origem. Guarda código, diagnóstico e configuração efetiva antes de atribuir a causa. Uma aplicação deve usar códigos estruturados para classificar erros, sem perder os detalhes necessários à investigação. O laboratório não mede precisão de temporizadores nem promete que uma operação termina num instante exato. Mostra qual limite interrompeu a query nas condições controladas e confirma a necessidade de recuperar a transação.
Usar um savepoint com uma intenção explícita
B começa uma transação, insere a nota before e cria retry_step. O UPDATE bloqueado falha; B regressa ao savepoint, insere after e confirma. Ambas as notas ficam gravadas, mas a atualização bloqueada não ocorreu. Esta é uma escolha de âmbito, não uma repetição automática da transação inteira. O exemplo permite observar que trabalho anterior ao savepoint continua elegível para commit. Num fluxo real, pergunta se confirmar esses efeitos sem a atualização principal respeita o requisito. Se a operação de negócio for indivisível, conservar as notas não é por si uma recuperação correta e pode exigir rollback total.
Reproduzir um ciclo de espera
Duas sessões novas atualizam linhas diferentes, subtraindo dez unidades. Depois, cada uma tenta atualizar a linha que a outra conserva. O observador confirma a primeira espera antes de iniciar a segunda, formando o ciclo de modo controlado. PostgreSQL interrompe uma das transações com 40P01 e permite à outra continuar. O teste exige exatamente uma vítima, sem escolher qual PID deve perder. A deteção não significa que todos os participantes foram revertidos. Depois de limpar a transação rejeitada e confirmar a sobrevivente, ambas as linhas têm 90: o trabalho parcial da vítima foi desfeito e as duas atualizações da sobrevivente foram confirmadas.
Mudar a ordem sem prometer ausência universal de deadlocks
O último ensaio faz as duas sessões pedir as linhas na mesma ordem. A primeira conserva a linha 1; a segunda espera nessa linha. A primeira consegue atualizar a linha 2 e confirmar, permitindo à segunda atualizar ambas. Os dois commits deixam 80 em cada linha. A ordem consistente elimina o ciclo específico reproduzido, mas não demonstra que uma aplicação inteira ficou livre de deadlocks. Triggers, relações adicionais e outros caminhos de código podem introduzir dependências. Conserva tratamento de erros e uma estratégia limitada de repetição da transação completa, com novas leituras e reconciliação dos efeitos externos quando existam.
Entregar uma decisão de recuperação verificável
Para a oficina fictícia, apresenta o erro e pede ao participante que indique o estado da ligação, o trabalho confirmado e o próximo comando autorizado. Uma resposta que apenas diz repetir é insuficiente: deve distinguir rollback completo, retorno a savepoint, nova transação e intervenção no bloqueador. O laboratório regista os resultados reais e remove o cluster depois de juntar todas as threads de queries. Não implementa retries de aplicação, pools ou deduplicação de pedidos externos. A validação representativa deve incluir esses elementos, limites de tentativas, observabilidade e critérios funcionais, além de revisão humana especializada antes de afirmar prontidão operacional.
-- Run only in the disposable workshop with its owned fixture.
BEGIN;
INSERT INTO notes VALUES (1, 'before');
SAVEPOINT retry_step;
SET LOCAL lock_timeout = '80ms';
-- If the following statement fails, the client must choose recovery scope:
UPDATE balances SET units = 70 WHERE id = 1;
-- The teaching branch uses ROLLBACK TO SAVEPOINT retry_step;
-- then inserts its second note and commits only that intended partial scope.
lock_timeout produz 55P03; um statement_timeout mais curto produz 57014. Um savepoint permite recuperar um passo sem perder as notas anteriores.
Armadilhas comuns
Classificar toda a espera como deadlock; repetir apenas o último UPDATE após abort; escolher uma vítima fixa; converter um timeout em autorização de commit.
Tópicos relacionados: Transações e concorrência · Diagnóstico e passagem para suporte
Recuperação correta depende do âmbito que falhou e do requisito de negócio. Um código de erro sozinho não determina o que repetir.
Referência: Statement, lock and transaction timeouts · PostgreSQL 18 reference semantics;18.6 current stable at review