Как работать с конкурирующими запросами в rails?

Пользователь

от dayna , в категории: Ruby , год назад

Как работать с конкурирующими запросами в rails?

Facebook Vk Ok Twitter LinkedIn Telegram Whatsapp

1 ответ

Пользователь

от brenna , год назад

@dayna 

В Rails существует несколько подходов к работе с конкурирующими запросами. Один из способов - использование транзакций.


Транзакции позволяют группировать несколько запросов в одну операцию базы данных, которая либо будет выполнена полностью, либо не выполнится вообще. Это предотвращает изменение данных в одном запросе, если другой запрос попытается изменить те же данные в то же время. Вместо этого второй запрос будет ожидать завершения первого запроса, а затем выполнит свои операции.


В Rails вы можете использовать транзакции в контроллерах или моделях. Например, в контроллере можно создать транзакцию следующим образом:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def create
  ActiveRecord::Base.transaction do
    @post = Post.new(post_params)
    @post.save!

    @comment = Comment.new(comment_params)
    @comment.save!
  end

  redirect_to @post
end


Здесь мы группируем создание новой записи Post и Comment в одну транзакцию. Если какой-либо из запросов не выполнится успешно (например, из-за нарушения ограничений уникальности), транзакция откатит все изменения, и данные останутся в неизменном состоянии.


Вы также можете использовать блок with_lock в модели, чтобы заблокировать запись в базе данных во время выполнения операции. Например:

1
2
3
4
5
6
def withdraw(amount)
  with_lock do
    self.balance -= amount
    save!
  end
end


Здесь мы блокируем запись перед изменением баланса и сохранением записи. Это гарантирует, что никакой другой запрос не сможет изменить баланс в то же время.


Наконец, вы можете использовать оптимистическую блокировку, чтобы избежать изменений конкурирующих запросов. Оптимистическая блокировка предполагает, что другой запрос не изменит запись, пока текущий запрос выполняется. Если другой запрос изменяет запись, оптимистическая блокировка генерирует ошибку. Например:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def withdraw(amount)
  self.class.transaction do
    reload
    if balance >= amount
      update!(balance: balance - amount)
    else
      raise "Insufficient funds"
    end
  end
end


Здесь мы обновляем запись только в том случае, если текущий баланс больше или равен запрошенному снятию. Если другой запрос изменит баланс в промежутке между reload и update!, оптимистическая блокировка генерирует ошибку.


В зависимости от вашего прилож