Mysql: Erreur étrange sur la connexion interrompue

Créé le 5 déc. 2013  ·  25Commentaires  ·  Source: go-sql-driver/mysql

Lorsque je tue les connexions qui ont des requêtes de longue durée, j'obtiens des choses comme ceci:

[MySQL] 2013/12/05 22:17:19 packets.go:30: EOF
[MySQL] 2013/12/05 22:17:19 statement.go:24: Invalid Connection
[MySQL] 2013/12/05 22:17:27 packets.go:30: EOF
[MySQL] 2013/12/05 22:17:27 statement.go:24: Invalid Connection
[MySQL] 2013/12/05 22:17:39 packets.go:30: EOF
[MySQL] 2013/12/05 22:17:39 statement.go:24: Invalid Connection

J'ai l'habitude de voir cette erreur:

2013 (HY000) at line 1: Lost connection to MySQL server during query

Autant que je sache, cette erreur est en fait renvoyée via la connexion réseau au client qui a été tué. Est-ce que cela est masqué dans le pilote ou la base de données / sql, ou n'est-ce pas une erreur transmise via le protocole comme je le pense? Pouvons-nous rendre plus clair ce qui se passe, d'une manière ou d'une autre?

bug thinking

Commentaire le plus utile

Je pense que c'est ça le problème:

package main

import (
    "database/sql"

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db, _ := sql.Open("mysql", "/")
    defer db.Close()
    tx, err := db.Begin()
    if err != nil {
        panic(err)
    }
    defer tx.Commit()
    stmt1, err := tx.Prepare("SELECT 1")
    if err != nil {
        panic(err)
    }
    rows1, err := stmt1.Query()
    if err != nil {
        panic(err)
    }
    stmt2, err := tx.Prepare("SELECT 2")
    if err != nil {
        // rows1 is not closed -> triggers busy buffer because transaction is on one connection
        // and stmt1, stmt2 use the same one.
        // Move rows1.Close() in front of tx.Prepare and it disappears
        panic(err)
    }
    rows2, err := stmt2.Query()
    if err != nil {
        panic(err)
    }
    rows1.Close()
    rows2.Close()
    stmt1.Close()
    stmt2.Close()
}

Tous les 25 commentaires

Non, aucune erreur n'est transmise. L'erreur est en fait que rien ne peut être reçu: wink:
Mais il peut être judicieux d'imprimer un message d'erreur plus descriptif que EOF

Je suppose que les erreurs statement.go:24: Invalid Connection sont le résultat de http://golang.org/issue/5718
Ces erreurs devraient disparaître lorsque la logique de reconnexion automatique est corrigée dans le package database / sql.

EOF est juste io.EOF , je ne sais pas si nous devrions filtrer et remplacer cela. C'est une sorte d'idiome Go de l'utiliser pour des cas comme celui-ci (comme par exemple dans le package net, d'où provient cette erreur). Mais au moins la signification devrait être documentée quelque part.

Quelle est votre opinion?

J'aimerais entendre ce que @gkristic en pense aussi. Il l'étudie.

J'ai suivi le code de la base de données / sql. Les erreurs statement.go:24: Invalid Connection ne sont pas le résultat de http://golang.org/issue/5718 , bien que je sois d'accord avec @julienschmidt en ce que le paquet devrait tenter de réessayer. Il y a jusqu'à dix tentatives pour les appels Query() et Exec() lorsqu'ils sont exécutés au niveau de la base de données, mais aucune lorsque vous utilisez un récepteur Stmt.

La scie @xaprb "Connexion invalide" est due au fait que la connexion MySQL n'est plus disponible lorsque Close() est appelé pour une instruction préparée, c'est-à-dire que stmt.mc.netConn est nul ici . C'est un message inoffensif, car la connexion à la base de données / sql sera fermée dès que ErrBadConn sera retourné.

Mais ce n'est pas la seule possibilité que j'ai vue; database / sql (Go 1.2) peut avoir des problèmes de concurrence qui peuvent également se retrouver avec ce message. Si une instruction (base de données / sql) est fermée lorsqu'une connexion de pilote l'utilise, l'instruction de pilote est planifiée pour être fermée au prochain putConn() par noteUnusedDriverStatement() . Mais putConn() pourrait s'exécuter avant que removeOpenStmt() soit appelé (voir (*Stmt) finalClose() ). Si, après avoir appelé des fonctions onPut attente, la connexion elle-même est fermée en raison de la limite de connexion inactive, alors le package essaiera de fermer à nouveau l'instruction, ce qui entraînera le message. C'est encore une fois inoffensif, mais néanmoins ennuyeux.

La base de données / sql de IMHO Go a besoin d'une révision minutieuse ...

@gkristic suggérez -vous des changements dans le pilote?

Le paquet database / sql a quelques défauts, j'espère apporter quelques améliorations pour Go 1.3. Revisiter la gestion des dépendances est un élément de ma liste.

Si vous découvrez des problèmes comme celui-ci, veuillez le signaler à l' adresse https://code.google.com/p/go/issues/list.

Étant donné que ces conditions (à savoir, la connexion perdue ou la double fermeture par la base de données / sql) sont inoffensives, je m'attendrais à ce que le pilote les ignore silencieusement, au lieu d'imprimer le message "Connexion invalide". À mon avis, ce dernier peut être soit: effrayant, si vous ne savez pas que c'est inoffensif; ennuyeux, une fois que vous le faites; mais peut-être trompeur, s'il y a un message identique imprimé ailleurs (et il y en a, sauf pour le numéro de ligne), cachant un véritable avertissement que vous pourriez facilement ignorer. Si stmt.mc ou stmt.mc.netConn sont nuls à la fonction Close() , je favoriserais un retour sans autre journalisation. Notez également que renvoyer driver.ErrBadConn là-bas (comme introduit à 4d3764bbcb17c31575642626baeab1bcdc30c301) n'a absolument aucun effet, car database / sql ne vérifie jamais le code de retour pour une instruction close.

En remarque, j'ajouterais probablement une fonction pour modifier l'enregistreur. Actuellement, errLog est initialisé à l'intérieur du paquet et est privé. Il imprime à une erreur standard qui peut, ou peut ne pas être disponible; c'est-à-dire qu'un processus peut choisir de fermer le descripteur pour d'autres raisons. Mais même si ce n'est pas le cas, l'utilisateur peut avoir une idée différente de la journalisation (pensez aux fichiers, au syslog, etc.). Le pilote pourrait fournir cet enregistreur par défaut, mais peut-être aussi permettre à l'utilisateur d'en définir un autre.

Dès que j'aurai du temps libre, je préparerai un petit exemple pour le problème que j'ai mentionné précédemment et rédigerai un rapport.

Merci!

@gkristic concernant l'enregistreur - vous voulez dire quelque chose comme https://github.com/go-sql-driver/mysql/pull/182 (fusionné avec master il y a une semaine)?

@arnehormann Oh, mec ... exactement ça. J'ai travaillé avec la v1.1. J'ai également vérifié les autres éléments chez le maître avant de publier, mais j'ai oublié de vérifier la journalisation. Désolé et merci!

@gkristic pas du tout un problème: sourire:

Y a-t-il des changements qui devraient être faits avant la prochaine version? Les RP sont les bienvenus :)

Salut les gars, nouvel utilisateur de go-sql-driver ici. Je remarque des problèmes similaires, mais je me demande si quelqu'un pourrait m'aider à confirmer que la cause est la même? Dans les crons de longue durée qui utilisent des goroutines, je reçois régulièrement ce qui suit.

[MySQL] 2014/05/14 20:33:05 packets.go:356: Busy buffer
[MySQL] 2014/05/14 20:33:05 packets.go:73: EOF
[MySQL] 2014/05/14 20:33:05 statement.go:24: Invalid Connection
[MySQL] 2014/05/14 20:33:05 connection.go:206: Invalid Connection

Je suis plus que disposé à me salir les mains ici, à la recherche de conseils sur la façon d'aborder le problème. Cette erreur "Connexion non valide" peut-elle être ignorée en toute sécurité dans certaines circonstances? Existe-t-il un moyen de récupérer automatiquement une autre connexion si cela est détecté?

Euh, oh ... maintenant que je vois, j'ai raté le commentaire de @julienschmidt de vendredi dernier. (Je m'excuse Julien.) @Rmulley , il semble que vous ayez rencontré le même problème que je décrivais ci-dessus. Regardez ce commentaire. La "Connexion invalide" sur statement.go: 24 est inoffensive. Le pilote doit faire ce qui convient en choisissant une connexion différente. Je ne suis pas sûr du "tampon occupé" cependant; Je ne me souviens pas avoir vu cela. Cela fait un moment que j'ai vérifié ce code. Je vais consacrer un peu de temps à y jeter un coup d'œil et voir si je peux vous aider. Malheureusement, cela ne peut pas arriver avant la fin de la semaine prochaine. Mais je vous reviendrai d'ici là.

@rmulley : Pour moi, cela ne semble pas lié à ce problème.
Veuillez essayer de reproduire cette erreur avec la version actuelle de git master au cas où vous utiliseriez une version installée par go get (qui est une version plus ancienne).
Veuillez alors ouvrir un nouveau numéro. Un exemple de code minimal déclenchant cette erreur serait certainement utile.

Il semble que (b *buffer) takeBuffer renvoie nil . Cela ne semble se produire que lorsque b.length > 0 . Cela signifierait que pour une raison quelconque, il y a des données non lues dans la mémoire tampon.
Je n'ai jamais vu cette erreur auparavant et je n'ai implémenté que ces contrôles supplémentaires pour plus de sécurité. Je n'aurais jamais cru à cette erreur dans la nature.

Selon le n ° 206, cette erreur disparaît lorsque go est exécuté à partir de la pointe. N'est-ce pas vraiment le cas?

Tout d'abord, désolé pour la réponse tardive, j'étais en vacances, puis j'ai oublié de faire un suivi. Je me suis assuré d'avoir la dernière version du pilote MySQL et je teste maintenant avec Go 1.3. Il semble que je vois plus de détails dans le message d'erreur maintenant.

[MySQL] 2014/06/23 11:46:04 packets.go:356: Busy buffer
[MySQL] 2014/06/23 11:46:04 packets.go:73: read tcp xxx.xxx.xxx.xx:3306: connection reset by peer

Est-ce un problème MySQL de mon côté? (Je n'ai rien trouvé d'utile en ligne sur la façon de résoudre ce problème)
Ou est-ce quelque chose que je peux vérifier puis reconnecter dans mon code d'une manière ou d'une autre?

J'ai rencontré le même problème que celui décrit par rmulley. ce qui était différent, c'est que le "tampon occupé" est apparu dans une requête extrêmement courte (aussi courte que 1 requête + 2 exécutables).

Je pense que le problème est lié aux déclarations préparées. J'ai constaté que si j'ai préparé une déclaration dans une transaction, comme

 stmt, err = tx.Prepare("SELECT ...")

Je dois fermer ce stmt avant de préparer une autre déclaration, sinon je rencontrerai un "tampon occupé" à chaque fois. si je n'utilise pas d'instructions préparées, par exemple faire directement à tx comme

 tx.Query("...")

ou alors

tx.Exec("...")

, c'était bien.

et, si je prépare une instruction, puis que j'exécute d'autres requêtes ou exécutions, c'est bien aussi ... ne préparez pas une autre instruction sans fermer la précédente, et ça a l'air bien.

j'espère que c'est vraiment là que se situe le problème.

Merci.

Pouvez-vous peut-être créer un petit programme Go avec lequel l'erreur peut être reproduite?
Cela semble être définitivement un bogue dans le pilote, mais pas lié au problème d'origine signalé ici.
Veuillez alors créer un nouveau problème.

Je pense que c'est ça le problème:

package main

import (
    "database/sql"

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db, _ := sql.Open("mysql", "/")
    defer db.Close()
    tx, err := db.Begin()
    if err != nil {
        panic(err)
    }
    defer tx.Commit()
    stmt1, err := tx.Prepare("SELECT 1")
    if err != nil {
        panic(err)
    }
    rows1, err := stmt1.Query()
    if err != nil {
        panic(err)
    }
    stmt2, err := tx.Prepare("SELECT 2")
    if err != nil {
        // rows1 is not closed -> triggers busy buffer because transaction is on one connection
        // and stmt1, stmt2 use the same one.
        // Move rows1.Close() in front of tx.Prepare and it disappears
        panic(err)
    }
    rows2, err := stmt2.Query()
    if err != nil {
        panic(err)
    }
    rows1.Close()
    rows2.Close()
    stmt1.Close()
    stmt2.Close()
}

Je viens de rencontrer un problème de tampon occupé similaire et je peux confirmer que

Salut les gars, je ne sais pas si cela a été résolu de quelque manière que ce soit. Je vois ce problème dans de courtes insertions effectuées avec "Exec" ->

var sql = "insérer dans la table (f1, f2 ... f25) les valeurs (?,?, ...?)"
_, err: = db.Exec (sql, ...)

Après environ 4000, parfois 5000 insertions, j'obtiens "EOF inattendu" et "Busy Buffer".
Les inserts sont serrés (dans une boucle for) mais il y a une requête REST entre chacun, donc pas de tir si rapide; même référence de connexion. C'est Go 1.3.

Merci,
Sal

@Sal - que se passe-t-il lorsque vous utilisez go 1.4?

Le mercredi 31 décembre 2014 à 22:11, Sal A. Magnone [email protected]
a écrit:

Salut les gars, je ne sais pas si cela a été résolu de quelque manière que ce soit. Je vois ça
problème dans les insertions courtes effectuées avec "Exec" ->

var sql = "insérer dans la table (f1, f2 ... f25) les valeurs (?,?, ...?)"
_, err: = db.Exec (sql, ...)

Après environ 4000, parfois 5000 insertions, j'obtiens "EOF inattendu" et "Occupé
Amortir".
Les inserts sont serrés (dans une boucle for) mais il y a une requête REST entre
chacun, donc, pas de tir si rapide; même référence de connexion. C'est Go 1.3.

Merci,
Sal

-
Répondez directement à cet e-mail ou affichez-le sur GitHub
https://github.com/go-sql-driver/mysql/issues/185#issuecomment -68479413.

Merci pour votre réponse.

1.4 - même erreur. Mort à 4205 inserts en 50 minutes environ (il y a des entrées / sorties de filet et d'écran entre chaque insert).

J'ai refactoré le code pour reprendre éventuellement là où il s'était arrêté la fois précédente. Ce n'est donc pas critique pour cette application, mais cela pourrait être critique pour d'autres applications de ce projet s'il s'agit d'un problème de demande cumulatif.

-Sal

De: carbocation [mailto: [email protected]]
Envoyé: dimanche 4 janvier 2015 09:50
Vers: go-sql-driver / mysql
Cc: Sal A. Magnone
Objet: Re: [mysql] Erreur étrange sur la connexion interrompue (# 185)

@Sal - que se passe-t-il lorsque vous utilisez go 1.4?

Le mercredi 31 décembre 2014 à 22:11, Sal A. Magnone < [email protected] [email protected] >
a écrit:

Salut les gars, je ne sais pas si cela a été résolu de quelque manière que ce soit. Je vois ça
problème dans les insertions courtes effectuées avec "Exec" ->

var sql = "insérer dans la table (f1, f2 ... f25) les valeurs (?,?, ...?)"
_, err: = db.Exec (sql, ...)

Après environ 4000, parfois 5000 insertions, j'obtiens "EOF inattendu" et "Occupé
Amortir".
Les inserts sont serrés (dans une boucle for) mais il y a une requête REST entre
chacun, donc, pas de tir si rapide; même référence de connexion. C'est Go 1.3.

Merci,
Sal

-
Répondez directement à cet e-mail ou affichez-le sur GitHub
https://github.com/go-sql-driver/mysql/issues/185#issuecomment -68479413.

-
Répondez directement à cet e-mail ou consultez-le sur GitHub https://github.com/go-sql-driver/mysql/issues/185#issuecomment -68635254. https://github.com/notifications/beacon/AA4DdvfCFMdiqPlTZITEs9VrxyyJqqFQks5neUqigaJpZM4BSM1b.gif

Ce fil se transforme-t-il en quelques fils maintenant? Les commentaires les plus récents devraient-ils être intégrés dans un nouveau numéro et celui-ci fermé?

@xaprb Je suis d'accord, je pense qu'il y a quelques discussions en cours maintenant. Nous pouvons probablement fermer celui-ci et prendre de https://github.com/go-sql-driver/mysql/issues/185#issuecomment -68479413 dans un nouveau problème.

Cette page vous a été utile?
0 / 5 - 0 notes