97

I have implemented the following code:

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();
    }
}

Everything works great only for the first time. If I want to "use" the query for the second time I get the following error:

Cannot enqueue Handshake after invoking quit

I have tried not to .end() connections but it didn't help.

How can I fix this issue?

Usama Abdulrehman
  • 1,041
  • 3
  • 11
  • 21
radek
  • 1,203
  • 1
  • 12
  • 16

13 Answers13

278

If you using the node-mysql module, just remove the .connect and .end. Just solved the problem myself. Apparently they pushed in unnecessary code in their last iteration that is also bugged. You don't need to connect if you have already ran the createConnection call

Andrew Rhyne
  • 5,060
  • 4
  • 28
  • 41
  • 30
    That is the worst answer that I have found ! when you create a connection , and it opens , now , think we create a mysql node in a function , each time the function called , it create and hold open connection ,a after a short time , you get Max Limit Reach of mysql connection – Ata Aug 07 '13 at 20:04
  • 6
    Then you are doing it wrong. You are supposed to reuse the connection – Andrew Rhyne Jan 05 '14 at 06:51
  • 8
    @ata node-mysql implements a connection pool. You aren't supposed to destroy the connection object with every request as it isn't the actual request. Not even sure why your comment is getting upvotes. Clearly people aren't reading the docs – Andrew Rhyne Feb 09 '17 at 00:50
  • 5
    If you are running in aws lambda, lambda will keep timing out if you don't close the connection. so all these suggestions does not help. I have spend days on this issue. – Joseph Bolade Caxton-Idowu Apr 19 '17 at 22:07
  • Thanks that works for me. The answer below of @XP1 gives more details on this behavior. – KeitelDOG Aug 08 '19 at 19:22
64

According to:

TL;DR You need to establish a new connection by calling the createConnection method after every disconnection.

and

Note: If you're serving web requests, then you shouldn't be ending connections on every request. Just create a connection on server startup and use the connection/client object to query all the time. You can listen on the error event to handle server disconnection and for reconnecting purposes. Full code here.


From:

It says:

Server disconnects

You may lose the connection to a MySQL server due to network problems, the server timing you out, or the server crashing. All of these events are considered fatal errors, and will have the err.code = 'PROTOCOL_CONNECTION_LOST'. See the Error Handling section for more information.

The best way to handle such unexpected disconnects is shown below:

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);

As you can see in the example above, re-connecting a connection is done by establishing a new connection. Once terminated, an existing connection object cannot be re-connected by design.

With Pool, disconnected connections will be removed from the pool freeing up space for a new connection to be created on the next getConnection call.


I have tweaked the function such that every time a connection is needed, an initializer function adds the handlers automatically:

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;
}

Initializing a connection:

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

Minor suggestion: This may not apply to everyone but I did run into a minor issue relating to scope. If the OP feels this edit was unnecessary then he/she can choose to remove it. For me, I had to change a line in initializeConnection, which was var connection = mysql.createConnection(config); to simply just

connection = mysql.createConnection(config);

The reason being that if connection is a global variable in your program, then the issue before was that you were making a new connection variable when handling an error signal. But in my nodejs code, I kept using the same global connection variable to run queries on, so the new connection would be lost in the local scope of the initalizeConnection method. But in the modification, it ensures that the global connection variable is reset This may be relevant if you're experiencing an issue known as

Cannot enqueue Query after fatal error

after trying to perform a query after losing connection and then successfully reconnecting. This may have been a typo by the OP, but I just wanted to clarify.

Chris Gong
  • 8,031
  • 4
  • 30
  • 51
XP1
  • 6,910
  • 8
  • 54
  • 61
  • 1
    Awesome code, but my script still seems to exit (code 8) after 90 seconds seemingly without even entering the addDisconectHandler routine. Ideas? – emc Apr 18 '14 at 21:56
  • Brilliant answer, I went for the pooling after a refactor, but this is an excellent option. – Pogrindis May 26 '16 at 22:35
  • Good answer thanks. This should be part of official doc (and handled by node-mysql, not developer). – Skoua Feb 07 '17 at 11:06
  • 1
    This is a fantastic answer, but I do have a suggestion/tweak that I needed to make for this to work for me. I'm not sure if this will be needed for everyone, but this definitely helped me. You may choose to remove the edit if you feel that it is unnecessary, thanks for your help thus far. – Chris Gong Apr 09 '17 at 14:17
25

I had the same problem and Google led me here. I agree with @Ata that it's not right to just remove end(). After further Googling, I think using pooling is a better way.

node-mysql doc about pooling

It's like this:

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

pool.getConnection(function(err, connection) {
    connection.query( 'bla bla', function(err, rows) {
        connection.release();
    });
});
hbrls
  • 2,110
  • 5
  • 32
  • 53
7

Do not connect() and end() inside the function. This will cause problems on repeated calls to the function. Make the connection only

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

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

});

once and reuse that connection.

Inside the function

function insertData(name,id) {

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


}
ajin
  • 1,136
  • 1
  • 11
  • 22
7

AWS Lambda functions

Use mysql.createPool() with connection.destroy()

This way, new invocations use the established pool, but don't keep the function running. Even though you don't get the full benefit of pooling (each new connection uses a new connection instead of an existing one), it makes it so that a second invocation can establish a new connection without the previous one having to be closed first.

Regarding connection.end()

This can cause a subsequent invocation to throw an error. The invocation will still retry later and work, but with a delay.

Regarding mysql.createPool() with connection.release()

The Lambda function will keep running until the scheduled timeout, as there is still an open connection.

Code example

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
  • 242
  • 3
  • 9
  • I've got a NodeJS script running as an AWS Lambda function. It pings an Azure API that returns 100 records at a time, with a "next" URL to retrieve the next 100, until the end of the data set. So my INSERT function gets called several times. I was getting the "Cannot enqueue Handshake after invoking quit" error, until I removed the connection.connect() and connection.end() lines. Does it make more sense to use a pool here instead? I'm not sure when I would call "connection.end()" when the final data set is returned from the API... – Shafique Feb 13 '20 at 21:39
2

I think this issue is similar to mine:

  1. Connect to MySQL
  2. End MySQL service (should not quit node script)
  3. Start MySQL service, Node reconnects to MySQL
  4. Query the DB -> FAIL (Cannot enqueue Query after fatal error.)

I solved this issue by recreating a new connection with the use of promises (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);
    });
};

I hope this helps.

Taku
  • 5,639
  • 2
  • 42
  • 31
2

inplace of connection.connect(); use -

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

if it is already called then connection._connectCalled =true,
& it will not execute connection.connect();

note - don't use connection.end();

Derrick
  • 3,669
  • 5
  • 35
  • 50
0

SOLUTION: to prevent this error(for AWS LAMBDA):

In order to exit of "Nodejs event Loop" you must end the connection, and then reconnect. Add the next code to invoke the callback:

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
  • 927
  • 1
  • 11
  • 20
  • Won't every invocation of the lambda function then result in an open mysql connection? This is just another way of doing the accepted answer which is not a good idea – Brian McCall May 23 '18 at 23:12
  • 1
    Nope. When you call connection.end, the loop finishes, but the connection to the database remains in "quitting connection" status. When you try to open a new connection, you will always get the error "Cannot enqueue Handshake after invoking quit", so, the additional createConnection in fact, get this failure error, and the next connection didn´t fail. This is only a way to get ride of this problem, in fact, the mysql module needs to perform a cleaner connection ending. – Jorge Valvert Aug 06 '18 at 22:49
0

If you're trying to get a lambda, I found that ending the handler with context.done() got the lambda to finish. Before adding that 1 line, It would just run and run until it timed out.

JonTroncoso
  • 791
  • 1
  • 8
  • 22
0

You can use debug: false,

Example: //mysql connection

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

A little digging showed that I wasn't closing the connection at all. So added this code before I opened up connection and when I was done with database manipulation

connection.end()
connection = mysql.createConnection(
  // database connection details
) 
connection.connect(function (err) {
  if (!err) {
    console.log("Connected!");
    var sql = `Select something from my_heart;`
    connection.query(sql, function (err, result) {
      if (!err) {
        console.log("1 record inserted");
        res.send("Recieved")
      } else {
        console.log(err.sqlMessage)
        res.send("error")
      }
    });
  }
})
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
0

Just use connection.connect() once outside of module.exports. It should be connect() once when node server is initialised, not in every request.

You can do this in this way :--

const connection = sql.createConnection({
  host: "****",
  user: "****",
  password: "*****",
  database: "****"
})
connection.connect((error) => {
  if( error ) throw new Error(error)
})

module.exports = {
  getDataFromUserGps: function(callback)
  {
      connection.query("SELECT * FROM usergps", 
          function(err, results, fields) {
              if (err) return callback(err, null);
              return callback(null, results);
          }
      ); 
  },

  ****
  ****
  ****
  
}
ayanparbat
  • 21
  • 4
0

It happens when you trigger connection.connect() more than once

Procrastinator
  • 2,526
  • 30
  • 27
  • 36