Em um banco de dados Postgres, tenho um relacionamento da seguinte forma:
- Uma tabela Grupo tem uma coluna userEntranceLimits.
- Uma tabela UserEntrance tem uma coluna userId e uma coluna groupId.
- A contagem de usuários distintos de uma UserEntrance para um grupo específico não pode exceder userEntranceLimits do grupo.
- Dentro de uma transação (cujo nível de isolamento é REPEATABLE_READ) para criar uma nova UserEntrance, verifico se a contagem atual de UserEntrances para aquele grupo é >= o limite.
- Se não for, prossigo para criar um novo UserEntrance.
- Em algumas raras ocasiões, um grupo aparece com mais UserEntrances do que seu limite.
Entendo por que essa condição de corrida existe, mas não entendo a maneira correta de evitar que isso aconteça sem usar o nível de isolamento SERIALIZABLE. Temos muitas escritas simultâneas nesta tabela, e quando tentamos tornar a transação SERIALIZABLE antes, não deu muito certo.
Minha ideia agora é fazer um SELECT ... FOR UPDATE
e contar o número de ocorrências no lado do aplicativo, mas isso me parece uma solução ruim. Qual é a solução adequada para este tipo de problema?
SELECT ... FOR UPDATE não funcionará neste caso. Ainda requer nível de isolamento SERIALIZABLE porque o que você precisa fazer é evitar que outras sessões insiram linhas com o mesmo groupId. E para fazer isso você precisa bloquear uma chave (o que realmente o nível SERIALIZABLE faz). O que você pode tentar fazer?: