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.rb
config.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
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
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!
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
.