web-dev-qa-db-fra.com

ActiveRecord :: ConnectionTimeoutError: impossible d'obtenir une connexion à la base de données dans les 5 000 secondes (attendu 5 000 secondes)

J'ai une application Rails en production dans laquelle j'ai déployé des modifications l'autre jour. Tout à coup, je reçois le message d'erreur ActiveRecord::ConnectionTimeoutError: could not obtain a database connection within 5.000 seconds (waited 5.000 seconds) plusieurs fois par jour et je dois redémarrer puma pour résoudre le problème.

Je suis complètement perplexe quant à la cause de ceci. Je n'ai rien changé sur mon serveur et les modifications que j'ai apportées étaient assez simples (ajouter à une vue et ajouter à une méthode de contrôleur).

Je ne vois pas grand chose dans les fichiers de log.

J'utilise Rails 4.1.4 et Ruby 2.0.0p481

Des idées sur la raison pour laquelle mes relations se remplissent? Mon pool de connexion est défini sur 10 et j'utilise la configuration puma par défaut.

Voici une trace de pile:

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 script

#!/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

MODIFIER

Je pense que j'ai finalement réduit la question pour être avec le aérofrein bijou et en utilisant la méthode inventercurrent_user ou user_signed_in? dans application_controller.rb dans un before_action.

Voici mon contrôleur d'application:

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

Si je supprime le bloc if, je n’ai aucun problème. Depuis que j'ai introduit ce code, mon application semble manquer de connexions. Si je laisse ce bloc if en place et supprime la gemme de l'aérographe, mon application semble fonctionner correctement, avec uniquement les 5 connexions par défaut définies sur mon pool dans mon fichier database.yml.

MODIFIER

Je me suis enfin rendu compte que si je commente cette ligne dans mon fichier config/environments/production.rbconfig.exceptions_app = self.routes, je ne reçois pas l'erreur. Il semble que les routes personnalisées + inventées dans le contrôleur de l'application before_action en soient la cause. J'ai créé un problème et un projet reproductible sur github.

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

24
Catfish

Avec l'aide des gars de la stratégie, je pense que j'ai enfin compris le problème. Il semblait qu'en utilisant des pages d'erreur personnalisées avec son propre contrôleur, je n'ignorais pas le before_action get_new_messages. La solution très simple consistait donc à ajouter:

skip_before_filter :get_new_messages

à mon contrôleur d'erreur personnalisé.

Ce numéro explique en détail la raison derrière ceci: https://github.com/plataformatec/devise/issues/3422

2
Catfish

J'ai eu les mêmes problèmes qui ont été causés par trop de connexions ouvertes trop la base de données. Cela peut arriver lorsque vous avez des requêtes sur la base de données en dehors d'un contrôleur (dans un modèle, un logiciel de mailing, un générateur de pdf, ...).

Je pourrais le résoudre en encapsulant ces requêtes dans ce bloc qui ferme automatiquement la connexion.

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

Puisque Puma fonctionne en multi-thread, la taille du pool (comme mentionné par Abraham) peut également être une limitation. Essayez de l'augmenter (un peu) ...

J'espère que ça aide!

6
Railsana

En fin de compte, cette question me tourmentait encore pendant environ un an. J'ai enfin trouvé une bonne solution en travaillant avec les gars de puma. 

Améliorez votre puma à au moins 2.15.x.

0
Catfish