完全相同的时间,多个请求重复出现输入错误

时间:2019-05-02 02:16:43

标签: ruby-on-rails ruby ruby-on-rails-5

我正在对我的代码进行测试,它可以在单个请求上正常工作,但是当我尝试同时在多个请求上发送它时,我遇到了重复错误。我正在使用MySQL作为数据库。

型号

class Playtime < ApplicationRecord
  validates :local_id, uniqueness: true, allow_blank: true
end

控制器

从此

def create
    begin
      if !Playtime.where(local_id: params[:local_id]).exists?
        @playtime = Playtime.create!(playtime_params)
        json_response(@playtime.local_id,true)
      else
        json_response(params[:local_id], true)
      end
    rescue ActiveRecord::RecordInvalid => invalid
      json_response(invalid.record.errors.full_messages.first,false)
    end
  end

对此,我认为它将得到解决。

def create
    begin
      if !Playtime.where(local_id: params[:local_id]).exists?
        @playtime = Playtime.create(playtime_params)
        if @playtime.valid?
          json_response(@playtime.local_id,true)
        else
          json_response(params[:local_id], true)
        end
      else
        json_response(params[:local_id], true)
      end
    rescue ActiveRecord::RecordInvalid => invalid
      json_response(invalid.record.errors.full_messages.first,false)
    end
  end

但同样的错误。

我的请求。

curl -X POST \
  http://localhost:3000/events/playtime \
  -H 'Content-Type: application/json' \
  -H 'Postman-Token: b4a636e9-5802-446f-9770-692895ebdbfd' \
  -H 'cache-control: no-cache' \
  -d '{
    "local_id": "664278-153"
}'&
curl -X POST \
  http://localhost:3000/events/playtime \
  -H 'Content-Type: application/json' \
  -H 'Postman-Token: b4a636e9-5802-446f-9770-692895ebdbfd' \
  -H 'cache-control: no-cache' \
  -d '{
    "local_id": "664278-153"
}'

发生错误,没有继续进行救援。

ActiveRecord::RecordNotUnique (Mysql2::Error: Duplicate entry '664278-153' for key 'index_playtimes_on_local_id': INSERT INTO `playtimes`

1 个答案:

答案 0 :(得分:3)

问题在这里:

if !Playtime.where(local_id: params[:local_id]).exists?
  # ⇒ SWITCH OF THE CONTEXT, RECORD CREATED BY ANOTHER PROCESS
  @playtime = Playtime.create!(playtime_params)

基本上,检查通过了,然后之前创建到数据库中的另一个处理并发请求的进程已在数据库中创建了记录

最常见的荒谬方法是使用Optimistic Locking并营救StaleObjectError,但是这里您已经在该领域受到了限制,因此更加容易:

def create
  # no need to begin here, you might rescue the whole function body
  @playtime = Playtime.create!(playtime_params)
  json_response(@playtime.local_id, true)
rescue ActiveRecord::RecordNotUnique
    # record is already there, ok
    json_response(params[:local_id], true)
rescue ActiveRecord::RecordInvalid => invalid
    # params are invalid, report
    json_response(invalid.record.errors.full_messages.first, false)
end

边注:我们通常提供带有错误报告的HTTP错误代码,您的代码当前可能在“无效”响应中提供200 OK