Как исключить модель из транзакции в ActiveRecord? - PullRequest
1 голос
/ 10 сентября 2010

У меня есть модель особого случая, которая не должна становиться частью внешней транзакции:

Outer.Transaction do
  ...

  Inner.create(:blah)

  ...
end

Как мне не дать Inner стать частью транзакции, если предположить, что Inner ничего не знает о конкретной транзакции?например, создание внутренней транзакции - это не пойдет, потому что это тоже станет частью внешней транзакции.

Я хочу сделать это, потому что внутренняя модель должнапишите немедленно и не ожидайте внешней транзакции для фиксации.

Ответы [ 2 ]

1 голос
/ 10 сентября 2010

Мне любопытно, что потребует такой конструкции!

Я думаю, что вы будете изо всех сил пытаться сделать это без небольшого количества хакерских атак, как вы описали. Например, вы можете в mysql установить тип хранения таблицы для Inner на тип, который не поддерживает транзакции (например, MyIsam), в то же время сохраняя хранение таблиц других классов с чем-то, что поддерживает транзакции (YUK!).

Если вы можете, вам почти наверняка будет лучше отложить Inner.create до завершения транзакции. Вы можете использовать начало, чтобы убедиться, что создание всегда происходит. Что-то вроде:

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 не будет создан. Это не будет работать должным образом, когда два или более вызовов вложены. И наконец, я не проверил это полностью - используйте с осторожностью!

0 голосов
/ 11 сентября 2010

Вы можете определить отдельное соединение с базой данных для Inner, тогда транзакция будет применяться только для соединения с Outer.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...