PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Subseleção PostgreSQL e ActiveRecord para condição de corrida


Suas opções são:

  • Executar em SERIALIZABLE isolamento. As transações interdependentes serão abortadas na confirmação como tendo uma falha de serialização. Você receberá muitos spams de log de erros e fará muitas tentativas, mas funcionará de maneira confiável.

  • Defina um UNIQUE restrição e tente novamente em caso de falha, como você observou. Mesmos problemas acima.

  • Se houver um objeto pai, você pode SELECT ... FOR UPDATE o objeto pai antes de fazer seu max consulta. Nesse caso, você SELECT 1 FROM bar WHERE bar_id = $1 FOR UPDATE . Você está usando bar como um bloqueio para todos os foo s com esse bar_id . Você pode então saber que é seguro continuar, desde que cada consulta que está fazendo seu incremento de contador faça isso de forma confiável. Isso pode funcionar muito bem.

    Isso ainda faz uma consulta agregada para cada chamada, o que (por próxima opção) é desnecessário, mas pelo menos não envia spam ao log de erros como as opções acima.

  • Use uma mesa de balcão. Isto é o que eu faria. Ou em bar , ou em uma tabela lateral como bar_foo_counter , adquira um ID de linha usando
    UPDATE bar_foo_counter SET counter = counter + 1
    WHERE bar_id = $1 RETURNING counter
    

    ou a opção menos eficiente se sua estrutura não puder lidar com RETURNING :
    SELECT counter FROM bar_foo_counter
    WHERE bar_id = $1 FOR UPDATE;
    
    UPDATE bar_foo_counter SET counter = $1;
    

    Então, na mesma transação , use a linha do contador gerada para o number . Quando você confirma, a linha da tabela de contador para esse bar_id é desbloqueado para a próxima consulta a ser usada. Se você reverter, a alteração será descartada.

Eu recomendo a abordagem do contador, usando uma mesa lateral dedicada para o contador em vez de adicionar uma coluna à bar . Isso é mais fácil de modelar e significa que você cria menos atualizações na bar , o que pode diminuir a velocidade das consultas para bar .