如何从ActiveRecord中的事务中排除模型?

我有一个特殊情况模型,它不能成为外部事务的一部分:

Outer.Transaction do ... Inner.create(:blah) ... end 

我如何阻止Inner成为交易的一部分,假设Inner不知道它将被拉入哪个特定交易?

例如,创建内部事务是不行的,因为它也将成为外部事务的一部分。

我想这样做是因为内部模型需要立即写入而不是等待外部事务提交。

我很好奇什么需要这样的结构!

我认为,如你所描述的那样,如果没有一点ha,就会很难做到这一点。 例如,您可以在mysql中将Inner的表存储类型设置为不支持事务的表(MyIsam eg),同时保持其他类的表存储具有支持事务的内容(YUK!)。

如果可以的话,在交易Inner.create之前,你几乎肯定会更好地推迟Inner.create 。 您可以使用begin with ensure确保始终发生创建。 就像是:

 create_inner = false begin Outer.transaction.do ... create_inner = true # instead of Inner.create(:blah) ... end ensure if create_inner Inner.create(:blah) end end 

如果块的其余部分依赖于创建的Inner实例,这将变得更加复杂。 您可以在块中创建实例,并在块的末尾将created_inner设置为false,这样,如果代码运行无例外,它将在事务中创建,您将不会再次在ensure中创建。

如果你想在一般情况下这样做,你可以在Inner上定义一个类方法来执行一个块但总是创建一个Inner对象。 您还需要将一个after_create添加到Inner 。 您可以依赖块中的Inner.create调用在事务成功时创建它,但如果它被回滚,那么您需要在之后创建它。 例如:

 class Inner < ActiveRecord::Base def self.ensure_created(&block) Thread.current[:created_inner] = false begin block.call rescue => e if Thread.current[:created_inner] Inner.create(:blah) end raise e end end def after_create # Flag that an instance has been created in this thread so # that if we rollback out of a transaction we can create again Thread.current[:created_inner] = true end 

然后你会这样称呼它:

 Inner.ensure_created do Outer.transaction do ... Inner.create(:blah) ... end end 

但是,这种方法存在很多缺点,我不确定我是否会提倡这种方法。 这很复杂。 如果引发ActiveRecord :: Rollback,它将无效,因为该exception不会从Outer.transaction但会导致不创建Inner实例。 嵌套两个或多个调用时,它将无法正常工作。 最后我还没有彻底测试过 – 请谨慎使用!

您可以为Inner定义单独的数据库连接,然后该事务将仅应用于Outer的连接。