ENADE Ciência da Computação study materials
ENADE Ciência da Computação study resources

Consistência e isolamento em transferência bancária concorrente

ENADE Ciência da Computação · Put your knowledge to work in context.

All case studies

Consistência e isolamento em transferência bancária concorrente

Um sistema bancário processa transferências entre contas. O saldo é lido, validado e depois atualizado em três passos separados no código da aplicação, cada um com sua própria transação de banco (READ COMMITTED). Em teste de carga com 200 transferências simultâneas entre as mesmas duas contas (A e B), apareceram saldos finais inconsistentes: a soma dos saldos diminuiu, e algumas contas ficaram negativas mesmo com a validação de saldo no código. O time sugere apenas aumentar o nível de isolamento globalmente para SERIALIZABLE e adicionar retentativas, mas teme queda de desempenho. Há também relatos de transferências "perdidas": o débito ocorreu e o crédito não.

1. Explique as anomalias observadas relacionando-as aos três passos separados e ao nível READ COMMITTED.

Read the worked answer

Com três transações separadas, não há atomicidade entre débito e crédito: se o processo cair entre elas, o débito persiste e o crédito não - é a transferência perdida relatada (violação de atomicidade, não de isolamento). O saldo negativo vem de read-modify-write concorrente: em READ COMMITTED, duas transações podem ler o mesmo saldo, ambas validarem que há fundos e ambas debitarem; a segunda sobrescreve a atualização da primeira (lost update). A soma dos saldos mudar indica atualizações perdidas ou escritas não atômicas. READ COMMITTED só impede leituras sujas; não impede lost update nem leitura não repetível. Ou seja, o problema é de projeto da transação (granularidade errada) combinado com nível de isolamento insuficiente para o padrão de acesso.

2. Proponha a correção mínima no código/SGBD e avalie se é necessário SERIALIZABLE global.

Read the worked answer

Correção mínima: (1) unificar os três passos em uma única transação (BEGIN … COMMIT), garantindo atomicidade débito+crédito; (2) usar UPDATE condicional - UPDATE conta SET saldo = saldo - :v WHERE id = A AND saldo >= :v - e verificar linhas afetadas; isso resolve a validação de saldo sem janela de corrida; (3) bloquear na ordem consistente (sempre debitar A e creditar B na mesma ordem, ou usar SELECT ... FOR UPDATE nas duas contas) para evitar deadlocks; (4) manter READ COMMITTED, que é suficiente quando se usa UPDATE atômico com condição. Não é necessário SERIALIZABLE global: ele resolveria, mas ao custo de abortos e retentativas em toda a aplicação. Aplicar SERIALIZABLE só onde há leitura seguida de escrita dependente, se necessário. O ganho de desempenho vem de restringir o escopo do problema.

3. Como você validaria que a correção funciona e que não há regressão de desempenho?

Read the worked answer

Correção: teste de carga concorrente com N threads transferindo entre as mesmas contas, verificando o invariante soma(A)+soma(B) constante e nenhum saldo negativo; incluir teste de falha injetada (matar o processo no meio) para confirmar que não há débito sem crédito; teste de deadlock com transferências cruzadas A→B e B→A simultâneas. Desempenho: medir latência p50/p99 e throughput antes/depois, com o mesmo perfil de carga; observar taxa de deadlocks/retentativas e tempo de bloqueio. Critério de aceite: invariantes sempre válidas sob carga e latência p99 dentro do SLA. Se a taxa de conflitos for alta, considerar particionamento de contas quentes ou filas serializadas por conta.