web-dev-qa-db-ja.com

ActiveRecord :: ConnectionTimeoutError:5.000秒以内にデータベース接続を取得できませんでした(5.000秒待機)

Rails先日いくつかの変更を展開したアプリがあります。突然1日複数回エラーActiveRecord::ConnectionTimeoutError: could not obtain a database connection within 5.000 seconds (waited 5.000 seconds)を受け取り、プーマを再起動する必要があります。問題を修正します。

私はこれを引き起こしているものに関して完全に困惑しています。サーバーで何も変更せず、変更は非常に簡単でした(ビューに追加し、コントローラーメソッドに追加します)。

ログファイルにはほとんど何も表示されません。

私はRails 4.1.4およびRuby 2.0.0p481を使用しています

私のつながりがいっぱいになる理由についてのアイデアはありますか?接続プールは10に設定されており、デフォルトのプーマ構成を使用しています。

スタックトレースは次のとおりです。

ActiveRecord::ConnectionTimeoutError (could not obtain a database connection within 5.000 seconds (waited 5.000 seconds)):
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:190:in `block in wait_poll'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:181:in `loop'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:181:in `wait_poll'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:136:in `block in poll'
  /usr/local/rvm/rubies/Ruby-2.0.0-p481/lib/Ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:146:in `synchronize'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:134:in `poll'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:418:in `acquire_connection'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:351:in `block in checkout'
  /usr/local/rvm/rubies/Ruby-2.0.0-p481/lib/Ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:350:in `checkout'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:265:in `block in connection'
  /usr/local/rvm/rubies/Ruby-2.0.0-p481/lib/Ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:264:in `connection'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:541:in `retrieve_connection'
  activerecord (4.1.4) lib/active_record/connection_handling.rb:113:in `retrieve_connection'
  activerecord (4.1.4) lib/active_record/connection_handling.rb:87:in `connection'
  activerecord (4.1.4) lib/active_record/query_cache.rb:51:in `restore_query_cache_settings'
  activerecord (4.1.4) lib/active_record/query_cache.rb:43:in `rescue in call'
  activerecord (4.1.4) lib/active_record/query_cache.rb:32:in `call'
  activerecord (4.1.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:621:in `call'
  actionpack (4.1.4) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
  activesupport (4.1.4) lib/active_support/callbacks.rb:82:in `run_callbacks'
  actionpack (4.1.4) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  actionpack (4.1.4) lib/action_dispatch/middleware/remote_ip.rb:76:in `call'
  airbrake (4.1.0) lib/airbrake/Rails/middleware.rb:13:in `call'
  actionpack (4.1.4) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
  actionpack (4.1.4) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  railties (4.1.4) lib/Rails/rack/logger.rb:38:in `call_app'
  railties (4.1.4) lib/Rails/rack/logger.rb:20:in `block in call'
  activesupport (4.1.4) lib/active_support/tagged_logging.rb:68:in `block in tagged'
  activesupport (4.1.4) lib/active_support/tagged_logging.rb:26:in `tagged'
  activesupport (4.1.4) lib/active_support/tagged_logging.rb:68:in `tagged'
  railties (4.1.4) lib/Rails/rack/logger.rb:20:in `call'
  actionpack (4.1.4) lib/action_dispatch/middleware/request_id.rb:21:in `call'
  rack (1.5.2) lib/rack/methodoverride.rb:21:in `call'
  dragonfly (1.0.5) lib/dragonfly/cookie_monster.rb:9:in `call'
  rack (1.5.2) lib/rack/runtime.rb:17:in `call'
  activesupport (4.1.4) lib/active_support/cache/strategy/local_cache_middleware.rb:26:in `call'
  rack (1.5.2) lib/rack/sendfile.rb:112:in `call'
  airbrake (4.1.0) lib/airbrake/user_informer.rb:16:in `_call'
  airbrake (4.1.0) lib/airbrake/user_informer.rb:12:in `call'
  railties (4.1.4) lib/Rails/engine.rb:514:in `call'
  railties (4.1.4) lib/Rails/application.rb:144:in `call'
  railties (4.1.4) lib/Rails/railtie.rb:194:in `public_send'
  railties (4.1.4) lib/Rails/railtie.rb:194:in `method_missing'
  puma (2.9.0) lib/puma/configuration.rb:71:in `call'
  puma (2.9.0) lib/puma/server.rb:490:in `handle_request'
  puma (2.9.0) lib/puma/server.rb:361:in `process_client'
  puma (2.9.0) lib/puma/server.rb:254:in `block in run'
  puma (2.9.0) lib/puma/thread_pool.rb:92:in `call'
  puma (2.9.0) lib/puma/thread_pool.rb:92:in `block in spawn_thread'

Puma init.dスクリプト

#!/bin/sh
# Starts and stops puma
#


case "$1" in
        start)
                su myuser -c  "source /etc/profile && cd /var/www/myapp/current && rvm gemset use myapp && puma -d -e production -b unix:///var/www/myapp/myapp_app.sock -S /var/www/myapp/myapp_app.state"
        ;;

        stop)
                su myuser -c "source /etc/profile && cd /var/www/myapp/current &&  rvm gemset use myapp && Rails_ENV=production bundle exec pumactl -S /var/www/myapp/myapp_app.state stop"
        ;;

        restart)
                $0 stop
                $0 start
        ;;

        *)
                echo "Usage: $0 {start|stop|restart}"
                exit 1
esac

[〜#〜] edit [〜#〜]

ようやく問題を絞り込んだと思う airbrake gemおよび devise method current_userまたはuser_signed_in? in application_controller.rb in before_actionを使用します。

これが私のアプリケーションコントローラーです。

class ApplicationController < ActionController::Base
  protect_from_forgery
  before_filter :authenticate_user!, :get_new_messages 

  # Gets the unread messages for the logged in user
  def get_new_messages
    @num_new_messages = 0 # Initially set to 0 so login page, etc works
    # If the user is signed in, fetch the new messages
    if user_signed_in? # I also tried !current_user.nil?
      @num_new_messages = Message.where(:created_for => current_user.id).where(:viewed => false).count
    end
  end

...
end

ifブロックを削除しても問題ありません。私はそのコードを導入して以来、私のアプリは接続が切れているようです。 ifブロックをそのままにしてairbrake gemを削除すると、database.ymlファイルのプールにデフォルトの5接続のみが設定された状態でアプリが正常に実行されるようです。

[〜#〜] edit [〜#〜]

最後に、config/environments/production.rbファイルconfig.exceptions_app = self.routesでこの行をコメントアウトしても、エラーが発生しないことがわかりました。アプリコントローラーbefore_actionのカスタムルート+工夫が原因のようです。 githubで問題と再現可能なプロジェクトを作成しました。

https://github.com/plataformatec/devise/issues/3422https://github.com/toymachiner62/devise-connection-failure/blob/master/config/environments/ production.rb#L84

25
Catfish

考案者の助けを借りて、私はついにこの問題を理解したと思います。独自のコントローラーでカスタムエラーページを使用することで、before_action get_new_messagesをスキップしていなかったようです。そのため、非常に簡単な修正は以下を追加することでした:

skip_before_filter :get_new_messages

カスタムエラーコントローラーに。

この問題は、この背後にある理由を詳しく説明しています: https://github.com/plataformatec/devise/issues/3422

2
Catfish

データベースへのオープン接続が多すぎるために引き起こされる同じ問題がありました。これは、コントローラーの外部(モデル、メーラー、pdfジェネレーターなど)でデータベースクエリがある場合に発生する可能性があります。

接続を自動的に閉じるこのブロックでこれらのクエリをラップすることで修正できました。

ActiveRecord::Base.connection_pool.with_connection do
  # your code
end

Pumaはマルチスレッドで動作するため、プールのサイズ(eabrahamが述べたように)も制限になる可能性があります。 (少し)増やしてみてください...

これがお役に立てば幸いです!

10
Railsana

最終的に、この問題は私をもう1年ほど悩ませました。私はついにプーマ人と仕事をすることから良い解決策を得ました。

Pumaを少なくとも2.15.xにアップグレードします。

0
Catfish