19 de Fevereiro de 2025
Transações na prática: commit, rollback e savepoint
Como pensar transação como proteção contra estado pela metade, o que commit e rollback realmente fazem e onde savepoint entra sem virar enfeite raro.
Andrews Ribeiro
Founder & Engineer
6 min Intermediario Sistemas
O problema
Transação costuma ser ensinada de um jeito meio ritualístico.
- abre transação
- faz umas operações
- commit no final
Só que isso não ajuda muito a entender o motivo.
O problema real é outro:
às vezes uma regra de negócio depende de mais de uma escrita, e o banco não pode parar no meio do caminho.
Exemplo clássico:
- debitar saldo de uma conta
- creditar saldo em outra
- registrar o histórico
Se uma parte entra e outra falha, o dado fica pela metade.
E dado pela metade costuma ser pior do que erro explícito.
Porque depois ninguém sabe se precisa refazer, compensar ou investigar fraude.
Então transação não existe para “rodar várias queries juntas”.
Ela existe para proteger uma mudança que só faz sentido completa.
Modelo mental
Pense assim:
transação é uma janela curta em que o banco trata várias mudanças como um pacote só.
Enquanto esse pacote não fecha, você ainda não quer assumir que aquilo virou verdade definitiva.
Nesse contexto:
commitconfirma o pacoterollbackjoga fora o que aconteceu dentro delesavepointcria um ponto intermediário para voltar sem perder tudo
Essa é a leitura mais útil.
Não é mágica.
Não é “modo seguro”.
É um mecanismo para dizer:
- ou essa mudança inteira vale
- ou essa mudança inteira não vale
E isso vem com custo.
Porque, durante essa janela, o banco pode segurar lock, ocupar conexão e aumentar disputa com outras operações.
Transação boa protege a regra sem ficar aberta mais tempo do que precisa.
Quebrando o problema
Transação faz sentido quando a regra atravessa mais de uma escrita
Se uma única operação atômica já resolve, melhor.
Mas quando a regra depende de duas ou mais mudanças coerentes, transação costuma entrar.
Casos comuns:
- criar pedido e reservar estoque
- registrar pagamento e atualizar saldo
- apagar registros relacionados em conjunto
- gravar dado principal e seu histórico
O ponto não é a quantidade de queries.
O ponto é a dependência lógica entre elas.
Commit não é “acabou o código, então persiste”
Commit é o momento em que você aceita aquele novo estado como válido.
Isso parece detalhe de linguagem, mas muda o raciocínio.
Porque o ideal é chegar no commit só depois que:
- as validações importantes passaram
- as escritas necessárias aconteceram
- a invariável da operação continua verdadeira
Se você trata commit como formalidade, começa a desenhar fluxo sem clareza sobre qual condição realmente fecha a operação.
Rollback protege contra estado parcial
Rollback não é só “deu exception”.
Ele existe para descartar uma mudança que deixou de ser válida antes do fechamento.
Pode ser erro técnico:
- timeout
- violação de constraint
- deadlock
Ou erro de regra:
- saldo insuficiente
- estoque acabou
- pedido já foi processado
O que importa é:
se a operação não pode terminar correta, melhor apagar o pacote inteiro do que deixar resto espalhado.
Savepoint é um checkpoint dentro da transação
Muita gente quase nunca usa savepoint e tudo bem.
Mas é útil entender para não parecer palavra decorada.
Savepoint é um ponto intermediário para o qual você pode voltar sem jogar fora toda a transação.
Isso ajuda quando existe uma parte opcional ou recuperável dentro do fluxo.
Você mantém o núcleo da operação e desfaz só o trecho problemático.
Não é recurso para qualquer caso.
É ferramenta de precisão.
Transação longa demais cobra caro
Outro erro comum é pensar só em corretude e esquecer custo operacional.
Quanto mais tempo a transação fica aberta, maior a chance de:
- lock mais demorado
- contenção
- timeout
- throughput pior
- disputa com outras transações
Então a pergunta madura não é só “preciso de transação?”.
Também é:
- por quanto tempo ela vai ficar aberta?
- o que realmente precisa ficar dentro dela?
Chamada externa dentro da transação costuma ser má ideia
Esse ponto aparece muito em sistema real.
Exemplo ruim:
- abre transação
- grava pedido
- chama API de pagamento
- espera resposta
- commit
Enquanto você espera rede, o banco fica com a transação aberta sem necessidade.
Isso aumenta risco e contenção.
Em geral, o mais saudável é:
- deixar dentro da transação só o que precisa de consistência local no banco
- empurrar efeito externo para depois, ou usar padrão como outbox quando fizer sentido
Quando tudo vai para dentro da transação por medo, o sistema troca estado parcial por contenção desnecessária.
Transação não resolve sistema distribuído inteiro
Esse também é um tropeço clássico.
Transação do banco resolve bem o que está dentro daquele contexto transacional.
Ela não garante, sozinha:
- consistência entre dois serviços
- sincronismo com fila
- sucesso de e-mail
- efeito em sistema terceiro
Quando o fluxo cruza fronteiras, o problema deixa de ser só “abre transação”.
Exemplo simples
Imagine uma transferência entre duas contas.
Você precisa:
- debitar
100da conta A - creditar
100na conta B - registrar uma linha no histórico
Sem transação, pode acontecer algo assim:
- débito funciona
- crédito falha
- histórico nem chega a ser gravado
Resultado:
- a conta A perdeu dinheiro
- a conta B não recebeu
- o sistema ainda ficou sem trilha clara do que aconteceu
Com transação, a ideia é:
begin;
update accounts
set balance = balance - 100
where id = 'A';
update accounts
set balance = balance + 100
where id = 'B';
insert into transfers (from_account, to_account, amount)
values ('A', 'B', 100);
commit;
Se alguma etapa crítica falhar antes do commit, você faz rollback e volta ao estado anterior.
Agora imagine que existe uma etapa opcional, como tentar gravar uma observação complementar ou um vínculo secundário que não deve derrubar a transferência inteira.
Você pode usar algo como:
begin;
-- núcleo da operação
update accounts ...
update accounts ...
savepoint optional_step;
-- passo opcional
insert into transfer_notes (...)
-- se esse trecho falhar:
rollback to savepoint optional_step;
commit;
O núcleo continua.
Só o trecho opcional volta.
Erros comuns
- Usar transação em volta de qualquer fluxo sem primeiro nomear a regra que precisa ficar inteira.
- Colocar chamada HTTP, fila ou processamento demorado dentro da transação.
- Achar que
commitno final resolve desenho ruim de concorrência. - Ignorar que transação longa aumenta lock e contenção.
- Tratar savepoint como solução elegante para fluxo confuso demais.
- Esquecer que, fora do banco local, talvez você precise de outbox, compensação ou outro arranjo.
Como um senior pensa
Um senior normalmente começa pela invariável.
Ele pergunta:
- o que não pode ficar quebrado se essa operação falhar no meio?
Depois disso, ele decide o tamanho mínimo da transação.
Ou seja:
- o que precisa entrar
- o que precisa ficar fora
- o que pode virar efeito posterior
Ele também evita linguagem mágica.
Em vez de falar “usa transação e pronto”, costuma falar algo como:
- “o débito e o crédito precisam fechar juntos”
- “o banco não pode observar esse estado parcial”
- “a parte externa eu desacoplo para não segurar lock à toa”
E mais importante:
ele sabe que transação protege corretude local, mas não absolve desenho ruim.
O que o entrevistador quer ver
Em entrevista, ninguém está medindo se você decorou sintaxe de BEGIN e COMMIT.
O que costuma estar sendo avaliado é se você:
- entende quando o problema é estado parcial
- sabe explicar commit, rollback e savepoint em linguagem simples
- reconhece custo de lock e contenção
- evita colocar rede dentro da transação
- não vende transação como solução universal para sistema distribuído
Uma resposta boa soa mais ou menos assim:
“Eu uso transação quando a regra depende de mais de uma escrita que precisa fechar junta. Commit confirma o pacote, rollback descarta se a operação deixou de ser válida e savepoint serve para voltar um trecho intermediário sem perder tudo. E eu tento manter a transação curta, sem chamada externa dentro dela, porque corretude sem contenção também importa.”
Isso mostra entendimento técnico e julgamento.
Transação não existe para juntar queries. Existe para impedir verdade quebrada no banco.
Commit fecha o pacote. Rollback apaga o pacote. Savepoint volta um pedaço sem fingir que tudo é igual.
Resumo rápido
O que vale manter na cabeça
- Transação existe para fechar um conjunto de mudanças como uma unidade coerente, não para enfeitar qualquer bloco de código.
- Commit confirma o pacote inteiro; rollback descarta o que aconteceu dentro da transação; savepoint cria um ponto intermediário de retorno.
- Transação longa demais aumenta lock, contenção e tempo de banco ocupado, então corretude e custo precisam andar juntos.
- Em entrevista, resposta forte mostra qual regra de negócio precisa ficar inteira, quando transação basta e quando o problema exige outro desenho.
Checklist de pratica
Use isto ao responder
- Consigo explicar o problema que a transação resolve sem cair em definição de manual?
- Sei dizer a diferença entre commit, rollback e savepoint em linguagem simples?
- Consigo nomear um caso em que eu NÃO colocaria chamada externa dentro da transação?
- Sei explicar por que transação longa demais também pode virar problema?
Você concluiu este artigo
Próximo passo
Como modelar entidades Próximo passo →Compartilhar esta página
Copie o link manualmente no campo abaixo.