Amazon RDS load balancing not working with mysql.createPool in nodejs - mysql

I have implemented load balancing in read database connection like when read db load increased to 60% it will initiate a new read database for balancing load on database but
When I see from AWS developer console dashboard all API calls It will initate new read database instance but most of the API's calls load took placed on database 1 upto 90 percent but like 10 req /sec and on read DB instance 2 1 to 5% database is used like 1req /sec
it should divided API request on both database equaly but It wont work
This issue is because mysql.createPool will not close connection from database 1 (createPool will reuse its opened connections) so that other API calls can move to second database instance.
To solve this problem I had changed mysql.createPool with mysql.createConnection on Each API calls
I had created 2 middleware
1-for createConnection
2-for connection.end()
whenever a request comes in middleware 1 calls and create new connection and on request finish middleware 2 will call which will end the connection. this solution has solved my problem of load balancing but a new issue takes place I have face to many database connection issues with this method
does anyone have a proper solution who has faced this issue or can help?
Sample Code :
var readDB = mysql.createConnection({
database: process.env.READ_DB_DATABASE,
host: process.env.READ_DB_HOST,
user: process.env.READ_DB_DB_USER,
password: process.env.READ_DB_DB_PASSWORD,
charset: "utf8mb4"
});
utils.js
async onFinish(req, res, next) {
return new Promise(async (resolve, reject) => {
try {
let readDB = req.readDB;
const dbEnd = util.promisify(readDB.end).bind(readDB);
const response = await dbEnd();
resolve(response);
} catch (error) {
reject(error);
}
});
}
app.js
/**
* middleware for create connection and end connection on finish
*/
app.use(async (req, res, next) => {
try {
const readDB = await utils.readDBCreateConnection();
req.readDB = readDB;
res.on("finish", function () {
console.log("onFinish called");
utils.onFinish(req, res, next);
});
next();
} catch (error) {
res.status(error.status || 500).send({
code: 500,
message: error.message || `Internal Server Error`,
});
}
});
/**
* Code to initialice routing
*/
require("./modules/v2-routes")(app); // v2 app routes

Related

RDS MySQL timing out intermittently when called from Lambda using NodeJS

My web app uses Lambda using NodeJS and backend is RDS(MySQL). I'm using serverless-mysql to make db calls.
For some reason, the db call times out intermittently. I tried the following:
Enabled flow logs to see if there are any errors (but couldn't find any reject statuses).
Tried making the database publicly available and took lambda out of VPC (to see if it is an issue with VPC configuration). But still, it was failing intermittently. So VPC is out of the equation.
RDS is not having any unusual spikes and connection exhaustion as monitoring shows a peak of only up to 3 connections. Lambda is always kept warm. I tried increasing the time out to up to 25 seconds. Still no luck.
Below is the code I use:
export async function get(event, context, callback) {
if (await warmer(event)) return 'warmed';
context.callbackWaitsForEmptyEventLoop = false;
try {
const userId = getUserIdFromIdentityId(event);
const query = "select * from UserProfile where UserId = ?";
const result = await mysql.query(query, [userId]);
console.log(result);
console.log('getting user account');
mysql.quit();
return success({
profileSettings: result.length > 0 ? result[0] : null,
});
} catch(e) {
console.log(e);
return failure();
}
}
Success function basically returns a json object like below:
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true
},
body: JSON.stringify(body)
};
mysql is initialized as below:
export const mysql = AWSXray.captureMySQL(require('serverless-mysql')({
config: {
host: process.env.dbHost,
user: process.env.dbUsername,
password: process.env.dbPassword,
database: process.env.database,
}
}));
The only error I can see in Cloudwatch logs is:
Task timed out after 10.01 seconds.

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.

How do you open/close mysql connection multiple times?

I'm using node with mysql and I have a route that does:
const mysql = require("./mysql");
router.post("/register_user", (req, res) => {
mysql.register(req.body).then((result) => {
// stuff
});
});
mysql.js:
const mysql = require("mysql");
const connection = mysql.createConnection("mysql://...");
exports.register = (req) => {
const user = { name: req.name };
return new Promise((resolve, reject) => {
// make sure user doesn't exist already
connection.query('...', [user], (err, data) => {
...
if (isNewUser) {
connection.query('INSERT INTO USER...', user, (insertErr, rows) => {
...
resolve(rows);
connection.end();
}
}
});
});
}
This works perfectly when I register the first user in my app. But immediately after, if I log out (on the web app), then register a new user, I get an error saying:
Error: Cannot enqueue Query after invoking quit.
Why doesn't this create a new connection?
I assume you are using the following NPM module mysql
If it is the case then could you simply use MySQL pooling connections ?
Rather than creating and managing connections one-by-one, this module also provides built-in connection pooling using mysql.createPool(config).
So instead of calling connection.end(); you would be calling connection.release(); instead to return connection to the pool of open connections.

How to debug an Azure Function using Node.js and mysql2 connecting to database

running into some issues trying to figure out an Azure Function (node.js-based) can connect to our mysql database (also hosted on Azure). We're using mysql2 and following tutorials pretty much exactly (https://learn.microsoft.com/en-us/azure/mysql/connect-nodejs, and similar) Here's the meat of the call:
const mysql = require('mysql2');
const fs = require('fs');
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
if (req.query.fname || (req.body && req.body.fname)) {
context.log('start');
var config = {
host:process.env['mysql_host'],
user: process.env['mysql_user'],
password: process.env['mysql_password'],
port:3306,
database:'database_name',
ssl:{
ca : fs.readFileSync(__dirname + '\\certs\\cacert.pem')
},
connectTimeout:5000
};
const conn = mysql.createConnection(config);
/*context.log(conn);*/
conn.connect(function (err) {
context.log('here');
if (err) {
context.error('error connecting: ' + err.stack);
context.log("shit is broke");
throw err;
}
console.log("Connection established.");
});
context.log('mid');
conn.query('SELECT 1+1',function(error,results,fields) {
context.log('here');
context.log(error);
context.log(results);
context.log(fields);
});
Basically, running into an issue where the conn.connect(function(err)... doesn't return anything - no error message, no logs, etc. conn.query works similarly.
Everything seems set up properly, but I don't even know where to look next to resolve the issue. Has anyone come across this before or have advice on how to handle?
Thanks!!
Ben
I believe the link that Baskar shared covers debugging your function locally
As for your function, you can make some changes to improve performance.
Create the connection to the DB outside the function code otherwise it will create a new instance and connect every time. Also, you can enable pooling to reuse connections and not cross the 300 limit that the sandbox in which Azure Functions run has.
Use the Promises along with async/await
You basically can update your code to something like this
const mysql = require('mysql2/promise');
const fs = require('fs');
var config = {
host: process.env['mysql_host'],
user: process.env['mysql_user'],
password: process.env['mysql_password'],
port: 3306,
database: 'database_name',
ssl: {
ca: fs.readFileSync(__dirname + '\\certs\\cacert.pem')
},
connectTimeout: 5000,
connectionLimit: 250,
queueLimit: 0
};
const pool = mysql.createPool(config);
module.exports = async function(context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
if (req.query.fname || (req.body && req.body.fname)) {
context.log('start');
const conn = await pool.getConnection();
context.log('mid');
await conn.query('SELECT 1+1', function(error, results, fields) {
context.log('here');
context.log(error);
context.log(results);
context.log(fields);
});
conn.release();
}
};
PS: I haven't test this code as such but I believe something like this should work
Debugging on serverless is challenging for obvious reasons. You can try one of the hacky solutions to debug locally (like Serverless Framework), but that won't necessarily help you if your issue is to do with a connection to a DB. You might see different behaviour locally.
Another option is to see if you can step debug using Rookout, which should let you catch the full stack at different points in the code execution and give you a good sense of what's failing and why.

node js: check mysql connection before a query

I use node js with mysql and want to avoid that the app crash on connection errors.At the moment i use this :
function mysql_handleDisconnect() {
mysql_connection = mysql.createConnection(mysql_config_obj); // Recreate the connection, since
// the old one cannot be reused.
mysql_connection.connect(function(err) { // The server is either down
if(err) { // or restarting (takes a while sometimes).
console.log('error when connecting to db:', err);
mysql_handleDisconnect(); // We introduce a delay before attempting to reconnect,
} // to avoid a hot loop, and to allow our node script to
}); // process asynchronous requests in the meantime.
// If you're also serving http, display a 503 error.
mysql_connection.on('error', function(err) {
console.log('db error', err);
if(err.code === 'PROTOCOL_CONNECTION_LOST') { // Connection to the MySQL server is usually
mysql_handleDisconnect(); // lost due to either server restart, or a
} else { // connnection idle timeout (the wait_timeout
throw err; // server variable configures this)
}
});
}
mysql_handleDisconnect(mysql_connection);
so this is blocking because it leads to a hot loop if the connection is closed.my problem is, if i add a setTimeout to reestablish connection just every 2 seconds i could get an fatal error when i do a query with "mysql_connection.query('SELECT ...')".in this case the app crashes.
So my question is,if there's a possibility to check the connection before i do a query?
Try using below code in every microservice before doing anything:
if(connection.state === 'disconnected'){
return respond(null, { status: 'fail', message: 'server down'});
}
State of connection to DB could fall in 2 states:
disconnected (when due to DB server down or wrong config use for DB connection is wrong)
authenticated (when DB connection is successfully created to DB server).
So either check state == 'disconnected' or state == 'authenticated'
I know this is an old question but I have found connection.ping( (err) => {...}) to be very useful for health-checks made from load balancers and whatnot.
Every time, while I'm pushing my code in production, the mysql connection is lost. It is a very common problem in production, or local.
My solution is that At every query established the db connection and remove connection after completing the db query.
My solution is to establish the db connection before every query, and then remove the connection after completing the db query.
Step1: Here is the code for dbConnection.js
//this code is for conenct to db
const mysql = require('mysql2');
require('dotenv').config();
module.exports.stablishedConnection = ()=>{
return new Promise((resolve,reject)=>{
const con = mysql.createConnection( {
host: process.env.DB_HOST||localhost,
user: process.env.DB_USER_NAME||myUserName ,
password: process.env.DB_PASSWORD||mypassword,
database: process.env.DB_NAME||mydb
});
con.connect((err) => {
if(err){
reject(err);
}
resolve(con);
});
})
}
module.exports.closeDbConnection =(con)=> {
con.destroy();
}
Step2: For Router.js I am import the db connection and handle the promise
const router = require('express').Router();
const {stablishedConnection,closeDbConnection} =require('../db/dbConnection');
router.get('/user/:sId/:userId',function(req,res){
stablishedConnection()
.then((db)=>{
console.log("Db connection stablished");
db.query(`select * from user WHERE sent_id=${req.params.sId} AND user_id=${req.params.userId}`, null, function (err,data) {
if (!data) {
res.status(200).json({sucess:false,err});
}else{
res.status(200).json({sucess:true,data});
closeDbConnection(db);
console.log("Db Connection close Successfully");
}
})
}).catch((error)=>{
console.log("Db not connected successfully",error);
});
});
router.get('/sen/:userId',function(req,res){
stablishedConnection()
.then((db)=>{
console.log("Db connection stablished");
db.query(`select * from sen WHERE user_id=${req.params.userId}`, null, function (err,data) {
if (!data) {
res.status(200).json({sucess:false,err});
}else{
res.status(200).json({sucess:true,data});
closeDbConnection(db);
console.log("Db Connection close Successfully");
}
})
}).catch((error)=>{
console.log("Db not connected successfully",error);
});
});
router.get('/language',(req,res)=>{
stablishedConnection()
.then((db)=>{
console.log("Db connection stablished");
db.query("select * from language", null, function (err,data) {
if (!data) {
res.status(200).json({sucess:false,err});
}else{
res.status(200).json({sucess:true,data});
closeDbConnection(db);
console.log("Db Connection close Successfully")
}
})
}).catch((error)=>{
console.log("Db not connected successfully",error);
});
})
module.exports = router;
This is perfectly run If you want to create and close connection at every query ..
I solved this problem like this:
let connection = mysql.createConnection(DB_CONFIG);
function runDBQuery() {
const disconnected = await new Promise(resolve => {
connection.ping(err => {
resolve(err);
});
});
if (disconnected) {
connection = mysql.createConnection(DB_CONFIG);
}
... use actual connection
}