Rails 3.2 частые сообщения, подготовленные после публикации, уже существуют ошибки
Я копал в stackoverflow, пытаясь найти других, у которых эти подготовленные заявления уже существуют.
В большинстве случаев правильная настройка единорога с помощью вилки after/before устраняет эти проблемы.
Однако в моем случае мы все еще получаем ошибки как таковые:
ActiveRecord::StatementInvalid: PG::Error: ERROR: prepared statement "a495" already exists: INSERT INTO "user_logins" ("account_id", "created_at", "ip_address", "user_agent", "user_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id"
Эта ошибка возникает в разных областях нашего приложения, но всегда имеет один и тот же номер оператора "a495".
Мы находимся на рельсах 3.2.17, используя postgres, и мы находимся на героике.
Я действительно понятия не имею, почему это происходит, но теперь это начинает происходить чаще.
Любая помощь будет принята с благодарностью.
В трассировке стека рельсов эта ошибка возникает в вызове .prepare. Я смущен, потому что он проверяет ключ sql в коллекции операторов. Если он не существует, он готовит новый... однако, пытаясь его подготовить, он бросает ошибку.
def prepare_statement(sql)
sql_key = sql_key(sql)
unless @statements.key? sql_key
nextkey = @statements.next_key
@connection.prepare nextkey, sql
@statements[sql_key] = nextkey
end
@statements[sql_key]
end
Ответы
Ответ 1
У нас была та же проблема, и мы провели тщательное расследование. Мы пришли к выводу, что в нашем случае эта ошибка вызвана Rack::Timeout
, что очень иногда прерывает выполнение кода после того, как новый оператор уже создан, но до того, как счетчик обновлен на стороне Rails. Затем следующая подготовленная инструкция пытается использовать одно и то же имя (например, a494
), и произошло столкновение.
Я убежден, что Rails неправильно выполнил подготовленные инструкции. Вместо использования увеличивающего счетчика (a001
, a002
,...), они должны были использовать GUID. Таким образом, условие гонки, описанное выше, не будет проблемой.
Мы не нашли обходного пути. Улучшение производительности приложения и увеличение окна для Rack::Timeout
сделали эту проблему почти вымершей, но она все еще случается время от времени.
Ответ 2
Это обычно не проблема Postgres, а проблема с подключением к базе данных в чем-то вроде Unicorn:
Ответ 3
Здесь мое решение для Heroku, которое, к сожалению, немного связано. Однако с положительной стороны вам не нужно страдать от 100 уведомлений об ошибках, когда эта ошибка начинает происходить. Все, что нужно, это перезагрузка приложения /dyno.
Основной контур процедуры заключается в том, что, когда мы обнаруживаем исключение ActiveRecord::StatementInvalid
, с описанием сообщения об ошибке, которое содержит слова "подготовленный оператор", мы запускаем команду heroku restart
, используя герой Heroku platform-api
.
- Поместите камень
platform-api
в свой Gemfile и запустите bundle install
- Установите для HEROKU_API_KEY правильное значение. (Вы можете сгенерировать ключ с панели инструментов Heroku). Используйте
heroku config:set HEROKU_API_KEY=whatever-the-value-is
.
- Задайте для HEROKU_APP_NAME правильное значение. Вы можете получить эту информацию из CLI героя, но это все, что вы назвали своим приложением.
- Добавьте в свой
ApplicationController
(/app/controllers/application_controller.rb) следующее:
...
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::StatementInvalid do |exception|
# notify your error handler, or send an email, or whatever
# ...
if exception.message =~ /prepared statement/
restart_dyno
end
end
def restart_dyno
heroku = PlatformAPI.connect_oauth(ENV["HEROKU_API_KEY"])
heroku.dyno.restart(ENV["HEROKU_APP_NAME"], "web")
end
end
Что это. Надеюсь, это поможет.