Promise-MySQL cannot release connections back to the pool - mysql

I am very new to Node.js development and I am working on an app that requires me to pull users from a mysql database. I am using the promise-mysql library to query a mysql database. I am trying to use a connection pool like this:
var pool = mysql.createPool({
host: hgh.host,
user: hgh.user,
password: hgh.pw,
database: hgh.name,
connectionLimit: 10
});
As a global variable in my module.
I then have the above function to return a connection from the pool.
function connect() {
return pool.getConnection().then(function(connection) {
return connection
}).catch(function(error) {
console.log("Connect failed");
throw ErrorModel.generateErrorObject(error, 500);
});
}
Below is a function I am using to query the database with:
function getUser(username) {
var sql_query = `SELECT * FROM userstable WHERE userName = ` + `'` + username + `'`;
return connect().then(function(conn) {
return conn.query(sql_query).then(function(rows) {
console.log("HGH getUser Then");
console.log(pool);
conn.release();
return rows;
});
}).catch(function(error) {
console.log("HGH getUser Catch");
console.log(error);
throw ErrorModel.generateErrorObject(error, 500);
});
}
I am getting this issue:
conn.release is not a function when trying to release my current connection into the pool. Is my logic wrong here? My goal is to have a pool with a bunch of connections (up to a certain number) and if a user needs to query, the getConnection() function just grabs them either a free connection from the pool, or creates them one. Problem is I cannot for the life of me release it back to the pool as free..Every time I make a request with conn.end() instead of release the connection remains in the _allConnections array of the pool when I console it out, and there are absolutely no connections in the _freeConnections array.
Anyone know how I can make connections free again??

Looking at the module's code I found the function for releasing a connection from a pool:
pool.prototype.releaseConnection = function releaseConnection(connection) {
//Use the underlying connection from the mysql-module here:
return this.pool.releaseConnection(connection.connection);
};
So if all of these functions live in the same file you could do the following in the getUser function:
replace
conn.release();
with
pool.releaseConnection(conn);

Looking at the code, promise-mysql wraps the original connection object, which doesn't expose the release method. However, the original is exposed as a property called connection, so this works:
conn.connection.release();
A few random thoughts:
You should probably escape your query input:
var sql_query = `SELECT * FROM userstable WHERE userName = ${ pool.escape(username) }`;
Your code doesn't release connections when an error occurs (because the code in the .then() callback wouldn't get called); it's better to use .finally() to do the releasing, as that will get called for both resolved and rejected cases:
function connect() {
var conn = null;
return pool.getConnection().then(function(connection) {
conn = connection;
return connection;
}).catch(function(error) {
console.log("Connect failed", error);
}).finally(function() {
if (conn) {
conn.connection.release();
}
});
}

Related

How to switch from using mysql.createConnection to mysql.createPool

I'm fairly new to how database connections work using nodejs, and I'm having issues with database connections that aren't being closed properly. I've asked a few questions on here before about it, and it seems like everyone is telling me to use pool instead of the way I have been doing it. The only problem is that when I search online about using pool from promise-mysql, everyone seems to use a very simple and generic approach, but I'm using it within a complex application using sockets. So I'm wondering how I can switch my old approach using createConnection() to using pool instead, in hopes of clearing up these connection issues.
Each time I call a socket it makes a connection to the database and then releases it after it is complete, or so it seems. It sounds like this is not a very scalable approach, and that using pool will help run multiple queries in parallel.
db.js:
import mysql from 'promise-mysql';
import env from '../../../env.config.json';
const db = async (sql, descriptor, serializedParameters = []) => {
return new Promise( async (resolve, reject) => {
try {
const connection = await mysql.createConnection({
host: env.DB.HOST,
user: env.DB.USER,
password: env.DB.PASSWORD,
database: env.DB.NAME,
port: env.DB.PORT
})
if (connection && env.ENV === "development") {
//console.log(/*"There is a connection to the db for: ", descriptor*/);
}
let result;
if(serializedParameters.length > 0) {
result = await connection.query(sql, serializedParameters)
} else result = await connection.query(sql);
connection.end();
resolve(result);
} catch (e) {
console.log("ERROR pool.db: " + e);
reject(e);
};
});
}
export default db;
This is an example of how I would create a connection to query the db
inventory.js:
import db from '../API/db';
export const selectAllFromBuildItems = () => {
return new Promise( async (resolve, reject) => {
try {
const getAllBuildItems = "SELECT * FROM mydb.build_items;"
const response = await db(getAllBuildItems, "AllBuildItems");
resolve(response);
} catch (e) {
console.log("ERROR inventory.selectAllFromBuildItems: " + e);
reject(e);
}
});
};
How can I change my code so that I use a pool instead. I have a lot of different queries that can be called from our application so I'm not quite sure what the right approach for this would be. I saw some people say that I should create the pool once and then use it throughout the application, but I don't know where that would go. If anyone has any suggestions on how I can make this switch, that would help me out a lot. Thanks!
Create the pool. Better if you create once when you run your application.
If it is in different file then you have export here and import in required file.
var pool = mysql.createPool({
host: env.DB.HOST,
user: env.DB.USER,
password: env.DB.PASSWORD,
database: env.DB.NAME,
connectionLimit: 10
});
I had to create this prototype function as the library had a bug of close connection was not returning the connection to the pool.
pool.prototype.releaseConnection = function releaseConnection(connection) {
return this.pool.releaseConnection(connection.connection);
};
Funtion for getting connection from the pool that is created earlier.
If you want you can call pool.getConnection() in all your query functions.
function connect() {
return pool.getConnection().then(function(connection) {
return connection
}).catch(function(e) {
console.log("Error Creating Connection");
throw e;
});
}
Now this is your query function to get data from dd.
function selectAllFromBuildItems() {
var sql_query = `SELECT * FROM mydb.build_items`;
return connect().then(function(conn) {
return conn.query(sql_query).then(function(rows) {
pool.releaseConnection(conn);
return rows;
});
}).catch(function(e) {
console.log("ERROR inventory.selectAllFromBuildItems: " + e);
throw e;
});
}
Update: Descriptions are added. Hope this helps you.

How to make Alexa run MySQL Querys through a skill

I am trying to set up an alexa skill that calls MySQL Querys when a certain question gets asked. Nothing I tried seemed to work because I either get an error or nothing happens at all.
I am using/what I am working with:
Alexa Developer Console
Cloud9 as IDE(which uploads the code to AWS Lambda, where I defined the environmental variables used in my code)
AWS Lambda, NodeJS
Amazon RDS, which hosts my DB instance
MySQL Workbench (where I created a few tables to test the database, which works fine)
I tried several ways to solve my problem, like creating a connection or a pool, but I think it has to be handled differently, because Alexa has to wait for the response.
const GetOeffnungszeiten_Handler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest' && request.intent.name === 'GetOeffnungszeiten' ;
},
handle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
const responseBuilder = handlerInput.responseBuilder;
let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
let say = 'OUTPUT: ';
var mysql = require('mysql');
var connection = mysql.createPool({
host : process.env.MYSQL_HOSTNAME,
user : process.env.MYSQL_USERNAME,
password : process.env.MYSQL_PASSWORD,
database : process.env.MYSQL_DATABASE,
port : process.env.MYSQL_PORT
});
exports.handler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
pool.getConnection(function(err, connection) {
connection.query('select name from persons where id=1', function (error, results, fields) {
connection.release();
if (error) {
callback(error);
say=say+'0';
} else {
callback(null,results[0].name);
say=say+' 1';
}
});
});
};
return responseBuilder
.speak(say)
.reprompt('try again, ' + say)
.getResponse();
},
};
I expect the output to either be "OUTPUT: 1" or "OUTPUT: 0" but it is "OUTPUT: "
With output I refer to the say variable.
Your function is returning responseBuilder...getResponse() before the SQL connection finishes and callback is called.
I would suggest to refactor your code using async and await to make it easier to read and to understand. (read https://stormacq.com/2019/06/22/async-js.html for help)
Be sure to return the Alexa response only when your call to MySQL returns, and not before. Remember that Alexa timeout is 8 secs, so your code need to return before that. Be sure that the AWS Lambda timeout is aligned to the Alexa timeout too (put it at 7 secs)
Finally, I would advise against using MySQL for Alexa skills. Because each Lambda invocation might be served by different containers, your code will create a connection pool for each interaction between customers and your skill, creating a significant delay to bring a response to customers. DynamoDB and Elastic Cache are much better suited to Alexa skills.

ECONNRESET/ETIMEDOUT error with mysql2 nodejs pooled connection

I have a node/express/mysql2 web app that accesses a mySql DB through a connection pool object and I often run into the following issue: I leave the code for a while then when i come back and access pages that run queries I'll get
Error in foo: Error: connect ETIMEDOUT
Error in bar: Error: read ECONNRESET
I guess that on the other side mysql sees idle connections and close them, the client app doesn't know that, get those connections from the pool and then run into those issues, fine. But I was under the impression that this was automatically handled by mysql2 ?
This is roughly how i organised the db code
sqlConnectionPool.js
const dbParam = require('./dbParam.js');
const sqlPool = require('mysql2/promise').createPool(dbParam.connection.prod);
module.exports = sqlPool;
dummyQuery.js
const sqlPool = require('./sqlConnectionPool.js');
module.exports.updatefoo = async (ID, sqlConnection = undefined) => {
let connection;
try {
connection = sqlConnection === undefined ? await sqlPool.getConnection() : await sqlConnection;
const [updateResult] = await connection.query('update foo set barID=?', [ID]);
if (updateResult.affectedRows !== 1) {
throw (new Error(`error on ID ${ID}`));
}
return undefined;
} catch (err) {
console.error(`Error in updatefoo: ${err}`);
return err;
} finally {
if (sqlConnection === undefined) {
connection.release();
}
}
};
Is there something I'm missing to have those errors automatically handled, or simply not run into them ? I guess the mysql2 library needs to close the connection when they get connreset or conntimeout error and return them to the pool...
Thanks !
I think you should use something like setTimeout() to jab good the DB.

How to return a promise as well as close the mysql connection in the same code in NodeJS

We want our function to return a promise as well as close the database connection. We put the promise in try and connection closing command in finally to which it gives the following errors -
conPool.close() //It says Error - conPool.close() is not a function
conPool.release() // Error - conPool.release() is not a function
conPool.releaseConnection(); // Error - Cannot read property '_pool'
of undefined
Please help me close the mySQL connection. I want to return my data . But after return nothing works so the connection doesn't close. I am afraid i may loose out on maximum limit. I have even set maximum connections in mySQL but still. And there is a possible solution that i may declare conPool outside the function and make all the functions use this single conPool but this is also not working.
code -
function viewChoice() {
var sqlQuery;
sqlQuery = "select * from TRANSACTION_PAYLOAD where INTERFACE_NAME
= 'Vehicle RC' AND (STATUS ='INPUT_ERROR' OR STATUS ='ERROR')";
}
var deferred = Q.defer();
var host = config.host;
var user = config.user;
var password = config.password;
var database = config.database;
var conPool = mysql.createPool({
host: host,
user: user,
password: password,
database: database
});
try{
conPool.getConnection(function (err) {
console.log("Inside getConnection ")
if (err)
deferred.reject(err.name + ': ' + err.message);
conPool.query(sqlQuery,
function (err, result, fields) {
if (err) deferred.reject(err.name + ': ' + err.message);
console.log(result);
deferred.resolve(result);
});
});
return deferred.promise;
}
finally{
console.log("inside Finally")
conPool.releaseConnection();
}
}
From MySQL connector docs :
Closing all the connections in a pool
When you are done using the pool, you have to end all the connections or the Node.js event loop will stay active until the connections are closed by the MySQL server. This is typically done if the pool is used in a script or when trying to gracefully shutdown a server. To end all the connections in the pool, use the end method on the pool:
pool.end(function (err) {
// all connections in the pool have ended
});
The end method takes an optional callback that you can use to know when all the connections are ended.
Once pool.end is called, pool.getConnection and other operations can no longer be performed. Wait until all connections in the pool are released before calling pool.end. If you use the shortcut method pool.query, in place of pool.getConnection → connection.query → connection.release, wait until it completes.
pool.end calls connection.end on every active connection in the pool. This queues a QUIT packet on the connection and sets a flag to prevent pool.getConnection from creating new connections. All commands / queries already in progress will complete, but new commands won't execute.
Plus you may move your return deferred.promise; inside finally block

Pooling keeps incrementing connections and ends up in ER_CON_COUNT_ERROR

I have a class called Connection like one below. This only executes select statements. I have non pooling connection for insert or update.
var _mysql = require('mysql');
function Connection()
{
//private variables and dependencies includes
//create mysql pool connection requires nodejs mysql this connection is used only for selects.
var _connectionSelect = _mysql.createPool({
host : _config.mySQLDB.select.dbHost,
user : _config.mySQLDB.select.dbUser,
password : _config.mySQLDB.select.dbPass,
database : _config.mySQLDB.select.dbName,
supportBigNumbers : true,
connectTimeout : 7000,
connectionLimit : 5,
queueLimit : 5
});
this.executeSelect = function(sql, callback, Message)
{
//connects to mysql.
_connectionSelect.getConnection(function(connectionError, Connection){
if(connectionError)
{
console.log(connectionError);
//throws error if connection or sql gone wrong
Message.add("error", 'serviceDown');
Message.add("devError", 'unknownError');
callback(false);
}
else
{
//executes the query passed
Connection.query(sql, function(error, rows) {
Message.incerementQuery();
if(error)
{
Connection.release();
console.log(error+sql);
//throws error if connection or sql gone wrong
Message.add("error", 'unknownError');
Message.add("devError", "seriousError", "Database errors at resource server side");
callback(false);
}
else
{
Connection.release();
//executes the callback function
callback(rows);
}
});
}
});
};
}
exports.Connection = Connection;
I created an instance of this class whenever I want to execute a query.
I am aware that the default concurrent connections in MySQL is 100 and I wanted to keep that number.
Whenever I try running my application, this connection pooling is incrementing every select and reaches 100 connections pretty soon.
As you can see I am releasing the connection on success or error states. I am pretty sure that I must be doing something wrong, but difficult to figure out.
Is it because how I create instances of this class? I was hoping that if I supply
connectionLimit : 5
even if I create many instances of this class it should only utilise 5 connection?
Note: I have only one instance of this app in my local machine.
Sorry to be so amateur, I am new to this streaming I/O business. I love the idea of pooling but if I cant sort this out, I may need to use traditional open and close connection for every query . Any help would be much appreciated.
Many thanks,
Karthik
Got the answer from Doug Wilson from git hub https://github.com/dougwilson.
I should have instantiated createPool outside of the function. Works like a charm.
The code goes like
var _mysql = require('mysql');
//create mysql pool connection requires nodejs mysql this connection is used only for selects.
var _connectionSelect = _mysql.createPool({
host : _config.mySQLDB.select.dbHost,
user : _config.mySQLDB.select.dbUser,
password : _config.mySQLDB.select.dbPass,
database : _config.mySQLDB.select.dbName,
supportBigNumbers : true,
connectTimeout : 7000,
connectionLimit : 5,
queueLimit : 5
}
function Connection()
{
//private variables and dependencies includes
);
this.executeSelect = function(sql, callback, Message)
{
//connects to mysql.
_connectionSelect.getConnection(function(connectionError, Connection){
if(connectionError)
{
console.log(connectionError);
//throws error if connection or sql gone wrong
Message.add("error", 'serviceDown');
Message.add("devError", 'unknownError');
callback(false);
}
else
{
//executes the query passed
Connection.query(sql, function(error, rows) {
Message.incerementQuery();
if(error)
{
Connection.release();
console.log(error+sql);
//throws error if connection or sql gone wrong
Message.add("error", 'unknownError');
Message.add("devError", "seriousError", "Database errors at resource server side");
callback(false);
}
else
{
Connection.release();
//executes the callback function
callback(rows);
}
});
}
});
};
}
exports.Connection = Connection;
Thanks a lot. Sorry to be so stupid.
Karthik