与ActiveRecord分布式事务边界

时间:2014-11-04 09:11:25

标签: mysql ruby-on-rails activerecord transactions

假设我有连接到另一个数据库的模型:

class Model1 < ActiveRecord::Base
  establish_connection {config_to_connection_to_database2}
end


class Model2 < ActiveRecord::Base
  establish_connection {config_to_connection_to_database2}
end

当我开始交易时,请说

Model1.transaction do
  Model1.create! something
  Model2.create! something
  #some other thing
end

生成的SQL是:

BEGIN
  INSERT INTO model1 ...
  BEGIN
    INSERT INTO model2 ...
  COMMIT
#some other thing might happen here
COMMIT

#some other thing中发生错误时,INSERT INTO model2将不会回滚,因为它在嵌套事务中并且已经提交,而INSERT INTO model1将被回滚。

我找到了一个丑陋的解决方法:

Model1.transaction do
  Model2.transaction do
    Model1.create! something
    Model2.create! something
    #some other thing
  end
end

SQL成为:

BEGIN
  BEGIN
    INSERT INTO model1 ...
    INSERT INTO model2 ...
    #some other thing might happen here
  COMMIT
COMMIT

哪个有效,但有点烦我。

所以我的问题是:activerecord如何决定是否应该使用BEGINCOMMIT包装一个SQL语句(看起来像activerecord将不会检查Model1和Model2是否连接到同一个数据库),这里有更好的解决方法吗?

1 个答案:

答案 0 :(得分:0)

事实证明解决方案非常简单(在ActiveRecord :: Base文档中答案是正确的,我完全失明了)

设置课程

class Database2 < ActiveRecord::Base
  self.abstract_class = true #this is important, otherwise the inherited class's table name would be 'database2'
  establish_connection your_connection_config
end

然后Model1Model2只需要继承Database2,一切正常。

如果有人想知道,Model1Model2以这种方式共享同一个连接池。如果你在每个类中调用establish_connection,那么每个类都有自己的连接池,这会浪费资源,并且会在我的问题中提到事务问题。