Essa semana estava pensando sobre OCC e PCC, que são formas de lidar com controle de concorrência em bancos de dados. OCC Optimistic Concurrency Control assume que dois acessos simultâneos tendem a acontecer raramente e PCC que acontecerão quase sempre.
É difícil saber qual abordagem usar de cara, portanto eu presumo, mas não no vácuo. Parto da realidade que observo em produção para decidir, da natureza do produto, modelo de negócio e experiência, por exemplo.
Usei OCC num sistema de respostas de questionário. Cada plano tinha um contador de respostas e uma capacidade. Duas respostas simultâneas poderiam burlar a capacidade, o que não seria o fim do mundo. As capacidades por plano eram baixas, assim como o fluxo de requisições.
Uma forma de implementar OCC é através de uma coluna de versão na tabela. Eu leio a versão atual pra usar ela na escrita. O ponto crítico aqui é a janela entre as operações. Se alguém alterou o dado entre elas, version já mudou e o banco retorna 0 affected rows, portanto conflito.
SELECT id, capacity_used, version FROM plans WHERE id = @planId;UPDATE plansSET capacity_used = capacity_used + 1, version = version + 1WHERE id = @planId AND version = @expectedVersion;Essa forma funciona bem quando a leitura e a gravação estão no mesmo lugar. Em aplicações corporativas geralmente leitura e gravação ficam em serviços diferentes. Por isso hoje uso uma alternativa, que aprendi com o Khononov, que é ler o valor atual e passar esse valor para o serviço de escrita. É parecido, mas sustenta melhor arquiteturas onde leitura e escrita estão em serviços distintos.
public class IncrementCapacity { public void execute(UUID planId, long expectedCapacityUsed) { int affected = db.execute( "UPDATE plans SET capacity_used = capacity_used + 1 " + "WHERE id = ? AND capacity_used = ?", planId, expectedCapacityUsed ); if (affected == 0) throw new ConcurrencyException(); }}Tá no Learning Domain Driven Design, pag 68 | Chapter 5: Implementing Simple Business Logic.
Eu usaria PCC num sistema de vendas de passagens. Não dá pra admitir que duas pessoas comprem o mesmo assento. Ou em pagamentos com cartão de crédito, dado o custo de estorno.
Uma forma de implementar PCC é com SELECT FOR UPDATE. É bloqueante, mas o remédio acompanha o sintoma. Por isso a ordem é considerar OCC antes de PCC, MAS sempre depende do cenário, como eu disse.
BEGIN;SELECT id, seat_number, statusFROM seatsWHERE id = @seatIdFOR UPDATE;UPDATE seats SET status = 'sold', buyer_id = @userIdWHERE id = @seatId;COMMIT;Eu já vi usarem colunas como status = 'available', mas eu acho desnecessário. A query já é sobre availability, na minha opinião. Com certeza essa não é a única forma de implementar nem OCC nem PCC.
Você já implementou OCC ou PCC alguma vez e quer compartilhar como fez?