Nodejs mysql how to handle timeout and Handshake inactivity timeout - mysql

I am a newbie in Nodejs and I have a lambda function written on NodeJS that's supposed to delete some rows and insert some data on a mysql db.
I have come across various instances with error messages such as PROTOCOL_SEQUENCE_TIMEOUT, PROTOCOL_CONNECTION_LOST and an instance where the RDS db dns couldn't be resolved and connect to the db.
I was wondering how I might handle these events so I'd be able to re-connect and proceed.
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'somehost',
user : 'someuser',
password : 'somepassword',
database : 'somedb',
port : 3306
});
pool.on('connection', function (connection) {
console.log('Pool id %d connected', connection.threadId);
});
pool.on('enqueue', function () {
console.log('Waiting for available connection slot');
});
exports.handler = async (event, context) => {
context.callbackWaitsForEmptyEventLoop = false;
let request = JSON.parse(event.body);
/** SOME OTHER LOGIC HERE **/
let delete_query = "DELETE FROM answers WHERE sId= ? AND `key` = ?";
pool.query(delete_query, [sId, questionId], function(err, result){
if(err) throw err;
});
let insert_query = "INSERT INTO answers (qId, sId, `key`, value, hutk) VALUES ?";
pool.query(insert_query, [values], function(err, result){
if(err) throw err;
console.log("Successfull Insert")
});
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify({message : 'success'}),
};
return response;
};
And also am I using the best approach to connecting to the db as in a pool or should I be using just a connection etc?

You can cache the connection, so the first call to your lambda would create the connection and the 2nd call (if the lambda is not cold started) can reuse the connection and is much faster.
Here is how we do it:
const mysql = require('mysql');
const util = require('util');
let mySQLconnection = undefined;
exports.handler = async function handler(event, context) {
try {
getMySQLConnection();
const queryResult = await mySQLconnection.query('Select * from yourtable where id = ? and attribute = ?', [id, attribute]);
} catch (error) {
console.log('ERROR: ' + error);
}
};
function getMySQLConnection() {
if (mySQLconnection !== undefined) {
return;
}
mySQLconnection = mysql.createConnection(yourConnectionJson);
mySQLconnection.query = util.promisify(mySQLconnection.query);
}
You could also do a connection retry in the catch block.

Related

convert a NodeJS lambda function (AWS) to use "async" (promises, etc.) instead of callbacks

I have a lambda function that connects to mysql and runs a set of queries, but I actually have a sequence of mysql queries that need to run one after another. I.e., the value of one query is used in the next query, etc.
Currently, I have a bunch of callbacks to achieve this, but this is leading to "callback hell". How would I rewrite this to use async / await?
My code is actually split into 2 files. The first file does an initial query, and then the value is passed into a function of the second file. Please note that the mysql node_module is included but not shown here. The AWS API gateway calls index.js
// index.js
var mysql = require('mysql'); // from node_modules
var config = require('./config.json');
var dashboard = require('./dashboard.js');
var pool = mysql.createPool({
host : config.dbhost,
user : config.dbuser,
password : config.dbpassword,
database : config.dbname
});
exports.handler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
pool.getConnection(function(err, connection) {
// check for mysql connection error first
if ( err ) {
throw err;
}
let qry = "select id from some_table where some_field = ?";
let someval = event.queryStringParameters.someval;
connection.query(qry, [someval], function(error, result) {
if ( error ) {
throw err;
}
else {
dashboard.processRequest(connection, callback, event, res[0].id);
}
});
});
}
// dashboard.js
module.exports = {
jsonResponse: function(results) {
return {
"statusCode": 200,
"body": JSON.stringify({ results }),
"isBase64Encoded": false,
"headers": {
"Access-Control-Allow-Origin": "*"
}
};
},
processRequest: function(connection, callback, event, val) {
let qry = "update first_table set some_field = ?";
connection.query(qry, [val], function(error, results) {
// return to client if error
if (error) {
callback(null, this.jsonResponse(error));
}
else {
// assume that this table must be update AFTER the previous statement
qry = "select id from second_table where some_field = ?";
connection.query(qry, [val], function(error1, results1) {
// return to client if error
if ( error1 ) {
callback(null, this.jsonResponse(error1));
}
qry = "update third_table set some_field = ? where id = ?";
connection.query(qry, [results1[0].id], function(error2, results2) {
// release connection when all queries are completed
connection.release();
if ( error2 ) {
callback(null, this.jsonResponse(error2));
}
else {
callback(null, this.jsonResponse(results2));
}
});
});
}
});
}
};
It was suggested to me that something like the below code might work. Unfortunately, it does not. I was curious to know why using try...catch blocks in the way shown below is not working, and is it the same thing as what you've shown, but just written differently?
// index.js
var mysql = require('mysql'); // from node_modules
var config = require('./config.json');
var dashboard = require('./dashboard.js');
var pool = mysql.createPool({
host : config.dbhost,
user : config.dbuser,
password : config.dbpassword,
database : config.dbname
});
exports.handler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
pool.getConnection(function(err, connection) {
// check for mysql connection error first
if ( err ) {
throw err;
}
let qry = "select id from users where username = ? limit 1;";
let username = event.queryStringParameters.username;
try {
let res = await connection.query(qry, [event.queryStringParameters.username]);
dashboard.processRequest(connection, callback, event, res[0].id);
} catch (err) {
console.log(err);
}
});
}
// dashboard.js
module.exports = {
jsonResponse: function (results) {
return {
"statusCode": 200,
"body": JSON.stringify({results}),
"isBase64Encoded": false,
"headers": {
"Access-Control-Allow-Origin": "*"
}
};
},
processRequest: async function (connection, callback, event, val) {
let qry = "update first_table set some_field = ?";
try {
let results = await connection.query(qry, [val]);
qry = "select id from second_table where some_field = ?";
try {
let results1 = await connection.query(qry, [val]);
qry = "update third_table set some_field = ? where id = ?";
try {
let results2 = await connection.query(qry, [results1[0].id]);
connection.release();
callback(null, this.jsonResponse(results2));
} catch (error2) {
callback(null, this.jsonResponse(error2));
}
} catch (error1) {
callback(null, this.jsonResponse(error1));
}
} catch (error) {
callback(null, this.jsonResponse(error));
}
}
};
We need use promises.
Typically I follow this approach:
Create one async method mainProcess and have bunch of methods step by step called with in that method. one after the other with await or all at once.
Each async method getConnection and runQuery in this case, called within mainProcess must a Promise.
If any errors from these methods i.e promise rejects from individual methods, goes in catch block of mainProcess().
If no errors, all methods within mainProcess gets executed and goes to then block of mainProcess()
Your code can be refactored like this (just wrote in an editor untested)
var pool = mysql.createPool({
host: config.dbhost,
user: config.dbuser,
password: config.dbpassword,
database: config.dbname,
});
exports.handler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
/**
* Main Lambda Process
*/
const mainProcess = async () => {
// Get Connection
let connection = await getConnection();
// Run Step 1
let qry1 = "select id from some_table1 where some_field = ?";
const response1 = await runQuery(connection, qry1, { someFiledValue: 1222})
// Run Step 2
let qry2 = "select id from some_table2 where some_field = ?";
const resonse2 = await runQuery(connection, qry2, { someFiledValue: 1222})
return 'All Good';
});
}
mainProcess()
.then(result => {
// All lambda success messages are returned from here
callback(null, result);
})
.catch(error => {
// All lambda errors thrown from here
callback(error);
});
};
function getConnection(qry, parms) {
return new Promise((resolve, reject) => {
pool.getConnection(function (error, connection) {
if (error) {
// return to client if error
reject(error);
} else {
// Return response
resolve(connection);
}
});
});
}
/**
* Runs a query, either resolves or rejects
*/
function runQuery(connection, qry, parms) {
return new Promise((resolve, reject) => {
connection.query(qry, [val], function (error, results) {
if (error) {
// return to client if error
reject(error);
} else {
// Return response
resolve(result);
}
});
});
}
When you're dealing with a lambda function which performs an async task you have two solutions:
you can use non async handlers, in which case you need to invoke "callback" on promises as you did in your example
you can use async handlers, which does not requires the "callback" input and that allows you to write async/await code, like the following example:
const mysql = require('mysql2/promise');
exports.handler = async(event, context) => {
//get path variable
const { pathVar } = event.pathParameters;
// get connection
const connection = await mysql.createConnection({
host : process.env.RDS_HOSTNAME,
user : process.env.RDS_USERNAME,
password : process.env.RDS_PASSWORD,
database : process.env.RDS_DB_NAME
});
// get text query
const textQuery = `SELECT field FROM entity WHERE attribute = ${pathVar}`;
// get res
const results = await connection.execute(textQuery);
return {
"statusCode": 200,
"body": results,
"isBase64Encoded": false
}
}
You can have a look at the AWS docs: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html

Nodejs 12 Callback not working for mysql connection?

I'm writing this code to run in AWS Lambda.
Looking at mysql connection documentation
I expect, if things are working with out errors, to get the message "Database connected successfully!"
and then to get a message "connected as id " but that is not the order that it is happening. Here is my code.
'use strict';
let mysql = require('mysql');
const connection = mysql.createConnection({
dateStrings: true,
host : process.env.rds_host,
user : process.env.rds_user,
password : process.env.rds_password,
database : process.env.rds_database,
port : process.env.rds_port
});
exports.handler = (event, context, callback) => {
//prevent timeout from waiting event loop
context.callbackWaitsForEmptyEventLoop = false;
let sql = 'SELECT * FROM company ORDER BY points DESC, name ASC';
let data = null;
console.log('\nGetCompanies SQL: ', sql);
let responseBody = "";
let statusCode = 0;
connection.connect(function(err) {
if (err) {
statusCode = 500;
responseBody = err;
}
else{
console.log("Database connected successfully!");
statusCode = 200;
responseBody = "Database connected successfully!";
}
});
console.log('connected as id ' + connection.threadId);
connection.query(sql, data, function(queryError, results) {
if(queryError) {
console.error(queryError.message);
callback(queryError);
}
else
{
console.log('\nGetCompanies Results: ', results[0]);
callback(null, results);
}
});
};
Here is the logged output:
INFO
GetCompanies SQL: SELECT * FROM company ORDER BY points DESC, name ASC 2020-01-01T11:52:57.813Z
INFO connected as id null 2020-01-01T11:52:57.952Z
INFO Database connected successfully! 2020-01-01T11:52:57.974Z
My thought was that the function that I supply to:
connection.connect(function(err) {
would execute before any code after connection.connect. Am I wrong to think that?
One more question: Why is the
connected as id null? I got that code (connection.threadId) straight from the mysql docs.
I know this is no big deal if all it effects is the order of log messages but I have other functions where I can't make queries because the connection isn't there yet.
It's quite confusing when it blows right past the
if (err) {
statusCode = 500;
responseBody = err;
}
else{
<do some connection.query code here>
}
I put these log messages here because this method works and my other method doesn't.
You are getting that behaviour because console.log('connected as id ' + connection.threadId); is not waiting for connection.connect to finish connecting.
You need to use promises here.
You could try to use async/await.
exports.handler = async (event, context, callback) => {
//prevent timeout from waiting event loop
context.callbackWaitsForEmptyEventLoop = false;
let sql = 'SELECT * FROM company ORDER BY points DESC, name ASC';
let data = null;
console.log('\nGetCompanies SQL: ', sql);
let responseBody = "";
let statusCode = 0;
try {
await connection.connect();
console.log("Database connected successfully!");
console.log('connected as id ' + connection.threadId);
const queryResult = await connection.query(sql, data);
// do something with queryResult
} catch (e) {
// handle error
}
};
EDIT
The resource you linked to suggests to use promises over callbacks:
await any promise instead of using callbacks
would execute before any code after connection.connect. Am I wrong to think that?
The function you are passing to the connection.connect is called a
callback function. what that mean is, it will be called only after a
successful connection(or after the connection attempt is errored) is made. So the answer is no. The connection.connect is called and then immediately it will call the next statement. It will not wait for the current statement, because javascript is event driven and non blocking.
connected as id null? I got that code (connection.threadId) straight from the mysql docs.
This is because of the previous statement, you are logging the connectionId before a connection is obtained.
using Callbacks
Your code should be like the below. I haven't modified much, the connection to the database is made only after the callback of the connect event is called.
exports.handler = (event, context, callback) => {
//prevent timeout from waiting event loop
context.callbackWaitsForEmptyEventLoop = false;
let sql = 'SELECT * FROM company ORDER BY points DESC, name ASC';
let data = null;
console.log('\nGetCompanies SQL: ', sql);
let responseBody = "";
let statusCode = 0;
connection.connect(function (err) {
// we are inside the callback function a successful connection has been obtained , or error connecting to database
if (err) {
statusCode = 500;
responseBody = err;
}
else {
console.log("Database connected successfully!");
statusCode = 200;
responseBody = "Database connected successfully!";
}
// connection exists
console.log('connected as id ' + connection.threadId);
connection.query(sql, data, function (queryError, results) {
if (queryError) {
console.error(queryError.message);
callback(queryError);
}
else {
console.log('\nGetCompanies Results: ', results[0]);
callback(null, results);
}
});
});
};
using promises
If you could use promises to make your code readable and understandable.
import { promisify } from 'util';
exports.handler = async (event, context) => {
//prevent timeout from waiting event loop
context.callbackWaitsForEmptyEventLoop = false;
let sql = 'SELECT * FROM company ORDER BY points DESC, name ASC';
let data = null;
console.log('\nGetCompanies SQL: ', sql);
const connect = promisify(connection.connect);
const query = promisify(connection.query);
try {
const connection = await connect();
// connection exists
console.log('connected as id ' + connection.threadId);
const results= await query(sql);
return {
statusCode: 200,
responseBody: JSON.stringify(results)
}
} catch (err) {
return {
statusCode: 500,
responseBody: err.message
}
}
};
You should move your connection.query statement into the connection.connect scope as even if you can not connect to database you will try to make a query which is not working properly.
exports.handler = (event, context, callback) => {
//prevent timeout from waiting event loop
context.callbackWaitsForEmptyEventLoop = false;
let sql = 'SELECT * FROM company ORDER BY points DESC, name ASC';
let data = null;
console.log('\nGetCompanies SQL: ', sql);
let responseBody = "";
let statusCode = 0;
connection.connect(function(err) {
if (err) {
statusCode = 500;
responseBody = err;
}
else{
console.log("Database connected successfully!");
statusCode = 200;
responseBody = "Database connected successfully!";
connection.query(sql, data, function(queryError, results) {
if(queryError) {
console.error(queryError.message);
callback(queryError);
}
else
{
console.log('\nGetCompanies Results: ', results[0]);
callback(null, results);
}
});
}
});
console.log('connected as id ' + connection.threadId);
};

Lambda Node.js Mysql RDS Timeout

My lambda function written in Node.js times out when connecting to RDS.
Weird thing is, this timeout happens only for the first request.
All subsequent requests work with the DB without the timeout.
Any idea why?
Just FYI not using any VPCs.
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'ahost',
user : 'auser',
password : 'apassword',
database : 'adb',
port : 3306
});
exports.handler = async (event, context) => {
let request = JSON.parse(event.body);
let question = request.question;
let answered = question.answered;
let sId = request.sid;
let questionnaireId = request.questionnaireId;
let hutk = request.hutk;
let questionId = question.question.id;
pool.getConnection((error, connection) => {
if (error) throw error;
let values = [];
if(Array.isArray(answered)){
let i = 0;
while(i < answered.length){
let td = [
questionnaireId,
sId,
questionId,
answered[i],
hutk
];
values.push(td);
i++;
}
} else {
let td = [
questionnaireId,
sId,
questionId,
answered,
hutk
];
values.push(td);
}
let delsql = "DELETE FROM answers WHERE sId= ? AND `key` = ?";
connection.query(delsql, [sId, questionId], function(err, result){
if(err) throw err;
});
let sql = "INSERT INTO answers (qId, sId, `key`, value, hutk) VALUES ?";
connection.query(sql, [values], function(err, result){
if(err) throw err;
console.log("Successfull Insert")
connection.release();
});
});
// TODO implement
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify({message : 'success'}),
};
return response;
};
You might be running into and issue where you're releasing your pool connection while (or prior to) one of your two queries is being run.
You don't need to explicitly call getConnection once you've created the pool. More importantly if you have code where queries might execute in parallel (as you do here) you have to be very judicious in managing the connection and only releasing it once you're certain all the queries that will use it have completed.
Read more here: (https://github.com/mysqljs/mysql#pooling-connections)
Consider trying the following:
var mysql = require('mysql');
var pool = mysql.createPool({
host: 'ahost',
user: 'auser',
password: 'apassword',
database: 'adb',
port: 3306
});
pool.on('connection', function (connection) {
console.log('Pool id %d connected', connection.threadId);
});
pool.on('enqueue', function () {
console.log('Waiting for available connection slot');
});
exports.handler = async (event, context) => {
let request = JSON.parse(event.body);
let question = request.question;
let answered = question.answered;
let sId = request.sid;
let questionnaireId = request.questionnaireId;
let hutk = request.hutk;
let questionId = question.question.id;
let values = [];
if (Array.isArray(answered)) {
let i = 0;
while (i < answered.length) {
let td = [
questionnaireId,
sId,
questionId,
answered[i],
hutk
];
values.push(td);
i++;
}
}
else {
let td = [
questionnaireId,
sId,
questionId,
answered,
hutk
];
values.push(td);
}
let delete_query = "DELETE FROM answers WHERE sId= ? AND `key` = ?";
pool.query(delete_query, [sId, questionId], function(err, result) {
if (err) throw err;
});
let insert_query = "INSERT INTO answers (qId, sId, `key`, value, hutk) VALUES ?";
pool.query(insert_query, [values], function(err, result) {
if (err) throw err;
console.log("Successfull Insert")
});
// TODO implement
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify({
message: 'success'
}),
};
return response;
};

returning values from mysql in nodejs crossbar implementation

I just started with crossbar and nodejs. I have a PHP background. I know that mysql on nodejs is async so I added a callback but i can't get the callback to return a value to the register method from crossbar. What would be the correct way to handle this?
var autobahn = require('autobahn');
var mysql = require('mysql');
var q = require('q');
var connection = new autobahn.Connection({
url: 'ws://127.0.0.1:8080/ws',
realm: 'realm1'}
);
connection.onopen = function (session) {
function dologin (cb) {
var connection = mysql.createConnection({
host : 'localhost',
user : 'xxxxx',
password : 'xxxxx',
database : 'xxxxx'
});
connection.connect();
connection.query('SELECT * from users LIMIT 0,2', function(err, rows, fields) {
if (!err){
cb("Done");
}else{
cb('Error while performing Query. ');
}
});
}
function login (args) {
return dologin(function(response){
return "status: "+response;
});
}
session.register('com.cms.login', login).then(
function (reg) {
console.log("Login registered");
},
function (err) {
console.log("failed to register procedure: " + err);
}
);
};
connection.open();

Reproduce MySQL error: The server closed the connection (node.js)

I'm trying to reproduce a MySQL error I'm seeing in my node.js app on EC2 with the node mysql library:
Connection lost: The server closed the connection.
I am unable to reproduce the error locally- killing the database is handled just fine by my code- it just rechecks every few seconds and reconnects to the db once it is restarted. On EC2, it happens around 4am Pacific, but the db is still up and running fine.
I'd like to
Reproduce the crash with my local mysql
Add whatever logic I need in my mysql helper module to handle this
Here's the error in my node.js app:
2012-10-22T08:45:40.518Z - error: uncaughtException date=Mon Oct 22
2012 08:45:40 GMT+0000 (UTC), pid=14184, uid=0, gid=0,
cwd=/home/ec2-user/my-app, execPath=/usr/bin/nodejs,
version=v0.6.18, argv=[/usr/local/bin/node,
/home/ec2-user/my-app/app.js, --my-app], rss=15310848,
heapTotal=6311392, heapUsed=5123292, loadavg=[0.0029296875,
0.0146484375, 0.04541015625], uptime=3238343.511107486, trace=[column=13,
file=/home/ec2-user/my-app/node_modules/mysql/lib/protocol/Protocol.js,
function=Protocol.end, line=63, method=end, native=false, column=10,
file=stream.js, function=Socket.onend, line=80, method=onend,
native=false, column=20, file=events.js, function=Socket.emit,
line=88, method=emit, native=false, column=51, file=net.js,
function=TCP.onread, line=388, method=onread, native=false],
stack=[Error: Connection lost: The server closed the connection.,
at Protocol.end
(/home/ec2-user/my-app/node_modules/mysql/lib/protocol/Protocol.js:63:13), at Socket.onend (stream.js:80:10), at Socket.emit
(events.js:88:20), at TCP.onread (net.js:388:51)]
Here's my code (mysql helper module):
module.exports = function (conf,logger) {
var mysql = require('mysql');
var connectionState = false;
var connection = mysql.createConnection({
host: conf.db.hostname,
user: conf.db.user,
password: conf.db.pass,
database: conf.db.schema,
insecureAuth: true
});
function attemptConnection(connection) {
if(!connectionState){
connection = mysql.createConnection(connection.config);
connection.connect(function (err) {
// connected! (unless `err` is set)
if (err) {
logger.error('mysql db unable to connect: ' + err);
connectionState = false;
} else {
logger.info('mysql connect!');
connectionState = true;
}
});
connection.on('close', function (err) {
logger.error('mysqldb conn close');
connectionState = false;
});
connection.on('error', function (err) {
logger.error('mysqldb error: ' + err);
connectionState = false;
/*
if (!err.fatal) {
return;
}
if (err.code !== 'PROTOCOL_CONNECTION_LOST') {
throw err;
}
*/
});
}
}
attemptConnection(connection);
var dbConnChecker = setInterval(function(){
if(!connectionState){
logger.info('not connected, attempting reconnect');
attemptConnection(connection);
}
}, conf.db.checkInterval);
return connection;
};
Check out mysql pool feature in node-mysql
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'example.org',
user : 'bob',
password : 'secret'
});
pool.getConnection(function(err, connection) {
// connected! (unless `err` is set)
connection.end();
});
I was having similar problems and created a getConnection() wrapper function that checks the health of the mysql connection before returning it to the caller and re-establishes the connection as necessary. In my testing it has handled fatal and non-fatal connection issues transparently for the application. If the connection simply timed out, the application recovers without experiencing any errors. If there is a transient but fatal database connection problem, the application will resume functioning automatically as soon as database connectivity is available again.
As far as reproducing the problem for testing, add the following two lines to the my.ini or my.cnf file under the [mysqld] block:
interactive_timeout=30
wait_timeout=30
Here is the contents of a file I have named "database.js":
var mysql = require("mysql");
var CONFIG = require(__dirname + "/configuration");
module.exports.getConnection = function() {
// Test connection health before returning it to caller.
if ((module.exports.connection) && (module.exports.connection._socket)
&& (module.exports.connection._socket.readable)
&& (module.exports.connection._socket.writable)) {
return module.exports.connection;
}
console.log(((module.exports.connection) ?
"UNHEALTHY SQL CONNECTION; RE" : "") + "CONNECTING TO SQL.");
var connection = mysql.createConnection({
host : CONFIG.db.host,
user : CONFIG.db.user,
password : CONFIG.db.password,
database : CONFIG.db.database,
port : CONFIG.db.port
});
connection.connect(function(err) {
if (err) {
console.log("SQL CONNECT ERROR: " + err);
} else {
console.log("SQL CONNECT SUCCESSFUL.");
}
});
connection.on("close", function (err) {
console.log("SQL CONNECTION CLOSED.");
});
connection.on("error", function (err) {
console.log("SQL CONNECTION ERROR: " + err);
});
module.exports.connection = connection;
return module.exports.connection;
}
// Open a connection automatically at app startup.
module.exports.getConnection();
// If you've saved this file as database.js, then get and use the
// connection as in the following example:
// var database = require(__dirname + "/database");
// var connection = database.getConnection();
// connection.query(query, function(err, results) { ....
Here's what I ended up using, and it worked pretty well. On the occasional connection lost/restart it recovered nicely. I have a database.js file which establishes connections and checks them periodically.
To make a request:
var conn = require('./database');
var sql = 'SELECT foo FROM bar;';
conn.query(sql, [userId, plugId], function (err, rows) {
// logic
}
Here's my databbase.js
var mysql = require('mysql');
var Common = require('./common');
var conf = Common.conf;
var logger = Common.logger;
var connectionState = false;
var connection = mysql.createConnection({
host: conf.db.hostname,
user: conf.db.user,
password: conf.db.pass,
database: conf.db.schema,
insecureAuth: true
});
connection.on('close', function (err) {
logger.error('mysqldb conn close');
connectionState = false;
});
connection.on('error', function (err) {
logger.error('mysqldb error: ' + err);
connectionState = false;
});
function attemptConnection(connection) {
if(!connectionState){
connection = mysql.createConnection(connection.config);
connection.connect(function (err) {
// connected! (unless `err` is set)
if (err) {
logger.error('mysql db unable to connect: ' + err);
connectionState = false;
} else {
logger.info('mysql connect!');
connectionState = true;
}
});
connection.on('close', function (err) {
logger.error('mysqldb conn close');
connectionState = false;
});
connection.on('error', function (err) {
logger.error('mysqldb error: ' + err);
if (!err.fatal) {
//throw err;
}
if (err.code !== 'PROTOCOL_CONNECTION_LOST') {
//throw err;
} else {
connectionState = false;
}
});
}
}
attemptConnection(connection);
var dbConnChecker = setInterval(function(){
if(!connectionState){
logger.info('not connected, attempting reconnect');
attemptConnection(connection);
}
}, conf.db.checkInterval);
// Mysql query wrapper. Gives us timeout and db conn refreshal!
var queryTimeout = conf.db.queryTimeout;
var query = function(sql,params,callback){
if(connectionState) {
// 1. Set timeout
var timedOut = false;
var timeout = setTimeout(function () {
timedOut = true;
callback('MySQL timeout', null);
}, queryTimeout);
// 2. Make query
connection.query(sql, params, function (err, rows) {
clearTimeout(timeout);
if(!timedOut) callback(err,rows);
});
} else {
// 3. Fail if no mysql conn (obviously)
callback('MySQL not connected', null);
}
}
// And we present the same interface as the node-mysql library!
// NOTE: The escape may be a trickier for other libraries to emulate because it looks synchronous
exports.query = query;
exports.escape = connection.escape;
Using generic-pool, I wrote something that works locally. I guess I'll test it and see if it doesn't crash in bizarre manner on the server side.
// Test node connection pool stuff
// Create a MySQL connection pool with
// a max of 10 connections, a min of 2, and a 30 second max idle time
var poolModule = require('generic-pool');
var pool = poolModule.Pool({
name : 'mysql',
create : function(callback) {
var Client = require('mysql').Client; // use node-mysql library in all it's dubious glory
var c = new Client();
c.user = 'root';
c.password = 'xxx';
c.database = 'test';
c.on('close', function (err) {
console.log('mysqldb conn close');
});
c.on('error', function (err) {
console.log('mysqldb error: ' + err);
});
// parameter order: err, resource
// new in 1.0.6
callback(null, c);
},
destroy : function(client) { client.end(); },
max : 10,
// optional. if you set this, make sure to drain() (see step 3)
min : 2,
// specifies how long a resource can stay idle in pool before being removed
idleTimeoutMillis : 30000,
// if true, logs via console.log - can also be a function
log : true
});
var http = require('http');
http.createServer(function (req, res) {
// Get db conn
pool.acquire(function(err, client) {
if (err) {
// handle error - this is generally the err from your
// factory.create function
console.log('pool.acquire err: ' + err);
res.writeHead(500, {'Content-Type': 'application/json'});
out = {
err: err
}
res.end(JSON.stringify(out));
}
else {
client.query("select * from foo", [], function(err, results) {
if(err){
res.writeHead(500, {'Content-Type': 'application/json'});
out = {
err: err
}
res.end(JSON.stringify(out));
} else {
res.writeHead(500, {'Content-Type': 'application/json'});
out = {
results: results
}
res.end(JSON.stringify(out));
}
// return object back to pool
pool.release(client);
});
}
});
}).listen(9615);
Pretty please don't die at 4am for no apparent reason!
The solution is use pooling connection !
You can wrote code to handle connection manually, it works.
However pooling is design for this, use pooling connection solved connection drop error.
var mysql = require('mysql');
var pool = mysql.createPool({
connectionLimit : 10,
host : 'example.org',
user : 'bob',
password : 'secret',
database : 'my_db'
});
pool.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
pooling mysql connection