Eu tenho alguma lógica em meu aplicativo que acho que resulta nas seguintes chamadas do MySQL; no entanto, quando duas delas são executadas em alguns milissegundos, recebo duas linhas filhas incompatíveis.
- Inicie a transação com isolamento de leitura repetível.
- Buscar e bloquear o objeto Conta
SELECT ... FROM Account WHERE id = ? FOR UPDATE
- Leia os destinatários existentes vinculados à conta
SELECT ... FROM Address WHERE account_id = ?
- Se o endereço existente estiver marcado como principal, atualize-o.
UPDATE Addresses SET primary = false WHERE id = ?
- Insira o novo endereço com primário = verdadeiro.
- Transação completa.
Se esse processo for executado duas vezes rapidamente, recebo duas linhas de endereço com primário = true e account_id definido para a conta que me surpreendeu porque pensei que bloquear a conta na etapa 2 impediria que várias transações fossem executadas simultaneamente. Não quero mudar o comportamento de uma forma que limite muito meu rendimento.
Eu me perguntei se preciso mudar o nível de isolamento para "Serializable" [sic], mas não tenho certeza se uma leitura "FOR SHARE" na etapa 3 realmente resolveria o problema.
Minha estrutura de dados é provavelmente óbvia pelo que foi dito acima, mas se parece com isto:
Account
:
id
PC
outros dados de conta irrelevantes
Address
:
id
PC,
account_id
- não é uma chave estrangeira real, apenas um ID que corresponde ao PK da conta.
primary
- booleano, deve ter no máximo um endereço primário por conta, mas isso não é aplicado no MySQL porque complicaria um pouco o banco de dados ter uma coluna gerada para permitir isso, já que o MySQL não suporta índices parciais.
outros dados de endereço irrelevantes