No se puede poner en cola el apretón de manos después de invocar la salida

82

He implementado el siguiente código:

module.exports = {
    getDataFromUserGps: function(callback)
    {
        connection.connect();
        connection.query("SELECT * FROM usergps", 
            function(err, results, fields) {
                if (err) return callback(err, null);
                return callback(null, results);
            }
        ); 
        connection.end();
    },
    loginUser: function(login, pass, callback)
    {
        connection.connect();
        connection.query(
            "SELECT id FROM users WHERE login = ? AND pass = ?",
            [login, pass],
            function(err, results, fields) 
            {
                if (err) return callback(err, null);
                return callback(null, results);
            }
        ); 
        connection.end();
    },
    getUserDetails: function(userid, callback)
    {
        connection.connect();
        connection.query(
            "SELECT * FROM userProfilDetails LEFT JOIN tags ON userProfilDetails.userId = tags.userId WHERE userProfilDetails.userid = ?",
            [userid],
            function(err, results, fields)
            {
                if (err) return callback(err, null);
                return callback(null, results);
            }
        );
        connection.end();
    },
    addTags: function(userId, tags)
    {
        connection.connect();
        connection.query(
            "INSERT INTO tag (userId, tag) VALUES (?, ?)",
            [userId, tags],
            function(err, results, fields)
            {
                if (err) throw err;
            }
        )
        connection.end();
    }
}

Todo funciona muy bien solo por primera vez. Si quiero "usar" la consulta por segunda vez, aparece el siguiente error:

Cannot enqueue Handshake after invoking quit

He intentado no hacer .end()conexiones pero no ayudó.

¿Cómo puedo solucionar este problema?

radek
fuente
2
¿Podría al menos cerrar la pregunta?
Andrew Rhyne
Para mí, el error se produjo cuando intento abrir una conexión mientras una está abierta (es decir, tengo dos llamadas connection.connect () una al lado de la otra)
sqram
llamar connection.enddentro de la connection.queryfunción de devolución de llamada porque se ejecutará de forma asincrónica.
Arjun Singh

Respuestas:

241

Si está utilizando el módulo node-mysql, simplemente elimine el .connect y .end. Solo resolví el problema yo mismo. Aparentemente, introdujeron código innecesario en su última iteración que también tiene errores. No es necesario que se conecte si ya ha ejecutado la llamada createConnection

Andrew Rhyne
fuente
21
¡Esa es la peor respuesta que he encontrado! cuando crea una conexión, y se abre, ahora, creo que creamos un nodo mysql en una función, cada vez que se llama a la función, crea y mantiene abierta la conexión, y después de un corto tiempo, obtiene el alcance máximo de la conexión mysql
Ata
4
Entonces lo estás haciendo mal. Se supone que debes reutilizar la conexión
Andrew Rhyne
6
@ata node-mysql implementa un grupo de conexiones. No se supone que debe destruir el objeto de conexión con cada solicitud, ya que no es la solicitud real. Ni siquiera estoy seguro de por qué su comentario está recibiendo votos a favor. Claramente la gente no está leyendo los documentos
Andrew Rhyne
5
Si está ejecutando aws lambda, lambda seguirá agotando el tiempo si no cierra la conexión. por lo que todas estas sugerencias no ayudan. He pasado días en este tema.
Joseph Bolade Caxton-Idowu
Gracias por eso me funciona. La respuesta a continuación de @ XP1 brinda más detalles sobre este comportamiento.
KeitelDOG
54

De acuerdo a:

TL; DR Debe establecer una nueva conexión llamando al createConnectionmétodo después de cada desconexión.

y

Nota: Si está atendiendo solicitudes web, no debería finalizar las conexiones en cada solicitud. Simplemente cree una conexión al iniciar el servidor y use el objeto de conexión / cliente para consultar todo el tiempo. Puede escuchar el evento de error para manejar la desconexión del servidor y para fines de reconexión. Código completo aquí .


Desde:

Dice:

El servidor se desconecta

Puede perder la conexión a un servidor MySQL debido a problemas de red, el tiempo de espera del servidor o el bloqueo del servidor. Todos estos eventos se consideran errores fatales y tendrán laerr.code = 'PROTOCOL_CONNECTION_LOST' . Consulte la sección Manejo de errores para obtener más información.

La mejor manera de manejar tales desconexiones inesperadas se muestra a continuación:

function handleDisconnect(connection) {
  connection.on('error', function(err) {
    if (!err.fatal) {
      return;
    }

    if (err.code !== 'PROTOCOL_CONNECTION_LOST') {
      throw err;
    }

    console.log('Re-connecting lost connection: ' + err.stack);

    connection = mysql.createConnection(connection.config);
    handleDisconnect(connection);
    connection.connect();
  });
}

handleDisconnect(connection);

Como puede ver en el ejemplo anterior, la reconexión de una conexión se realiza estableciendo una nueva conexión. Una vez terminado, un objeto de conexión existente no se puede volver a conectar por diseño.

Con Pool, las conexiones desconectadas se eliminarán del pool, liberando espacio para que se cree una nueva conexión en la próxima llamada getConnection.


He modificado la función de modo que cada vez que se necesita una conexión, una función de inicialización agrega los controladores automáticamente:

function initializeConnection(config) {
    function addDisconnectHandler(connection) {
        connection.on("error", function (error) {
            if (error instanceof Error) {
                if (error.code === "PROTOCOL_CONNECTION_LOST") {
                    console.error(error.stack);
                    console.log("Lost connection. Reconnecting...");

                    initializeConnection(connection.config);
                } else if (error.fatal) {
                    throw error;
                }
            }
        });
    }

    var connection = mysql.createConnection(config);

    // Add handlers.
    addDisconnectHandler(connection);

    connection.connect();
    return connection;
}

Inicializando una conexión:

var connection = initializeConnection({
    host: "localhost",
    user: "user",
    password: "password"
});

Sugerencia menor: es posible que esto no se aplique a todos, pero me encontré con un problema menor relacionado con el alcance. Si el OP considera que esta edición es innecesaria, puede optar por eliminarla. Para mí, tuve que cambiar una línea initializeConnection, que era var connection = mysql.createConnection(config);simplemente

connection = mysql.createConnection(config);

La razón es que si connectiones una variable global en su programa, entonces el problema anterior era que estaba creando una nueva connectionvariable al manejar una señal de error. Pero en mi código de nodejs, seguí usando la misma connectionvariable global para ejecutar consultas, por lo que la nueva connectionse perdería en el alcance local del initalizeConnectionmétodo. Pero en la modificación, asegura que la connectionvariable global se restablezca. Esto puede ser relevante si está experimentando un problema conocido como

No se puede poner en cola la consulta después de un error fatal

después de intentar realizar una consulta después de perder la conexión y luego reconectarse con éxito. Esto puede haber sido un error tipográfico del OP, pero solo quería aclararlo.

XP1
fuente
1
Código impresionante, pero mi script todavía parece salir (código 8) después de 90 segundos aparentemente sin siquiera ingresar a la rutina addDisconectHandler. Ideas?
emc
Brillante respuesta, fui por la agrupación después de una refactorización, pero esta es una excelente opción.
Pogrindis
Buena respuesta gracias. Esto debería ser parte del documento oficial (y manejado por node-mysql, no por el desarrollador).
Skoua
1
Esta es una respuesta fantástica, pero tengo una sugerencia / ajuste que necesitaba hacer para que esto funcione para mí. No estoy seguro de si esto será necesario para todos, pero definitivamente me ayudó. Puede optar por eliminar la edición si cree que no es necesaria, gracias por su ayuda hasta ahora.
Chris Gong
23

Tuve el mismo problema y Google me trajo hasta aquí. Estoy de acuerdo con @Ata en que no es correcto simplemente eliminar end(). Después de buscar más en Google, creo que usar poolinges una mejor manera.

Documento de node-mysql sobre la agrupación

Es como esto:

var mysql = require('mysql');
var pool  = mysql.createPool(...);

pool.getConnection(function(err, connection) {
    connection.query( 'bla bla', function(err, rows) {
        connection.release();
    });
});
hbrls
fuente
7

No conecte () y end () dentro de la función. Esto causará problemas en llamadas repetidas a la función. Haz la conexión solamente

var connection = mysql.createConnection({
      host: 'localhost',
      user: 'node',
      password: 'node',
      database: 'node_project'
    })

connection.connect(function(err) {
    if (err) throw err

});

una vez y reutilice esa conexión.

Dentro de la función

function insertData(name,id) {

  connection.query('INSERT INTO members (name, id) VALUES (?, ?)', [name,id], function(err,result) {
      if(err) throw err
  });


}
ajin
fuente
5

Funciones de AWS Lambda

Utilice mysql.createPool () con connection.destroy ()

De esta manera, las nuevas invocaciones usan el grupo establecido, pero no mantienen la función en ejecución. Aunque no obtiene el beneficio completo de la agrupación (cada nueva conexión usa una nueva conexión en lugar de una existente), hace que una segunda invocación pueda establecer una nueva conexión sin que la anterior tenga que cerrarse primero.

Respecto a connection.end()

Esto puede provocar que una invocación posterior arroje un error. La invocación se reintentará más tarde y funcionará, pero con un retraso.

Respecto mysql.createPool()aconnection.release()

La función Lambda seguirá ejecutándose hasta el tiempo de espera programado, ya que todavía hay una conexión abierta.

Ejemplo de código

const mysql = require('mysql');

const pool = mysql.createPool({
  connectionLimit: 100,
  host:     process.env.DATABASE_HOST,
  user:     process.env.DATABASE_USER,
  password: process.env.DATABASE_PASSWORD,
});

exports.handler = (event) => {
  pool.getConnection((error, connection) => {
    if (error) throw error;
    connection.query(`
      INSERT INTO table_name (event) VALUES ('${event}')
    `, function(error, results, fields) {
      if (error) throw error;
      connection.destroy();
    });
  });
};
James Nuanez
fuente
Tengo un script NodeJS ejecutándose como una función AWS Lambda. Hace ping a una API de Azure que devuelve 100 registros a la vez, con una URL "siguiente" para recuperar los siguientes 100, hasta el final del conjunto de datos. Entonces mi función INSERT se llama varias veces. Recibía el error "No se puede poner en cola el protocolo de enlace después de invocar salir" hasta que eliminé las líneas connection.connect () y connection.end (). ¿Tiene más sentido usar una piscina aquí en su lugar? No estoy seguro de cuándo llamaría a "connection.end ()" cuando la API devuelva el conjunto de datos final ...
Shafique
2

en lugar de connection.connect();uso -

if(!connection._connectCalled ) 
{
connection.connect();
}

si ya está llamado entonces connection._connectCalled =true,
& no se ejecutará connection.connect();

nota - no usarconnection.end();

Kundan Thakur
fuente
1

Creo que este problema es similar al mío:

  1. Conectarse a MySQL
  2. Finalizar el servicio MySQL (no debe salir del script del nodo)
  3. Inicie el servicio MySQL, el nodo se vuelve a conectar a MySQL
  4. Consultar la base de datos -> FAIL (No se puede poner en cola la consulta después de un error fatal).

Resolví este problema recreando una nueva conexión con el uso de promesas (q).

mysql-con.js

'use strict';
var config          = require('./../config.js');
var colors          = require('colors');
var mysql           = require('mysql');
var q               = require('q');
var MySQLConnection = {};

MySQLConnection.connect = function(){
    var d = q.defer();
    MySQLConnection.connection = mysql.createConnection({
        host                : 'localhost',
        user                : 'root',
        password            : 'password',
        database            : 'database'
    });

    MySQLConnection.connection.connect(function (err) {
        if(err) {
            console.log('Not connected '.red, err.toString().red, ' RETRYING...'.blue);
            d.reject();
        } else {
            console.log('Connected to Mysql. Exporting..'.blue);
            d.resolve(MySQLConnection.connection);
        }
    });
    return d.promise;
};

module.exports = MySQLConnection;

mysqlAPI.js

var colors          = require('colors');
var mysqlCon        = require('./mysql-con.js');
mysqlCon.connect().then(function(con){
   console.log('connected!');
    mysql = con;
    mysql.on('error', function (err, result) {
        console.log('error occurred. Reconneting...'.purple);
        mysqlAPI.reconnect();
    });
    mysql.query('SELECT 1 + 1 AS solution', function (err, results) {
            if(err) console.log('err',err);
            console.log('Works bro ',results);
    });
});

mysqlAPI.reconnect = function(){
    mysqlCon.connect().then(function(con){
      console.log("connected. getting new reference");
        mysql = con;
        mysql.on('error', function (err, result) {
            mysqlAPI.reconnect();
        });
    }, function (error) {
      console.log("try again");
        setTimeout(mysqlAPI.reconnect, 2000);
    });
};

Espero que esto ayude.

tsuz
fuente
0

SOLUCIÓN: para evitar este error (para AWS LAMBDA):

Para salir del "bucle de eventos de Nodejs", debe finalizar la conexión y luego volver a conectarse. Agregue el siguiente código para invocar la devolución de llamada:

connection.end( function(err) {
        if (err) {console.log("Error ending the connection:",err);}

       //  reconnect in order to prevent the"Cannot enqueue Handshake after invoking quit"

         connection = mysql.createConnection({
                host     : 'rds.host',
                port     :  3306,
                user     : 'user',
               password : 'password',
               database : 'target database'

               });
        callback(null, {
            statusCode: 200,
            body: response,

        });
    });
Jorge Valvert
fuente
¿No cada invocación de la función lambda resultará en una conexión mysql abierta? Esta es solo otra forma de hacer la respuesta aceptada que no es una buena idea
Brian McCall
1
¡No! Cuando llama a connection.end, el ciclo finaliza, pero la conexión a la base de datos permanece en el estado "Saliendo de la conexión". Cuando intentas abrir una nueva conexión, siempre obtendrás el error "No se puede poner en cola el protocolo de enlace después de invocar salir", por lo que el createConnection adicional de hecho, obtiene este error de falla y la siguiente conexión no falla. Esta es solo una forma de solucionar este problema, de hecho, el módulo mysql necesita realizar un final de conexión más limpio.
Jorge Valvert
0

Si está tratando de obtener una lambda, descubrí que terminar el controlador con hace context.done()que la lambda termine. Antes de agregar esa 1 línea, simplemente se ejecutaría y se ejecutaría hasta que se agotara el tiempo.

JonTroncoso
fuente
¿Ejemplo de código para esto, por favor?
Shafique
0

Puede usar debug: false,

Ejemplo: // conexión mysql

var dbcon1 = mysql.createConnection({
      host: "localhost",
      user: "root",
      password: "",
      database: "node5",
      debug: false,
    });
Jay Bharat
fuente