ruby-on-rails – 多线程并发Capybara请求?

前端之家收集整理的这篇文章主要介绍了ruby-on-rails – 多线程并发Capybara请求?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我的API允许用户购买某些独特的项目,每个项目只能出售给一个用户.所以当多个用户尝试购买相同的项目时,一个用户应该得到响应:好的,另一个用户应该得到这个响应too_late.

现在,我的代码似乎有错误.比赛条件.如果两个用户同时尝试购买相同的项目,他们都可以得到答案.这个问题在生产中是显而易见的.现在我写了一个简单的测试,尝试通过rspec来重现它:

context "when I try to provoke a race condition" do
  # ...

  before do
    @concurrent_requests = 2.times.map do
      Thread.new do
        Thread.current[:answer] =  post "/api/v1/item/buy.json",:id => item.id
      end
    end

    @answers = @concurrent_requests.map do |th|
      th.join
      th[:answer].body
    end
  end

  it "should only sell the item to one user" do
    @answers.sort.should == ["ok","too_late"].sort
  end
end

似乎不同时执行查询.为了测试这个,我把下面的代码放到我的控制器动作中:

puts "Is it concurrent?"
sleep 0.2
puts "Oh Noez."

预期的输出将是,如果请求是并发的:

Is it concurrent?
Is it concurrent?
Oh Noez.
Oh Noez.

但是,我得到以下输出

Is it concurrent?
Oh Noez.
Is it concurrent?
Oh Noez.

这告诉我,水瓶要求不是同时运行,而是一次一个.如何使我的卡巴拉要求并发?

解决方法

多线程和capybara不起作用,因为Capabara使用单独的服务器线程来顺序处理连接.但是,如果你叉,它是有效的.

我正在使用退出代码作为进程间通信机制.如果你做更复杂的东西,你可能想使用套接字.

这是我的快速肮脏的黑客:

before do
  @concurrent_requests = 2.times.map do
    fork do
      # ActiveRecord explodes when you do not re-establish the sockets
      ActiveRecord::Base.connection.reconnect!

      answer = post "/api/v1/item/buy.json",:id => item.id

      # Calling exit! instead of exit so we do not invoke any rspec's `at_exit`
      # handlers,which cleans up,measures code coverage and make things explode.
      case JSON.parse(answer.body)["status"]
        when "accepted"
          exit! 128
        when "too_late"
          exit! 129
      end
    end
  end

  # Wait for the two requests to finish and get the exit codes.
  @exitcodes = @concurrent_requests.map do |pid|
    Process.waitpid(pid)
    $?.exitstatus
  end

  # Also reconnect in the main process,just in case things go wrong...
  ActiveRecord::Base.connection.reconnect!

  # And reload the item that has been modified by the seperate processs,# for use in later `it` blocks.
  item.reload
end

it "should only accept one of two concurrent requests" do
  @exitcodes.sort.should == [128,129]
end

我使用非常异乎寻常的退出代码,如128和129,因为如果未达到大小写块,则进程退出代码0,如果异常发生则为1.两者都不应该发生.所以通过使用更高的代码,我注意到事情出错.

猜你在找的Ruby相关文章