如何仅允许一个请求执行诉讼并拒绝所有其他并发请求?

发布于 2025-02-04 02:33:55 字数 1738 浏览 2 评论 0原文

当多个并发请求尝试调用控制器拒绝操作时。在竞赛条件下,它多次生成相同的注释,尽管request> requestorder被拒绝。但是在种族条件下,请求无法识别订单已经被其中一个请求之一拒绝,因为所有这些过程都在很少的时间内执行订单更改为拒绝。为了避免此竞赛条件问题,我已锁定请求订单在DB行级别。但这不起作用。这是我的示例代码:

我使用Ruby 2.5.8,Rails 5.0.7。

我的模型

  • 订单有许多请求
  • 请求有一个批准者
class Request < ApplicationRecord
  def self.reject_request(key)
    request = Request.where(:reject_key => key).first
    order = request&.order
    raise ActiveRecord::Rollback if request.blank? || order.blank?
    raise ActiveRecord::Rollback if request.rejected? || order.rejected?

    order.lock!
    request.lock!

    request.status = 'rejected'
    request.save!
    order.update(status: 'rejected')
    true
  end
end

我的控制器

class OrdersController < ApplicationController
  def reject
    @request ||= Request.find_by(reject_key: params[:reject_key])
    @order = @request&.purchase_order

    ActiveRecord::Base.transaction do
      rejected = Request.reject_request(params[:reject_key])
      @order.comments.create(comment: "Order rejected by approver: #{@request.approver.name}") if rejected
      if rejected
        redirect_to root_path, notice: 'Rejected'
      else
        redirect_to root_path, notice: 'Try again!'
      end
    end
  end
end

问题

应该只有一个正确的评论如下所示。

批准者拒绝的订单:乔(正确)

批准者拒绝的订单:杰克(不正确)

批准者拒绝的订单:乔(不正确)

批准者拒绝的订单:杰克(不正确)

当拒绝行动执行拒绝时,它在执行拒绝时工作通过应用程序链接。但是,当多个请求通过多线程流程运行时,它就无法正常工作。如何仅允许一个请求执行该动作并拒绝所有其他行动?任何反馈都将不胜感激,谢谢!

When multiple concurrent requests try to call controller reject action. In race conditions, it generates same comments multiple times, although one of the request and order is rejected. But in race conditions, request cannot identify order was already rejected by one of the request because all these processes executes in very little amount of time before order changed to reject. To avoid this race condition issue, I have locked request and order in DB row level. But it's not working. Here are my sample codes:

I am using Ruby 2.5.8, Rails 5.0.7.

My Model

  • Order has many requests
  • Request has one approvers
class Request < ApplicationRecord
  def self.reject_request(key)
    request = Request.where(:reject_key => key).first
    order = request&.order
    raise ActiveRecord::Rollback if request.blank? || order.blank?
    raise ActiveRecord::Rollback if request.rejected? || order.rejected?

    order.lock!
    request.lock!

    request.status = 'rejected'
    request.save!
    order.update(status: 'rejected')
    true
  end
end

My Controller

class OrdersController < ApplicationController
  def reject
    @request ||= Request.find_by(reject_key: params[:reject_key])
    @order = @request&.purchase_order

    ActiveRecord::Base.transaction do
      rejected = Request.reject_request(params[:reject_key])
      @order.comments.create(comment: "Order rejected by approver: #{@request.approver.name}") if rejected
      if rejected
        redirect_to root_path, notice: 'Rejected'
      else
        redirect_to root_path, notice: 'Try again!'
      end
    end
  end
end

Problem

There should be only one correct comment, but there are more incorrect comments generated as shown below.

Order rejected by approver: Joe (correct)

Order rejected by approver: Jack (not correct)

Order rejected by approver: Joe (not correct)

Order rejected by approver: Jack (not correct)

It's working when reject action is executed by clicking on reject link through application. But when multiple requests are running through multi-threaded process, then it does not work as I described above. How to allow only one request to perform the action and reject all other actions? Any feedback would be appreciated, thank you!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

短叹 2025-02-11 02:33:55

在检查状态之前,您需要锁定订单。

如果2个并发请求执行检查请求。 ||订单。拒绝?与您的解决方案同时获得false,并在锁定下进行。

更改您的模型

class Request < ApplicationRecord
  def self.reject_request(key)
    request = Request.where(:reject_key => key).first
    order = request&.order

    # make sure both request and order objects are not empty
    raise ActiveRecord::Rollback if request.blank? || order.blank?

    # lock your object
    order.lock!
    request.lock!

    # put your conditions here after the `lock`
    raise ActiveRecord::Rollback if request.rejected? || order.rejected?


    request.status = 'rejected'
    request.save!
    order.update(status: 'rejected')
    true
  end
end

You need to lock an order before checking it's status.

If 2 concurrent requests execute check for request.rejected? || order.rejected? simultaneously with your solution, both gets false and proceed consequentially under the lock.

Changes for your model

class Request < ApplicationRecord
  def self.reject_request(key)
    request = Request.where(:reject_key => key).first
    order = request&.order

    # make sure both request and order objects are not empty
    raise ActiveRecord::Rollback if request.blank? || order.blank?

    # lock your object
    order.lock!
    request.lock!

    # put your conditions here after the `lock`
    raise ActiveRecord::Rollback if request.rejected? || order.rejected?


    request.status = 'rejected'
    request.save!
    order.update(status: 'rejected')
    true
  end
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文