使用实例变量来存储ActiveRecord对象的瞬态属性是否安全?

时间:2012-01-22 15:15:03

标签: ruby-on-rails-3 activerecord

我需要在视频编码时将编码状态值存储

我有一个视频对象。在对视频进行编码时,需要锁定其评论的编辑内容。

因此,视频需要存储其当前的编码状态(是/否发生?)并允许子注释查询该属性。

请注意

我知道有更好的方法来解决这个特殊问题。我实际上需要解决一个稍微不同的问题,但我觉得它的细微差别会使问题混淆,所以我选择了这个问题。我的问题是关于isntance变量的细微差别而不是如何更好地解决这个编码问题(显然需要一个队列)。

class Video

  has_many :comments

  after_initialize do
    @encoding_in_process = false
  end

  def encode
    @encoding_in_process = true
    ...
    @encoding_in_process = false
  end

  def encoding_in_process? 
    @encoding_in_process
  end
end

class Comment
  belongs_to :video

  before_update
    raise "locked" if video.encoding_in_process?
  end

  ...
end

如您所见,每个视频实例都存储一个实例变量@encoding_in_process,用于确定是否可以更新注释。

问题

同一个视频的多个内存中实例存在危险,每个实例都有@encoding_in_process的不同值。

e.g。

bieber_video = Video.find_all_by_artist('Bieber').last
bieber_video.encode 
# assume this takes a while...
bieber_video.encoding_in_process?
# => true

bieber_copy = Video.find_by_id bieber_video.id
bieber_copy.encoding_in_process?
# => false


# Each ActiveRecord objects refer to the same Bieber video
bieber_copy.id == bieber_video.id
# => true

# ...however they refer to different objects in memory:
puts bieber_video
#<Video:0x00000105a9e948>
puts bieber_copy
#<Video:0x00000105a11111>

# and hence each instance has a different version of commenting_locked?
# bieber_video.encoding_in_process? != bieber_copy.encoding_in_process?

问题

鉴于相同的数据库行可能会生成两个不同的内存中实例,那么存储有关这些实例的瞬时非数据库支持信息的安全方法是什么?

修改

我试图解决的实际问题是在启动destroy时在对象上设置一个标志,以便其子对象可以确定它们是否有资格成为毁了自己。

因此,它是一个非常即时的问题,不适合备份到数据库中。我使用了这个视频的例子,因为我觉得它有点清晰但是我可能只是把水弄糊涂了。

解决方案(由以下答案之一提供

@Alex D的建议确实解决了这个问题,但是对于想要重复的人来说,为了进一步明确这个问题,实际的代码是这样的:

class Video
  # set a class variable containing an array of all videos
  # which are currently being encoded
  @@ids_of_videos_being_encoded = []
  ...

  def encode
    store_encoding_state true
    begin
      encode()
    ensure
      # make sure we switch this off after 
      # encoding finishes or fails
      store_encoding_state false
    end
  end

  private  
    def store_encoding_state encoding_in_progress
      if encoding_in_progress
        @@ids_of_videos_being_encoded.push(id)
      else
        @@ids_of_videos_being_encoded.delete(id)
      end
    end

    def encoding_initiated?
      @@ids_of_videos_being_encoded.include? id
    end
end

2 个答案:

答案 0 :(得分:1)

  

因此,视频需要存储其当前的编码状态(是/否发生?)并允许子注释查询该属性。

IMO,这不是一个很好的方法,因为会出现所有同步问题。

更好的策略是启动unencoded状态下的所有视频,并使用视频记录的元数据存储这些视频。创建视频数据流时,将某个工作人员的编码任务排入队列。工作线程将对视频进行编码,一旦完成,它应该将视频的状态更新为encoded

现在没有暂时的国家问题;下次有人在编码完成时尝试发表评论时,它就会完成。

  

鉴于相同的数据库行可能会生成两个不同的内存中实例,那么存储有关这些实例的瞬时非数据库支持信息的安全方法是什么?

如果不需要同步,那么就没有问题。如果他们需要同步,则存在竞争条件的风险。您也可以调用.reload从数据库中刷新对象的状态。

如果数据需要像这样同步,那么你可能需要存储它。在视频编码示例中,您应该存储每个视频的编码/未编码状态,或者提供隐式的权威方式来了解视频是否已编码。


从原始问题更新:

  

我试图解决的实际问题是在启动销毁时在对象上设置一个标志,以便其子对象可以确定它们是否有资格自行销毁。

只需使用 after_destroy callback 在每个子对象上调用适当的方法,让他们确定是否应该销毁它们。这看起来像这样:

class Video < ActiveRecord::Base
  after_destroy :purge_pending_comments!

  def purge_pending_comments!
    comments.map &:destroy_if_pending
  end
end

答案 1 :(得分:1)

您的问题的答案取决于您是否可以使用多个服务器进程。如果您可能想要运行多个服务器进程(这是一个很好的假设),问题不仅仅是代表相同数据库行的多个内存中ActiveRecord对象,问题是不同内存空间中的多个对象

如果您有多个进程以某种方式协同处理相同的数据,那么必须将该数据保存在共享存储(即数据库)中,并且您必须将更改刷新到商店,并且根据需要刷新内存中的数据。在这种情况下,不能依赖于瞬时内存数据保持同步(因为它可能无法实现)。

如果不断地向数据库写入/读取瞬态数据听起来很昂贵,那就是因为它。通常,只要您有多个进程(在相同或不同的服务器上)协同工作,您就需要设计一些东西,以便每个进程可以抓取一大块数据并在其上工作一段时间,而无需与其他进程通信。分布式系统中的细粒度数据共享=性能不佳。

如果您确定只使用单个服务器进程,并且想要模拟在表示相同数据库行的多个ActiveRecord对象之间共享的实例变量的效果,请将数据保存在哈希中,并键入记录ID,并使用读/写哈希的getters / setter。如果你正在做很多这样的事情,你可以做一些元编程“魔术”来自动生成getter / setter(la“attr_accessor”)。如果你需要帮助编写元编程代码,请发一个问题然后我会回答它。

相关问题