Rails postgresql как установить уровень изоляции транзакций на сериализуемый

#ruby-on-rails #postgresql #transactions #callback

#ruby-on-rails #postgresql #транзакции #обратный вызов

Вопрос:

У меня есть модель комментариев, которая принадлежит к модели темы. В модели комментариев у меня есть обратный вызов before_create

 def on_create
  Topic.transaction(:require_new => true) do
    Topic.connection.execute('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE')
    self.topic.increment!(:comment_counter) if conditions
  end
end
 

Проблема в том, что я получаю ActiveRecord::StatementInvalid: PGError: ERROR: SET TRANSACTION ISOLATION LEVEL must be called before any query .

Есть ли другой способ установить уровень изоляции транзакций?

Комментарии:

1. Можете ли вы установить его перед началом транзакции?

2. На самом деле это был мой вопрос — как установить уровень изоляции до начала всей транзакции сохранения модели.

Ответ №1:

Начиная с Rails 4, #transaction предоставляется :isolation опция:

Если ваша база данных поддерживает настройку уровня изоляции для транзакции, вы можете установить его следующим образом:

 Post.transaction(isolation: :serializable) do
  # ...
end
 

Ответ №2:

PostgreSQL требует SET TRANSACTION , чтобы инструкции выполнялись после запуска транзакции и перед любым оператором DML (SELECT, INSERT, DELETE и т. Д.). Из документации похоже, что все это нужно будет делать через объект connection, а не объект transaction. Что-то вроде (непроверенный)

 Topic.connection.begin_db_transaction
  Topic.connection.execute('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE')
  # Other things go here. I'd test with another literal SQL statement to make
  # sure it works like I'd hope it does. Then possibly try rewriting in Rails.
Topic.connection.commit_db_transaction
 

Я действительно надеюсь, что я ошибаюсь в этом.

Одной из неприятных альтернатив является изменение уровня изоляции по умолчанию для всех транзакций на сервере PostgreSQL. (Поиск http://www.postgresql.org/docs/current/static/runtime-config-client.html для «default_transaction_isolation».) Но это похоже на использование пушки, чтобы убить муху.

Комментарии:

1. Код будет работать изолированно, но основная проблема заключается в том, что транзакция этой темы заключена в другую транзакцию. По-видимому, невозможно использовать вложенные транзакции с разными уровнями изоляции, поэтому мне понадобится способ сделать первую транзакцию сериализуемой — и я бы не хотел, чтобы какие-либо транзакции в модели комментариев были сериализуемыми.

2. @eugen: Вложить транзакцию с другим уровнем изоляции — логический парадокс. Это невозможно сделать.

3. @Catcall, спасибо — ваши комментарии на самом деле указали мне на другой способ решения проблемы, я собираюсь принять ответ как есть.

4. @Catcall: с помощью обратного вызова after_commit — этот выполняется после фиксации исходной транзакции, поэтому он может выполняться в своей собственной транзакции.

5. @Catcall: это одно из решений. Я не уверен, что это лучший вариант, но пока он работает.

Ответ №3:

Вы можете найти transaction_isolation gem полезным: https://github.com/qertoip/transaction_isolation