首页 > 解决方案 > 多个请求同时出现重复输入错误

问题描述

我正在对我的代码进行测试,它可以毫无问题地处理单个请求,但是当我尝试同时针对多个请求发送它时,我遇到了重复错误。我使用 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`

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

解决方案


问题在这里:

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

基本上,检查通过,然后create 进入数据库之前,处理同时请求的其他进程已经在数据库中创建了记录

最常见的方法是使用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在“无效”响应中提供服务。


推荐阅读