Модель My Attendance - это связь между пользователем и событием для записи посещаемости пользователей в данном событии.Любое событие имеет количество доступных мест, которое уменьшается каждый раз, когда пользователь регистрируется на это событие.Ниже приведен контент контроллера посещаемости:
before_action :logged_in_user
before_action :participants_limit, only: :create
def create
@attendance = current_user.attendances.build(attendance_params)
if @attendance.save
@event.decrement!(:seats)
flash[:success] = "Congratulation! Your participation has been recorded."
redirect_back(fallback_location: root_url)
else
flash[:danger] = @attendance.errors.full_messages.join(', ')
redirect_back(fallback_location: root_url)
end
end
private
def attendance_params
params.require(:attendance).permit(:event_id)
end
def participants_limit
@event = Event.find(params[:attendance][:event_id])
if @event.seats == 0
flash[:danger] = 'Attention! Unfortunately this event is full!'
redirect_back(fallback_location: root_url)
end
end
В предыдущем посте Vasfed предупредил меня, что мой код уязвим для состояния гонки, если два участника попытаются зарегистрироваться одновременновремя.Поэтому я пытаюсь выяснить, как предотвратить состояние гонки.Вероятно, худший сценарий - когда два пользователя пытаются зарегистрироваться для получения последнего доступного места, то есть, когда @event.seats == 1
.В этом случае оба пользователя перед началом действия передадут participants_limit
и перейдут к действию создания.Интересно, будет ли использование with_lock
в этот момент работать или бесполезно:
def create
@attendance = current_user.attendances.build(attendance_params)
if @attendance.save
@event.with_lock do
@event.decrement(:seats)
flash[:success] = "Congratulation! Your participation has been recorded."
redirect_back(fallback_location: root_url)
end
else
flash[:danger] = @attendance.errors.full_messages.join(', ')
redirect_back(fallback_location: root_url)
end
end
Единственная альтернатива, о которой я могу подумать, - это избавиться от before_action и включить его код в действие create какследует:
def create
@attendance = current_user.attendances.build(attendance_params)
@event = Event.find(params[:attendance][:event_id])
@event.with_lock do
if @event.seats == 0
flash[:danger] = 'Attention! Unfortunately this event is full!'
redirect_back(fallback_location: root_url)
elsif @attendance.save
@event.decrement(:seats)
flash[:success] = "Congratulation! Your participation has been recorded."
redirect_back(fallback_location: root_url)
else
flash[:danger] = @attendance.errors.full_messages.join(', ')
redirect_back(fallback_location: root_url)
end
end
end
Я впервые сталкиваюсь с этой проблемой, поэтому у меня нет опыта с пессимистической блокировкой, и я был бы признателен за любые советы.