我有一个特殊情况模型,它不能成为外部交易的一部分:
Outer.Transaction do
...
Inner.create(:blah)
...
end
我如何阻止Inner成为交易的一部分,假设Inner不知道它将被引入哪个特定交易?
例如,创建内部事务是不行的,因为它也将成为外部事务的一部分。
我想这样做是因为内部模型需要立即写入而不是等待外部事务提交。
答案 0 :(得分:1)
我很想知道这需要什么构造!
我认为如你所描述的那样,如果没有一点ha,就会很难做到这一点。例如,您可以在mysql中将Inner
的表存储类型设置为不支持事务的表(MyIsam eg),同时保持其他类的表存储具有支持事务的内容(YUK!)。
如果可以,在交易完成之前,你几乎肯定会更好地推迟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,这样,如果代码无异常地运行,它将在事务中创建,并且您不会再在确保。
如果你想在一般情况下这样做,你可以在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,它将无效,因为该异常不会从Outer.transaction
冒出,但会导致不创建Inner
实例。当嵌套两个或多个调用时,它将无法正常工作。最后我还没有彻底测试过 - 请谨慎使用!
答案 1 :(得分:0)
您可以为Inner定义单独的数据库连接,然后该事务将仅应用于Outers连接。