Le serveur PHP activerecord mysql est parti

J'utilise php-activerecord depuis peu de temps et je l'adore. Php-activerecord est une bibliothèque ORM open source basée sur le modèle ActiveRecord . Cependant, j'ai récemment essayé de l'utiliser en combinaison avec une application websocket basée sur la Wrench .

Cela fonctionne parfaitement, mais pour lancer le script, l'application doit être exécutée en tant que démon sur Linux afin de rendre les websockets toujours disponibles. Après un court instant d'inutilisation de l'application, puis une nouvelle tentative d'utilisation, des exceptions de base de données sont générées:

Au début, il donne un avertissement:

PHP Warning: Error while sending QUERY packet. PID=XXXXX in /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php on line 322

Ensuite, il génère une erreur fatale:

PHP Fatal error: Uncaught exception 'ActiveRecord\DatabaseException' with message 'exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 2006 MySQL server has gone away' in /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php:322

Trace de la pile:

#0 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php(322): PDOStatement->execute(Array)

#1 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Table.php(218): ActiveRecord\Connection->query('SELECT * FROM ...', Array)

#2 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Table.php(209): ActiveRecord\Table->find_by_sql('SELECT * FROM `...', Array, false, NULL)

#3 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Model.php(1567): ActiveRecord\Table->find(Array)

#4 in /home/user/domains/example.com/public_html/vendor/php-activerecord/lib/Connection.php on line 325

Il semble que php-activerecord garde la connexion MySQL ouverte tout le temps que le serveur websocket est en cours d'exécution, cela ne devrait bien sûr pas être un problème s'il tente ensuite automatiquement de se reconnecter et de relancer la requête. Mais ça ne va pas.

J'ai lu quelque chose sur la définition de MYSQL_OPT_RECONNECT . Mais je ne suis pas sûr si cela fonctionne ou comment définir cette option en utilisant php-activerecord. Quelqu'un at-il quelque expérience dans ce domaine?

Edit: Voici mes variables de configuration de timeout global

VARIABLE_NAME                   VARIABLE_VALUE  
DELAYED_INSERT_TIMEOUT          300
WAIT_TIMEOUT                    28800
CONNECT_TIMEOUT                 10
LOCK_WAIT_TIMEOUT               31536000
INNODB_ROLLBACK_ON_TIMEOUT      OFF
THREAD_POOL_IDLE_TIMEOUT        60
NET_WRITE_TIMEOUT               60
INNODB_LOCK_WAIT_TIMEOUT        50
INTERACTIVE_TIMEOUT             28800
DEADLOCK_TIMEOUT_LONG           50000000
SLAVE_NET_TIMEOUT               3600
DEADLOCK_TIMEOUT_SHORT          10000
NET_READ_TIMEOUT                30
12
Essayez-vous de maintenir la connexion plus de 8 heures?
ajouté l'auteur Rick James, source
Essayez-vous de maintenir la connexion plus de 8 heures?
ajouté l'auteur Rick James, source
J'ai eu le même problème, merci de demander;)
ajouté l'auteur Memet Olsen, source
J'ai eu le même problème, merci de demander;)
ajouté l'auteur Memet Olsen, source
Avez-vous vérifié comment activer la reconnexion automatique à la base de données en PHP? Selon lui, il n'est pas possible de définir MYSQL_OPT_RECONNECT dans PDO (et php-activerecord utilise PDO). Peut-être qu'une solution de contournement consiste à exécuter une requête simple toutes les heures?
ajouté l'auteur VolenD, source
Avez-vous vérifié comment activer la reconnexion automatique à la base de données en PHP? Selon lui, il n'est pas possible de définir MYSQL_OPT_RECONNECT dans PDO (et php-activerecord utilise PDO). Peut-être qu'une solution de contournement consiste à exécuter une requête simple toutes les heures?
ajouté l'auteur VolenD, source
Avez-vous implémenté le service de socket en PHP? Pouvez-vous me fournir un peu de code responsable de la connexion avec DB? Avez-vous essayé de vous connecter à DB via .sock à la place de tcp?
ajouté l'auteur yergo, source
Avez-vous implémenté le service de socket en PHP? Pouvez-vous me fournir un peu de code responsable de la connexion avec DB? Avez-vous essayé de vous connecter à DB via .sock à la place de tcp?
ajouté l'auteur yergo, source
OMI, MYSQL_OPT_RECONNECT n'est pas une bonne solution. Lire ma réponse ci-dessous. Il est basé sur la mise en œuvre réelle.
ajouté l'auteur ihsan, source
OMI, MYSQL_OPT_RECONNECT n'est pas une bonne solution. Lire ma réponse ci-dessous. Il est basé sur la mise en œuvre réelle.
ajouté l'auteur ihsan, source
@RTB En utilisant Active Record, vous avez alors ce que vous méritez. Active Record en tant que modèle est une abomination pour tout développeur de base de données, de même que pour toute bibliothèque basée sur ce modèle. Apprenez simplement à utiliser les SGBDR tels qu’ils ont été conçus, ou utilisez un magasin de données non relationnel si vous ne voulez qu’un magasin de données passif pour vos classes.
ajouté l'auteur David Soussan, source
@RTB En utilisant Active Record, vous avez alors ce que vous méritez. Active Record en tant que modèle est une abomination pour tout développeur de base de données, de même que pour toute bibliothèque basée sur ce modèle. Apprenez simplement à utiliser les SGBDR tels qu’ils ont été conçus, ou utilisez un magasin de données non relationnel si vous ne voulez qu’un magasin de données passif pour vos classes.
ajouté l'auteur David Soussan, source
@ Dimitri oui, j'ai le contrôle total et je viens de modifier ma question.
ajouté l'auteur RTB, source
@ Dimitri oui, j'ai le contrôle total et je viens de modifier ma question.
ajouté l'auteur RTB, source
Avez-vous le plein contrôle de la configuration de la base de données? Pouvez-vous lister tous les délais d'attente que vous avez définis dans votre configuration? Si votre configuration est vide, sélectionnez vos variables globales dans MySQL et publiez toutes les lignes contenant «timeout».
ajouté l'auteur Dimitri, source
Avez-vous le plein contrôle de la configuration de la base de données? Pouvez-vous lister tous les délais d'attente que vous avez définis dans votre configuration? Si votre configuration est vide, sélectionnez vos variables globales dans MySQL et publiez toutes les lignes contenant «timeout».
ajouté l'auteur Dimitri, source

12 Réponses

PHP ActiveRecord utilise PDO. Il n'y a absolument aucun moyen de fermer une connexion PDO, c'est la mauvaise couche de base de données pour les tâches d'arrière-plan longues.

Vous pouvez essayer d'influencer la déconnexion d'une connexion PDO avec l'extrait de code suivant.

//if not using ZF2 libraries, disconnect in some other way
$db->getDriver()->getConnection()->disconnect()
$db = NULL;
gc_collect_cycles();

Déconnectez-vous, définissez votre référence sur null, puis exécutez le ramasse-miettes. L'espoir est que cela appellera la méthode interne __destruct du PDO pour fermer réellement la connexion.

You must manage your DB connections in your own long running script. You must disconnect if your worker hasn't had to process work in a while, and you must reconnect when you have work.

La vraie solution consiste à ne pas utiliser PDO, à vous déconnecter et à vous reconnecter normalement.

Si vous définissez simplement l'infini des délais d'attente des bibliothèques client et serveur, vous rencontrerez des problèmes avec des scripts incontrôlables qui ne meurent jamais, ce qui vous oblige à redémarrer tout le serveur (ce n'est pas une bonne idée de gâcher les délais d'attente).

EDIT: En fait, j’ai eu exactement le même problème et j’ai utilisé cette solution au travail l’année dernière. Cela a résolu 99% de mes problèmes. Cependant, de temps en temps, il y avait une exception de connexion parasite que je ne pouvais pas attraper et essayer de me reconnecter. Je redémarre simplement les processus une fois par jour pour me débarrasser de ces erreurs de connexion parasites. C'est pourquoi ma réponse est, n'utilisez pas de PDO. Commutez maintenant et obtenez un contrôle réel sur les déconnexions et les reconnexions.

5
ajouté
... mysqli alors?
ajouté l'auteur angelcool.net, source

PHP ActiveRecord utilise PDO. Il n'y a absolument aucun moyen de fermer une connexion PDO, c'est la mauvaise couche de base de données pour les tâches d'arrière-plan longues.

Vous pouvez essayer d'influencer la déconnexion d'une connexion PDO avec l'extrait de code suivant.

//if not using ZF2 libraries, disconnect in some other way
$db->getDriver()->getConnection()->disconnect()
$db = NULL;
gc_collect_cycles();

Déconnectez-vous, définissez votre référence sur null, puis exécutez le ramasse-miettes. L'espoir est que cela appellera la méthode interne __destruct du PDO pour fermer réellement la connexion.

You must manage your DB connections in your own long running script. You must disconnect if your worker hasn't had to process work in a while, and you must reconnect when you have work.

La vraie solution consiste à ne pas utiliser PDO, à vous déconnecter et à vous reconnecter normalement.

Si vous définissez simplement l'infini des délais d'attente des bibliothèques client et serveur, vous rencontrerez des problèmes avec des scripts incontrôlables qui ne meurent jamais, ce qui vous oblige à redémarrer tout le serveur (ce n'est pas une bonne idée de gâcher les délais d'attente).

EDIT: En fait, j’ai eu exactement le même problème et j’ai utilisé cette solution au travail l’année dernière. Cela a résolu 99% de mes problèmes. Cependant, de temps en temps, il y avait une exception de connexion parasite que je ne pouvais pas attraper et essayer de me reconnecter. Je redémarre simplement les processus une fois par jour pour me débarrasser de ces erreurs de connexion parasites. C'est pourquoi ma réponse est, n'utilisez pas de PDO. Commutez maintenant et obtenez un contrôle réel sur les déconnexions et les reconnexions.

5
ajouté
... mysqli alors?
ajouté l'auteur angelcool.net, source

Comme indiqué, MySQL dans les scripts PHP expire quand il n'y a pas de communication entre les deux pendant un certain temps. C’est une bonne chose, car des connexions inactives satureraient les ressources de votre serveur.

L'erreur "Le serveur est parti" se produit généralement lorsqu'un calcul relativement long est effectué entre deux requêtes.

Pour éviter cela, vous pouvez

  • Periodically execute a SELECT 1 query during your execution
  • Create a wrapper around your queries which checks if connection is valid before executing
  • Use answer from this post

Cependant, je pense que la reconfiguration de MySQL pour garder la connexion ouverte plus longtemps encourage la programmation négligente et déconseillerait cette possibilité.

1
ajouté

Comme indiqué, MySQL dans les scripts PHP expire quand il n'y a pas de communication entre les deux pendant un certain temps. C’est une bonne chose, car des connexions inactives satureraient les ressources de votre serveur.

L'erreur "Le serveur est parti" se produit généralement lorsqu'un calcul relativement long est effectué entre deux requêtes.

Pour éviter cela, vous pouvez

  • Periodically execute a SELECT 1 query during your execution
  • Create a wrapper around your queries which checks if connection is valid before executing
  • Use answer from this post

Cependant, je pense que la reconfiguration de MySQL pour garder la connexion ouverte plus longtemps encourage la programmation négligente et déconseillerait cette possibilité.

1
ajouté

La raison la plus courante pour laquelle le serveur MySQL a disparu est due au fait que le serveur a expiré et a fermé la connexion.

Essayez de faire le changement suivant.

max_allowed_packet=64M

Si vous avez beaucoup de demandes, définissez-le et ne le définissez pas plus grand, car il est lié à votre environnement.

max_connections=1000

Ajouter cette ligne dans le fichier my.cnf peut résoudre votre problème. Redémarrez le service MySQL une fois que vous avez terminé avec le changement.

Read more on MySQL server has gone away

Si cela ne fonctionne pas, essayez this . La reconnexion automatique fonctionne également.

1
ajouté
La reconnexion automatique peut causer des problèmes en raison de la perte d'informations d'état. En particulier, si vous êtes dans une transaction, cela sera annulé par la déconnexion. Continuer après la reconnexion pourrait entraîner la perte/modification des données.
ajouté l'auteur Rick James, source

La raison la plus courante pour laquelle le serveur MySQL a disparu est due au fait que le serveur a expiré et a fermé la connexion.

Essayez de faire le changement suivant.

max_allowed_packet=64M

Si vous avez beaucoup de demandes, définissez-le et ne le définissez pas plus grand, car il est lié à votre environnement.

max_connections=1000

Ajouter cette ligne dans le fichier my.cnf peut résoudre votre problème. Redémarrez le service MySQL une fois que vous avez terminé avec le changement.

Read more on MySQL server has gone away

Si cela ne fonctionne pas, essayez this . La reconnexion automatique fonctionne également.

1
ajouté
La reconnexion automatique peut causer des problèmes en raison de la perte d'informations d'état. En particulier, si vous êtes dans une transaction, cela sera annulé par la déconnexion. Continuer après la reconnexion pourrait entraîner la perte/modification des données.
ajouté l'auteur Rick James, source

Si votre base de données ne gère pas plusieurs connexions et requêtes simultanées, vous pouvez définir des délais infinis. Cela n'affectera pas les ressources de base de données de manière significative. La meilleure approche consiste à envoyer des paquets ping (SELECT 1) pour renouveler le délai et rendre la connexion maintenue en vie.

1
ajouté

Si votre base de données ne gère pas plusieurs connexions et requêtes simultanées, vous pouvez définir des délais infinis. Cela n'affectera pas les ressources de base de données de manière significative. La meilleure approche consiste à envoyer des paquets ping (SELECT 1) pour renouveler le délai et rendre la connexion maintenue en vie.

1
ajouté

Cela peut également être la taille de la requête, car parfois, les ORM combinent les requêtes pour améliorer les performances.

Essayez de définir max_allowed_packet = 128M, au moins devrait être utile en tant que diagnostic.

1
ajouté

Cela peut également être la taille de la requête, car parfois, les ORM combinent les requêtes pour améliorer les performances.

Essayez de définir max_allowed_packet = 128M, au moins devrait être utile en tant que diagnostic.

1
ajouté

Afin de résoudre ce problème, je vous suggère de:

  1. Distribuez vos processus à l'aide du serveur d'emplois Gearman ( http://gearman.org/ )
  2. Gérez facilement ces processus à l'aide de Supervisor ( http://supervisord.org/ )

Voici comment.

Exécutez votre application de socket Web en tant que démon, comme vous le faisiez déjà (peut-être en utilisant cron). Ou mieux encore, gérez-le à l'aide de Supervisor. Configurez-le de sorte que Supervisor le démarre au démarrage de Supervisor et relancez automatiquement le démon s'il meurt.

Exemple de configuration:

[program:my-daemon]
command=/usr/bin/php /path/to/your/daemon/script
autostart=true
autorestart=true

Ensuite, au lieu d’exécuter le traitement de la requête dans le démon de l’application, créez un opérateur Gearman pour le gérer. Une fois inscrit, le travailleur attendra d’être exécuté/appelé. Vous devez appeler le travailleur à partir de votre application websocket, avec le paramètre de charge de travail nécessaire si nécessaire (voir le site Web Gearman pour l'explication de ce terme de charge de travail).

Dans Worker, configurez-le pour arrêter/quitter lorsqu'il a déjà terminé le travail demandé par le démon. Avec cela, vous n'aurez plus le problème "Le serveur MySQL est parti" car la connexion est immédiatement fermée.

Enfin, nous devons rendre le travailleur disponible à tout moment, tout comme le démon. Ainsi, comme pour le démon, configurez Supervisor pour qu’il démarre automatiquement et le redémarre automatiquement lorsque le travailleur meurt/s’arrête, comme ceci:

[program:my-worker]
command=/usr/bin/php /path/to/your/worker/script
autostart=true
autorestart=true

Une autre chose intéressante est que vous pouvez ajouter autant de travailleurs que vous souhaitez être en vie en attente. Ajoutez simplement la configuration suivante:

numprocs=9 #change it to any number
process_name=%(program_name)s_%(process_num)02d #to identity worker number

Depuis que nous avons demandé à Superviseur de redémarrer automatiquement chaque processus, nous avons toujours des travailleurs permanents qui fonctionnent en arrière-plan.

Here is another explanation about this strategy: http://www.masnun.com/2011/11/02/gearman-php-and-supervisor-processing-background-jobs-with-sanity.html

J'espère que cela pourra aider!

0
ajouté
En bas, s'il vous plaît, lisez attentivement la stratégie. Lire le lien que je poste aussi! Le problème "Le serveur MySQL est parti" semble simple à résoudre, mais ce n'est pas le cas.
ajouté l'auteur ihsan, source
@ArunPoudel pourquoi est-ce? J'ai implémenté ceux-ci dans l'environnement de production. Le serveur MySQL a disparu parce que la connexion est ouverte trop longtemps et que PHP ne convient pas à long terme.
ajouté l'auteur ihsan, source
@ArunPoudel Pourquoi n'est-il pas lié? Cela résout le problème "le serveur MySQL est parti".
ajouté l'auteur ihsan, source
@ArunPoudel Alors maintenant, vous dites que c'est lié, mais idiomatique? Si vous avez une autre solution, écrivez votre propre réponse. C'est le moyen privilégié de SO. * En fait, je ne comprends pas ce que vous voulez dire par idiomatique.
ajouté l'auteur ihsan, source
Oui, je l’ai fait et cela n’a aucun rapport avec la publication. Le problème rencontré par le PO est très différent de ce que vous essayez de résoudre.
ajouté l'auteur Arun Poudel, source
Je pense que cela ne résout aucun des problèmes du PO.
ajouté l'auteur Arun Poudel, source
Oui, en utilisant une approche insensée. Il ne s'agit pas simplement de résoudre le problème. Mais le faire de manière idiomatique. Il existe d'autres moyens de traiter ce type de problèmes, et le drapeau de reconnexion en est un.
ajouté l'auteur Arun Poudel, source

Afin de résoudre ce problème, je vous suggère de:

  1. Distribuez vos processus à l'aide du serveur d'emplois Gearman ( http://gearman.org/ )
  2. Gérez facilement ces processus à l'aide de Supervisor ( http://supervisord.org/ )

Voici comment.

Exécutez votre application de socket Web en tant que démon, comme vous le faisiez déjà (peut-être en utilisant cron). Ou mieux encore, gérez-le à l'aide de Supervisor. Configurez-le de sorte que Supervisor le démarre au démarrage de Supervisor et relancez automatiquement le démon s'il meurt.

Exemple de configuration:

[program:my-daemon]
command=/usr/bin/php /path/to/your/daemon/script
autostart=true
autorestart=true

Ensuite, au lieu d’exécuter le traitement de la requête dans le démon de l’application, créez un opérateur Gearman pour le gérer. Une fois inscrit, le travailleur attendra d’être exécuté/appelé. Vous devez appeler le travailleur à partir de votre application websocket, avec le paramètre de charge de travail nécessaire si nécessaire (voir le site Web Gearman pour l'explication de ce terme de charge de travail).

Dans Worker, configurez-le pour arrêter/quitter lorsqu'il a déjà terminé le travail demandé par le démon. Avec cela, vous n'aurez plus le problème "Le serveur MySQL est parti" car la connexion est immédiatement fermée.

Enfin, nous devons rendre le travailleur disponible à tout moment, tout comme le démon. Ainsi, comme pour le démon, configurez Supervisor pour qu’il démarre automatiquement et le redémarre automatiquement lorsque le travailleur meurt/s’arrête, comme ceci:

[program:my-worker]
command=/usr/bin/php /path/to/your/worker/script
autostart=true
autorestart=true

Une autre chose intéressante est que vous pouvez ajouter autant de travailleurs que vous souhaitez être en vie en attente. Ajoutez simplement la configuration suivante:

numprocs=9 #change it to any number
process_name=%(program_name)s_%(process_num)02d #to identity worker number

Depuis que nous avons demandé à Superviseur de redémarrer automatiquement chaque processus, nous avons toujours des travailleurs permanents qui fonctionnent en arrière-plan.

Here is another explanation about this strategy: http://www.masnun.com/2011/11/02/gearman-php-and-supervisor-processing-background-jobs-with-sanity.html

J'espère que cela pourra aider!

0
ajouté
En bas, s'il vous plaît, lisez attentivement la stratégie. Lire le lien que je poste aussi! Le problème "Le serveur MySQL est parti" semble simple à résoudre, mais ce n'est pas le cas.
ajouté l'auteur ihsan, source
@ArunPoudel pourquoi est-ce? J'ai implémenté ceux-ci dans l'environnement de production. Le serveur MySQL a disparu parce que la connexion est ouverte trop longtemps et que PHP ne convient pas à long terme.
ajouté l'auteur ihsan, source
@ArunPoudel Pourquoi n'est-il pas lié? Cela résout le problème "le serveur MySQL est parti".
ajouté l'auteur ihsan, source
@ArunPoudel Alors maintenant, vous dites que c'est lié, mais idiomatique? Si vous avez une autre solution, écrivez votre propre réponse. C'est le moyen privilégié de SO. * En fait, je ne comprends pas ce que vous voulez dire par idiomatique.
ajouté l'auteur ihsan, source
Oui, je l’ai fait et cela n’a aucun rapport avec la publication. Le problème rencontré par le PO est très différent de ce que vous essayez de résoudre.
ajouté l'auteur Arun Poudel, source
Je pense que cela ne résout aucun des problèmes du PO.
ajouté l'auteur Arun Poudel, source
Oui, en utilisant une approche insensée. Il ne s'agit pas simplement de résoudre le problème. Mais le faire de manière idiomatique. Il existe d'autres moyens de traiter ce type de problèmes, et le drapeau de reconnexion en est un.
ajouté l'auteur Arun Poudel, source