Une fois que notre application Ruby on Rails a fonctionné pendant un moment, elle commence à lancer des 500 avec "Le serveur MySQL est parti". Cela se produit souvent du jour au lendemain. Cela a commencé récemment, sans changement évident dans la configuration de notre serveur.
Mysql::Error: MySQL server has gone away: SELECT * FROM `widgets`
Redémarrer les versions (pas le serveur MySQL) le corrige.
Comment pouvons-nous régler ceci?
Cela est probablement dû au fait que les connexions persistantes à MySQL ont cessé (le délai d'expiration est probable s'il se produit la nuit) et que Ruby on Rails ne parvient pas à restaurer la connexion, ce qu'il devrait faire par défaut:
Dans le fichier vendor/Rails/actionpack/lib/action_controller/dispatcher.rb est le code:
if defined?(ActiveRecord)
before_dispatch { ActiveRecord::Base.verify_active_connections! }
to_prepare(:activerecord_instantiate_observers) {ActiveRecord::Base.instantiate_observers }
end
La méthode verify_active_connections!
exécute plusieurs actions, dont l'une consiste à recréer les connexions expirées.
La cause la plus probable de cette erreur est qu’il s’agit d’un monkey patch qui a redéfini le répartiteur pour qu’il n’appelle pas verify_active_connections!
, ou que verify_active_connections!
ait été modifié, etc.
Ruby on Rails 2.3 a une option de reconnexion pour votre connexion à la base de données:
production:
# Your settings
reconnect: true
Voir:
Bonne chance!
Comme l'ont dit les autres contributeurs à ce fil, il est fort probable que le serveur MySQL ait fermé la connexion à votre application Ruby on Rails en raison de son inactivité. Le délai d'attente par défaut est de 28 800 secondes ou 8 heures.
set-variable = wait_timeout=86400
Ajouter cette ligne à votre /etc/my.cnf
fera passer le délai d'attente à 24 heures http://dev.mysql.com/doc/refman/5.0/fr/server-system-variables.html#option_mysqld_wait_timeout .
Bien que la documentation ne l'indique pas, une valeur de 0 peut désactiver complètement le délai d'attente, mais vous devrez peut-être expérimenter car il ne s'agit que de spéculations.
À ma connaissance, il existe trois autres situations pouvant générer cette erreur. Le premier est le serveur MySQL en cours de redémarrage. Cela va évidemment faire tomber toutes les connexions, mais comme le client MySQL est passif, cela ne sera remarqué que lors de la prochaine requête.
La deuxième condition est si quelqu'un tue votre requête à partir de la ligne de commande MySQL, ce qui interrompt également la connexion, car le client pourrait rester dans un état non défini.
La dernière est que votre serveur MySQL redémarre tout seul en raison d’une erreur interne fatale. Autrement dit, si vous effectuez une requête simple sur une table et constatez instantanément que «MySQL est parti», je jetterai un œil attentif aux journaux de votre serveur pour rechercher une erreur matérielle ou une corruption de la base de données.
J'ai eu ce problème lors de l'envoi de très gros relevés à MySQL. MySQL limite la taille des instructions et ferme la connexion si vous dépassez cette limite.
set global max_allowed_packet = 1048576; # 2^20 bytes (1 MB) was enough in my case
Essayez ActiveRecord::Base.connection.verify!
dans Ruby on Rails 4. Vérifiez qu’il envoie une commande ping au serveur et se reconnecte s'il n'est pas connecté.
Tout d’abord, déterminez le max_connections dans MySQL:
show variables like "max_connections";
Vous devez vous assurer que le nombre de connexions que vous établissez dans votre application Ruby on Rails est inférieur au nombre maximal de connexions autorisé. Notez que des connexions supplémentaires peuvent provenir de vos processus cron jobs, processus retardés (chaque poste aura la même taille de pool dans votre database.yml
), etc.
Surveillez les connexions SQL tout au long de votre application, de l'exécution de processus, etc. en procédant comme suit dans MySQL:
show status where variable_name = 'Threads_connected';
Vous voudrez peut-être envisager de fermer les connexions après l'exécution d'une Thread
, car les connexions de base de données ne sont pas automatiquement fermées (je pense que le problème est moins grave avec les applications Ruby on Rails 4 Reaper ):
Thread.new do
begin
# Thread work here
ensure
begin
if (ActiveRecord::Base.connection && ActiveRecord::Base.connection.active?)
ActiveRecord::Base.connection.close
end
rescue
end
end
end
La connexion au serveur MySQL expire probablement.
Vous devriez pouvoir augmenter le délai d'attente dans MySQL, mais pour que le correctif soit correct, demandez à votre code de vérifier que la connexion à la base de données est toujours active et de vous reconnecter si ce n'est pas le cas.
Surveillez-vous le nombre de connexions ou de threads MySQL ouverts? Quels sont vos paramètres mysql.ini pour max_connections?
mysql> show status;
Regardez Connexions, Max_used_connections, Threads_connected et Threads_created.
Vous devrez peut-être augmenter les limites dans votre configuration MySQL ou peut-être que Rails ne ferme pas la connexion correctement *.
Remarque: je n'ai utilisé Ruby on Rails que brièvement ...
La documentation MySQL sur l’état du serveur se trouve dans http://dev.mysql.com/doc/refman/5.0/en/server-status-variables.html .
Si vous utilisez reconnect: true dans le fichier database.yml, la connexion à la base de données sera rétablie APRÈS que l'erreur ActiveRecord :: StatementInvalid soit générée (comme Dave Cheney l'a mentionné).
Malheureusement, l'ajout d'une nouvelle tentative sur l'opération de base de données a semblé nécessaire pour se protéger du délai de connexion:
begin
do_some_active_record_operation
rescue ActiveRecord::StatementInvalid => e
Rails.logger.debug("Got statement invalid #{e.message} ... trying again")
# Second attempt, now that db connection is re-established
do_some_active_record_operation
end
J'ai eu ce problème dans une application Ruby on Rails 3, en utilisant la gemme mysql2
. J'ai copié la requête incriminée et essayé de l'exécuter directement dans MySQL, et j'ai eu la même erreur, "Le serveur MySQL est parti.".
La requête en question était très grande. Un très gros insert (+1 MB). Le champ dans lequel j'essayais d'insérer était une colonne TEXT et sa taille maximale est de 64 Ko. Plutôt que de jeter une erreur, la connexion a disparu.
J'ai augmenté la taille du champ et obtenu la même chose, donc je ne sais toujours pas quel était le problème exact. Le fait est que c'était dans la base de données en raison d'une requête étrange. En tous cas!
Une autre chose à vérifier est que la configuration de la Licorne est correcte. Voir la gestion before_fork et after_fork de la connexion ActiveRecord ici: https://Gist.github.com/nebiros/2776085#file-Unicorn-rb