#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