Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 如何在基于父属性创建子id时避免竞争条件_Ruby On Rails_Ruby - Fatal编程技术网

Ruby on rails 如何在基于父属性创建子id时避免竞争条件

Ruby on rails 如何在基于父属性创建子id时避免竞争条件,ruby-on-rails,ruby,Ruby On Rails,Ruby,想象一下,你有一个网站,它有一个“接受数字”的东西,人们接受一个数字,然后等待轮到他们。假设每个数字都是一个顺序。这种关系是一个站点有许多订单 在一个给定的重置点,站点的管理者将使用新的数字卷替换“获取数字”内容,也就是说,你可以想象这种情况每天都在发生。然而,“拿一个数字”的东西是用有限的数字制造的,所以每卷由,比如说1-100组成,然后下一卷又从100-999开始 我试图对上述行为进行建模,以及我是如何看待它的: 在站点父站点上,有一个开始编号属性。点击重置/交换卷,将开始编号重置为100,

想象一下,你有一个
网站
,它有一个“接受数字”的东西,人们接受一个数字,然后等待轮到他们。假设每个数字都是一个
顺序
。这种关系是一个
站点
有许多
订单

在一个给定的重置点,站点的管理者将使用新的数字卷替换“获取数字”内容,也就是说,你可以想象这种情况每天都在发生。然而,“拿一个数字”的东西是用有限的数字制造的,所以每卷由,比如说1-100组成,然后下一卷又从100-999开始

我试图对上述行为进行建模,以及我是如何看待它的:

  • 站点
    父站点上,有一个
    开始编号
    属性。点击重置/交换卷,将
    开始编号
    重置为100,即卷的第一个编号
  • Order
    子级上,有一个分配数字的回调。如果父
    站点
    编号为100,则这意味着这是自第1步中所述重置以来的第一个
    订单
    ,因此该编号也为100。现在,父
    站点
    将自动更新,使其不再处于重置状态(例如,
    开始编号
    不再为100)。对于未来的
    订单
    ,分配的编号仅为上一订单后的下一个编号
  • 代码如下:

    class Site
      has_many :orders
    end
    
    class Order
      belongs_to :site
    
      before_save :assign_number
    
      def assign_number
        if site.start_number == 100
          self.number = 100
          self.site.update_column(:start_number, nil)
        else
          self.number = self.site.orders.where.not(number:nil).last.number + 1
        end
      end
    end
    
    但这是糟糕的,因为与现实世界中的“拿一个数字”不同,2个订单可以同时处理,对
    Order.number
    没有
    unique
    约束,因为数字可以重复使用(滚动会被重置)。但是,如果在重置时,两个放在一起的订单都是
    100
    ,则显然没有帮助。如果确实发生了重置事件,而不仅仅是巧合的时间,您只希望多个订单共享一个编号

    这种方法的另一个问题是,一个订单后面紧跟着第二个订单。例如,假设最后分配的编号是
    415
    ,两个订单快速连续。第一个被分配到
    416
    ,第二个如此接近,以至于
    self.site.orders.where.not(number:nil)。最后一个.number
    仍然返回
    415
    (即,
    416
    尚未保存),因此第二个订单现在也被分配到
    416

    如果能获得关于如何更好地模拟所需行为的想法,那就太好了。谢谢

    更新根据@Fernando的评论,我将使用一个悲观锁,我将按照notes实现它。所以现在代码看起来像:

      def assign_number
        site = self.site.lock!
        if site.start_number == 100
          self.number = 100
          site.start_number = nil
        else
          last_order = self.site.orders.where.not(number:nil).last.lock!
          self.number = last_order.number + 1
          last_order.save! # releases lock
        end
        site.save! #releases lock, whether or not call number was updated to nil
      end
    

    我不完全确定如何规范这虽然。。。由于编写规范是按定义排序的。。。如何强制两个订单紧密保存以模拟此行为?

    一种方法是创建另一个表来存储最新的编号

    #code   :string   not null
    #number :bigint   default(0), not null
    
    class Serial < ApplicationRecord
      def self.get_latest_number
        Serial.transaction do
          self.lock.find_or_create_by!(code: 'just_any_identification').increment!(:number).count
        end
      end
    end
    

    请注意。

    一种方法是创建另一个存储最新编号的表

    #code   :string   not null
    #number :bigint   default(0), not null
    
    class Serial < ApplicationRecord
      def self.get_latest_number
        Serial.transaction do
          self.lock.find_or_create_by!(code: 'just_any_identification').increment!(:number).count
        end
      end
    end
    

    请注意。

    谢谢!更新了我的问题,我现在正在使用锁,更喜欢那个评级器而不是一个新表。关于如何测试这个问题有什么想法吗?我不知道如何测试这种情况。可能接受\u嵌套的\u属性\u?示例:Site.new({:order\u attributes=>{…})。您可能应该发布一个关于测试的新问题,以吸引其他观众。谢谢!更新了我的问题,我现在正在使用锁,更喜欢那个评级器而不是一个新表。关于如何测试这个问题有什么想法吗?我不知道如何测试这种情况。可能接受\u嵌套的\u属性\u?示例:Site.new({:order\u attributes=>{…})。您可能应该发布一个关于测试的新问题,以吸引其他观众。