如何预防可能的比赛状况

时间:2019-05-01 12:36:50

标签: ruby-on-rails race-condition

“我的出勤”模型是“用户”与“事件”之间的关联,用于记录用户对给定事件的出勤。任何事件的可用座位数都会随着用户每次为该事件注册而减少。以下是“出勤”控制器的内容:

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

previous post中,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

这是我第一次遇到此问题,因此我没有悲观锁定的经验,我将不胜感激。

0 个答案:

没有答案