4

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 ...

Répondre

5

Il semble que cela soit lié à un enregistrement actif touchant un thread créé. Il semble que la connexion db ne soit pas renvoyée dans le pool tant qu'elle n'est pas récoltée. J'ai pu résoudre ce problème en demandant explicitement une connexion à l'avance et en la fermant une fois que j'ai terminé. Essayez ceci:

Thread.new do 
    ActiveRecord::Base.connection_pool.with_connection do |conn| 
     UserMailer.accepted_parent_invitation_email(@parent_profile).deliver 
    end 
end 
+0

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

+0

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

Questions connexes