2015-10-27 5 views
2

У нас есть таблица elements, которая может быть issued клиентам. Эти elements могут быть предоставлены только клиенту один раз, и у нас есть ситуации, когда многие клиенты могут одновременно вытягивать элементы. Затем нам нужно вернуть связанные с ним данные (так что есть обновление, а затем выберите).Выбор и обновление строки при работе с условиями гонки?

В настоящее время решение состоит в том, что случайный один найден/обновляется, чтобы быть issued=trueи устанавливает его id в LAST_INSERTED_ID; затем сразу же после этого выбирает вызов для поиска where('id = LAST_INSERTED_ID()'), который является уникальным для каждого соединения.

Поскольку мы обновляем, где issued=false - issued=true and [last inserted], этот вызов достаточно мал, чтобы не встречаться с проблемами состояния гонки.

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

Какое решение мы не думали?

+0

Я не имею тонну опыт здесь, и это мне интересно. Вы находите и обновляете 'issu = true' в одном запросе базы данных? Как вы это достигаете? –

+0

Можете ли вы разместить свои вопросы, пожалуйста –

ответ

0

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

Один из способов сделать это в MySQL является SELECT FOR UPDATE так:

SELECT * FROM elements WHERE issued=false LIMIT 1 FOR UPDATE

В ActiveRecord (Rails), это называется pessimistic locking, и реализация будет выглядеть следующим образом:

Element.transaction do 
    element = Element.lock(true).where(issued: false).first 
    element.issued = true 
    # ... do other stuff to assign to a given client 
    element.save! 
end 

Если это произошло одновременно, второй вызов будет заблокирован до завершения первого вызова, поэтому к моменту его выполнения первая запись уже будет обновлена ​​до выданного = true и th Второй вызов будет возвращать следующую запись вместо той же записи.

Вы можете прочитать о SELECT FOR UPDATEhere

Смежные вопросы