C'est un domaine dont je ne sais presque rien, donc je m'excuse d'avance. J'ai une suite de plus de 800 tests rspec. Soudainement et inexplicablement lors de l'exécution de l'ensemble ou juste des fichiers de test particuliers, après quelques-uns, (disons 20, même si ce n'est jamais exactement le même nombre), chaque test commence à échouer avec la même erreur:Expiration soudaine inexplicable des délais de connexion d'enregistrement actifs lors des tests avec RSPEC
Failure/Error: Unable to find matching line from backtrace
ActiveRecord::ConnectionTimeoutError:
could not obtain a database connection within 5.000 seconds (waited 5.000 seconds)
Dans une exécution typique, je vais commencer à obtenir ces erreurs après environ 20 tests de demande, et les tests 780+ restants échouent tous avec exactement la même erreur ci-dessus. J'ai essayé de revenir à un précédent git commit et une branche qui a déjà été testée parfaitement. Pas de chance - encore 780+ échecs. Aussi complètement abandonné un DB de test recréé. Aussi pas de chance.
J'ai lu beaucoup de discussions sur les pools de connexion, etc., mais je crains de ne pas savoir comment diagnostiquer ce qui se passe. Voici les faits tels que je les connais en ce moment:
- En utilisant Postgresql
- environnement de développement fonctionne très bien pour autant que je peux dire
- Entre le temps, tout fonctionnait bien et maintenant, je l'ai fait pas de changements/mises à niveau de l'environnement que je connais. Pas même les migrations de bases de données. Modifie simplement le code du modèle, de la vue et du contrôleur. Et comme je l'ai mentionné, revenir aux commits précédents ne fixe rien.
config.use_transactional_fixtures = false
dans spec_helper.rb parce que je teste la fonctionnalité ajax via Selenium. Cependant, les tests échouent, que le sélénium soit utilisé pour l'ensemble de tests en question. Même si je n'exécute que des tests n'utilisant pas le sélénium, les échecs commencent toujours après une vingtaine de tests.
au lieu des lieux de transaction, je me sers plus propre base de données avec la configuration suivante:
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
Toutes les idées ce qui se passe ici? Et plus précisément, toutes les idées où je devrais regarder pour voir quel est le problème? Je n'ai presque aucune expérience dans le traitement des problèmes ActiveRecord de ce type et je ne sais même pas par où commencer.
Mise à jour Bien que je ne sache toujours pas exactement pourquoi cela se produit, je sais précisément quel code le cause. Dans un commit récent j'avais ajouté l'envoi d'un email de notification dans un nouveau thread. Voici le code:
def teacher_notification_email
Thread.new do
UserMailer.accepted_parent_invitation_email(@parent_profile).deliver
ActiveRecord::Base.connection.close
end
end
Je l'ai utilisé ce modèle exact (avec des e-mails différents) dans beaucoup d'autres endroits dans l'application, tous se tester. Pour une raison quelconque, celui-ci provoque des erreurs de délai d'attente de base de données. Toutes les idées sur les raisons de cette situation sont les bienvenues.
Mise à jour Depuis que je ne suis pas à un stade où je comprends comment fonctionne le filetage dans ce cas, je ne connais pas la source exacte du problème, autre que ceci: ce que je l'ai lu, il est très difficile à contrôler par programme l'exécution d'un thread créé de cette manière. Cependant, j'ai trouvé une solution. Au lieu du bloc ci-dessus, j'ai changé le bloc à ce qui suit:
def teacher_notification_email
if Rails.env.test?
UserMailer.accepted_parent_invitation_email(@parent_profile).deliver
else
Thread.new do
UserMailer.accepted_parent_invitation_email(@parent_profile).deliver
ActiveRecord::Base.connection.close
end
end
end
Je suis fondamentalement en cours d'exécution un code différent pour le test de développement - pas de nouveau thread pour le test.Je suppose que c'est une mauvaise idée, mais jusqu'à ce que je puisse comprendre où est le vrai problème (un test qui n'échoue pas pour une raison quelconque, ou un code qui utilise toujours le thread qui ne force pas un test échoué), est ce que je dois aller avec.
Mise à jour finale
J'ai laissé tomber la méthode Thread.new
d'envoi des e-mails et de manière asynchrone ont, au contraire, mis en œuvre Sidekiq. C'est un peu plus de travail, mais ça marche bien et les tests sont bons ...
Intéressant. Je laisse ce problème pour le moment pendant que je travaille sur d'autres choses, mais en attendant, j'ai cherché à gérer cela via [Sidekiq] (http://sidekiq.org). Cela semble être une manière plus robuste de gérer des opérations asynchrones comme l'envoi d'un email. Voici un bon [Railscast] (http://railscasts.com/episodes/366-sidekiq) dessus. – tobogranyte
cela va certainement fonctionner. Une autre façon de faire la même chose serait de libérer manuellement la connexion du pool de connexions: ActiveRecord :: Base.connection_pool.release_connection. Fermer une connexion ne l'ajoutera pas au pool. – Jordan