CodeGym /Cursos /SQL SELF /Erros comuns ao trabalhar com transações

Erros comuns ao trabalhar com transações

SQL SELF
Nível 40 , Lição 4
Disponível

Nessa aula, a gente vai focar nos erros típicos que rolam quando mexemos com transações e como fugir deles. Confia: até o SQL-hero mais brabo às vezes esquece de mandar um COMMIT! Vamos dar umas dicas pra esses vacilos virarem coisa rara.

Infelizmente (ou felizmente?), banco de dados não é castelo mágico onde tudo sempre funciona perfeito. Erros com transações são bem comuns, principalmente pra quem tá começando. Bora destrinchar isso aí.

Esquecendo o COMMIT ou ROLLBACK

Esquecer de finalizar a transação é o famoso "clássico do rolê". Imagina um restaurante onde você pediu a comida, mas o garçom esqueceu de trazer a conta. No mundo do PostgreSQL, isso significa que o banco "fica preso" no estado da transação, segurando recursos e travando outras operações.

Exemplo de erro:

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- Opa! Esquecemos de colocar COMMIT ou ROLLBACK.

Quando a transação "fica presa", o bloqueio pode pegar a tabela toda. Se o DBA sacar que deu ruim, ele pode finalizar ela "na marra". Mas melhor não deixar chegar nesse ponto.

Como evitar?

  • Sempre finalize a transação de forma explícita: COMMIT ou ROLLBACK.
  • Use ferramentas de cliente que avisam automaticamente sobre transações penduradas.
  • Se a transação não foi finalizada e você reiniciar o app, o banco vai dar ROLLBACK automático, mas nem sempre isso é legal pro estado do sistema.

Usando o nível de isolamento errado

Parecer besteira, mas escolher o nível de isolamento certo é chave pra evitar anomalias. Por exemplo, se você usa READ UNCOMMITTED numa operação financeira importante, pode acabar lendo dados "sujos" que depois vão ser desfeitos.

Exemplo:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;
-- Lendo dados que outra transação tá mudando
SELECT balance FROM accounts WHERE account_id = 1;
-- Outra transação faz ROLLBACK e seus dados ficam inválidos.

Como evitar?

  • Decida o quanto os dados são importantes pro seu app.
  • Use READ COMMITTED na maioria dos casos pra evitar leitura "suja".
  • Use níveis mais rígidos tipo REPEATABLE READ ou SERIALIZABLE quando for crítico evitar mudanças ou dados fantasmas.

Conflito de transações e locks

Às vezes duas ou mais transações tentam mudar os mesmos dados. Aí o PostgreSQL trava uma delas até a outra terminar. Isso pode virar um deadlock (bloqueio mútuo).

Exemplo de erro:

-- Primeira transação
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;

-- Segunda transação
BEGIN;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 1;
-- Esperando a primeira transação...

Se as duas seguram recursos que a outra precisa, rola deadlock. O PostgreSQL detecta, mata uma das transações e mostra a mensagem:

ERROR: deadlock detected

Como evitar?

  • Siga sempre a mesma ordem de operações nas transações.
  • Deixe as transações o mais rápidas possível pra diminuir o risco de lock.
  • Use SERIALIZABLE só quando for realmente necessário.

Erro com SAVEPOINT

SAVEPOINT é massa pra fazer rollback parcial, mas se usar errado pode dar confusão. Por exemplo, se esquecer de liberar o savepoint (RELEASE SAVEPOINT), pode acabar com locks desnecessários ou erros.

Exemplo de erro:

BEGIN;
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
ROLLBACK TO SAVEPOINT my_savepoint;
-- Esquecemos de liberar o SAVEPOINT!

Como evitar?

  • Confere se você remove o SAVEPOINT quando não precisa mais dele.
  • Evite criar savepoints demais pra não complicar as queries.

Incompatibilidade de transações com sistemas externos

Imagina que uma transação no PostgreSQL tenta interagir com sistemas externos: mandar notificação, atualizar API, etc. Se der ruim no sistema externo, fica difícil desfazer as mudanças.

Exemplo:

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- Não conseguiu mandar notificação: email-server não responde.
COMMIT; -- Mudanças salvas, mas notificação não foi enviada.

Como evitar?

  • Se possível, separe as operações que falam com sistemas externos.
  • Use tabelas intermediárias ou filas de tarefas pra coordenar ações com sistemas externos.

Erros por transações grandes demais

Transações grandes, com várias operações, tendem a ser mais vulneráveis a erros: locks, timeouts e deadlocks.

Exemplo:

BEGIN;
-- Milhares de operações de update
UPDATE orders SET status = 'completed' WHERE delivery_date < CURRENT_DATE;
COMMIT; -- Pode demorar bastante.

Como evitar?

  • Quebre transações grandes em várias menores.
  • Use batches pra atualizar os dados.
  • Minimize a quantidade de dados alterados numa transação só.

Esquecendo de checar erros

Nem todo SQL dentro da transação vai rodar de boa. Se uma operação der erro, a transação toda fica zoada.

Exemplo:

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = -1; -- Erro: account_id não existe.
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
COMMIT; -- Não rola por causa do erro.

Como evitar?

  • Sempre confira o resultado de cada operação.
  • Use tratamento de erro nas queries ou no código cliente.

Entendendo errado o comportamento do ROLLBACK

Muita gente acha que ROLLBACK desfaz tudo e volta ao estado inicial. Mas ROLLBACK só funciona dentro da transação atual.

Exemplo de confusão:

UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
ROLLBACK; -- Erro! Não funciona porque a operação não tava numa transação.

Como evitar?

  • Lembre: BEGIN é seu parça, sem ele o ROLLBACK não faz nada.
  • Sempre coloque operações críticas dentro de transações.
1
Pesquisa/teste
Introdução aos níveis de isolamento de transações, nível 40, lição 4
Indisponível
Introdução aos níveis de isolamento de transações
Introdução aos níveis de isolamento de transações
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION