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 сделали эту проблему почти вымершей, но она все еще случается время от времени.

Ответ 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

Что это. Надеюсь, это поможет.