AWS Lambda stops execution in the middle of the code - mysql

I am trying to trigger csv file upload in s3 and insert the data from the file to database using lambda.
Most of the times code executes successfully if i run the code back to back in couple of seconds gap.
But sometimes the problem i face is the code stops execution at console console.log('about to get the data'); and ignore rest of the code and sometimes mysql connection gets time out.
I can find that the problem occurs only when i test the lambda code with more than 20 seconds of gap. So, i guess this is a cold start problem.
I don't want to miss even a single s3 trigger. So, i need help to find flaw in my code that is causing this problem.
const AWS = require('aws-sdk');
const s3 = new AWS.S3({region: 'ap-south-1', apiVersion: '2006-03-01'});
var mysql= require('mysql');
var conn = mysql.createPool({
connectionLimit: 50,
host: 'HOST',
user: 'USER',
password: 'PASSWORD',
database: 'DATABASE'
})
async function mainfunc (event, context, callback) {
console.log("Incoming Event: ", JSON.stringify(event));
const bucket = event.Records[0].s3.bucket.name;
const filename = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
const params = {
Bucket: bucket,
Key: filename
};
console.log('about to get the data'); //Code stops here some times
return await getresult(params);
};
async function getresult(params){
var result = await s3.getObject(params).promise();
var recordList = result.Body.toString('utf8').split(/\r?\n/).filter(element=>{
return element.length> 5;
})
recordList.shift()
var jsonValues = [];
var jsonKeys = result.Body.toString('utf8').split(/\r?\n/)[0]
recordList.forEach((element) => {
element = element.replace(/"{2,}/g,'"').replace(/, /g,"--").replace(/"{/, "{").replace(/}"/, "}").replace(/,/g, ';').replace(/--/g,', ').split(';');
jsonValues.push(element)
});
var lresult = await query(jsonKeys, jsonValues);
return lresult;
}
async function query(jsonKeys, jsonValues){
var qresult = await conn.getConnection(function(err, connection) {
if (err){
console.log(err,'------------------------------------');// Sometimes i get Sql Connection timeout error here
} else {
console.log("Connected!");
var sql = "INSERT INTO reports ("+jsonKeys+") VALUES ?";
connection.query(sql, [jsonValues], function (err, result) {
if (err){
console.log(err);
connection.release()
return err;
} else {
console.log("1 record inserted");
console.log(result);
connection.release()
return result;
}
});
}
})
}
exports.handler = mainfunc

I have solved the issue by using promise in the "query" function
function query(jsonKeys, jsonValues){
return new Promise(function(resolve, reject) {
conn.getConnection(function (err, connection) {
if (err) {
console.log(err, '------------------------------------');
}
else {
console.log("Connected!");
var sql = "INSERT INTO principal_reports (" + jsonKeys + ") VALUES ?";
connection.query(sql, [jsonValues], function (err, result) {
if (err) {
console.log(err);
connection.release();
reject(err)
}
else {
console.log("1 record inserted");
console.log(result);
connection.release();
resolve(result)
}
});
}
})
})
}
and changed the code
var lresult = await query(jsonKeys, jsonValues);
to
var lresult = await query(jsonKeys, jsonValues).then(data =>{
return data;
}).catch(error =>{
return error;
});

Related

ER_BAD_NULL_ERROR when inserting value into column with nodejs + mysql

I am trying to insert a value using postman to test my api. My class table have 2 columns (classId which is auto incremented and classes). However, I kept getting this error message and I am unsure of how to solve this.
This is the postman result.
This is my database table class.
Here is my code.
const db = require('../config/databaseConfig');
const adminDB = {};
adminDB.createClass = (classes, callback) => {
var dbConn = db.getConnection();
dbConn.connect(function (err) {
if (err) {
return callback(err, null);
}
const query = "INSERT INTO practiceme.class (classes) VALUES (?)";
dbConn.query(query, [classes], (err, results) => {
dbConn.end();
if (err) {
console.log(err);
return callback(err, null);
} else {
return callback(null, results);
}
});
});
};
module.exports = adminDB;
const express = require("express");
const router = express.Router();
const adminDB = require("../model/admin");
router.post("/createClass", (req, res, next) => {
var {classes} = req.body;
adminDB.createClass(classes,(err, results) => {
if (err) {
return res.status(500).send({ err });
}
return res.status(200).json(results);
}
);
}
);
module.exports = router;
You're sending the classes variable as a query parameter. To access it from req, you should use req.query instead of req.body.
Change from:
var {classes} = req.body;
to:
var {classes} = req.query;
Or, in Postman, you select the Body tab and then type the body of the request in JSON format. Then your actual code should work.

Multiple Transactions in mysql for Node

I'm using node's driver for mysql and need to execute 'n' number of transactions one after the other and not simultaneously.
I've tried using a for/forEach loop but the transactions seem to happen concurrently and that causes my api to crash.Here's the error :-
throw err; // Rethrow non-MySQL errors
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
One single transactions seems to work just fine.
Each transaction has 4 queries, req.body is an array of objects:-
router.post('/production/add', (req, res) => {
for (const obj of req.body) {
pool.getConnection(function (err, connection) {
connection.beginTransaction(function (err) {
if (err) throw err;
const query1 = `select qty from production where prc_id = ${obj.prc_id}`;
console.log(query1);
connection.query(query1, function (error, result1, fields) {
if (error) {
return connection.rollback(function () {
res.status(400).send({ query: 1, message: error.sqlMessage, code: error.code, errno: error.errno });
return;
});
}
const new_prod_qty = result1[0].qty - obj.auth_prod_qty;
const query2 = new_prod_qty > 0 ? `update production set qty = ${new_prod_qty} where prc_id = ${obj.prc_id}` : `delete from production where prc_id = ${obj.prc_id}`;
console.log(query2);
connection.query(query2, function (error, results2, fields) {
if (error) {
return connection.rollback(function () {
res.status(400).send({ message: error.sqlMessage, code: error.code, errno: error.errno });
return;
});
}
const query3 = `update prc set auth_prod_qty = ${obj.auth_prod_qty} where prc_id = ${obj.prc_id}`;
console.log(query3);
connection.query(query3, function (error, results3, fields) {
if (error) {
return connection.rollback(function () {
res.status(400).send({ message: error.sqlMessage, code: error.code, errno: error.errno });
return;
});
}
const query4 = "select * from store";
connection.query(query4, function (error, results3, fields) {
if (error) {
return connection.rollback(function () {
res.status(400).send({ message: error.sqlMessage, code: error.code, errno: error.errno });
return;
});
}
connection.commit(function (err) {
if (err) {
return connection.rollback(function () {
res.status(400).send({ message: error.sqlMessage, code: error.code, errno: error.errno });
return;
});
}
res.status(201).send(results2);
});
});
});
});
});
});
});
};
});
Based off some research Sequelize ORM seems to promisify transactions but however I'm hoping to use it as a last resort. Any sort of solution with or without Sequelize would be appreciated!
Thanks in advance!
You need to use async / await to run your txs sequentially. How to do this?
Use npm mysql2 in place of npm mysql. That gets you promisified (awaitable) versions of the APIs when you require('mysql2/promise'). Plus, this is much more fun to program and debug than those miserable nested callbacks. Just don't forget the awaits.
Use this basic outline for your code's data processing loop. Everything will go in order sequentially. The way you create your pool is a little different; read the npm page. This is not debugged.
const mysql = require('mysql2/promise');
router.post('/production/add', async (req, res) => {
const connection = await pool.getConnection()
for (const obj of req.body) {
try {
await connection.beginTransaction()
const query1 = 'whatever'
const result1 = await connection.query(query1)
const query2 = 'something else'
const result 2 = await connection.query(query2)
/* etcetera etcetera */
await connection.commit()
}
catch (error) {
await connection.rollback()
pool.releaseConnection()
res.status(400).send({ something })
}
}
pool.releaseConnection()
}
mysql2/promise is exactly the package I was looking for, works with mysql and uses promise() method to upgrade mysql connection to a promise based mysql2 connection.
router.post('/stock/add', async (req, res) => {
const connection = pool.getConnection(async function (err, connection) {
if (err) {
connection.release();
res.status(400).send(err);
return;
}
else {
for (const obj of req.body) {
try {
await connection.promise().beginTransaction();
const [result1, fields1] = await connection.promise().query(query1)
const [result2, fields2] = await connection.promise().query(query2);
const [result3, fields3] = await connection.promise().query(query3);
const [result4, fields4] = await connection.promise().query(query4);
await connection.promise().commit();
}
catch (error) {
await connection.promise().rollback();
connection.release();
res.status(400).send(error);
return;
}
}
res.status(200).send('Transaction Complete');
}
});
});

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

How to return result of db from a nested functions in nodejs

I want the result on other file but not getting how to return it
function getUsers(){
console.log("Fetching all user data");
const connection = getConnection();
const sql = "SELECT * FROM users";
var result = connection.query(sql,(err, rows, fields) =>{
if(err){
console.log("Failed to get users data");
res.sendStatus(500);
throw err;
}
console.log("Fetched Users Successfully");
return rows;
})
return result;
}
This is a standard asynchronous function problem.
return rows will not assign rows to the result variable. Second argument of query() function is a callback function and it indicates a block of code that will be executed after rows are fetched. It means that result of the query is only visible inside callback block.
You can fix it in 2 ways:
Write all your code that uses SQL result inside callback function block (You can call another function at that point and pass result as argument for example).
This method is simpler if you are new to javascript programming. I see that you want to use function getUsers() in another place. If you choose this approach, best way would probably be to pass a callback to the getUsers() function and then invoke it in the callback of the query() method.
Example:
getUsers(callback){
const connection = getConnection();
const sql = "SELECT * FROM users";
connection.query(sql,(err, rows, fields) => callback(rows))
}
mainFunction(req,res){
getUsers(doSomethingWithUsers)
}
doSomethingWithUsers(users){
...
}
This is of course, oversimplified pattern, but the idea should be clear.
Research about promises and how they handle async functions. This will require you to "wrap" you function body into Promise object type.
They will allow you to write something like this:
mainFunction(){
getUsers().then(result => ....);
// or even
const users = await getUsers();
}
EDIT:
To wrap "return value of promise", or as we say "to resolve a promise", you can wrap function the following way:
function getUsers(){
console.log("Fetching all user data");
const connection = getConnection();
const sql = "SELECT * FROM users";
return new Promise( (resolve,reject) => {
var result = connection.query(sql,(err, rows, fields) =>{
if(err){
// this will cause promise to "fail"
reject(err);
}
console.log("Fetched Users Successfully");
// this will tell javascript that promise is finished
// and rows are accesable in then()
resolve(rows);
})
})
}
function mainFunction(){
const result = getUsers()
.then(rows =>{/** this will happen if resolve(rows) is called */})
.catch(error => {/** this will be executed if reject(err) happens
*/});
}
// or... (Try/Catch is required since "await" silently fails
async function asyncMainFunction(){
try{
const rows = await getUsers();
// You can use rows from resolve() here;
}catch(error){
// here, reject is called
}
}
Here is a link where you can read more about promises:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
You are not getting the result because the code is asynchronous. So, you have to use async/await to get the result. Your code should look like this:-
async function getUsers(){
console.log("Fetching all user data");
const connection = await getConnection();
const sql = "SELECT * FROM users";
try {
var result = await connection.query(sql);
console.log("Fetched Users Successfully");
return result;
} catch (err) {
console.log("Failed to get users data");
res.sendStatus(500);
throw err;
}
}
Finally, call this function like this:-
var output = await getUsers();
But make sure that you call this line in an async function.
Hope this helps!
create file for function : getUsers.js
module.exports = async getUsers(){
const connection = getConnection();
const sql = "SELECT * FROM users";
var myPromise = () => {
return new Promise((resolve, reject) => {
connection.query(sql, query.params, function (error, results, fields) {
error ? reject(error) : resolve(results);
})
});
}
var result = await (myPromise());
return result;
}
require this file and call function :
var getUsers = require('..path to getUsers');
var data = getUsers().then((data)=>{
}).catch((error)=>{
console.log('error',error)
return error;
});
using callback :
or using callback function :
module.exports = function getUsers(callback){
const connection = getConnection();
const sql = "SELECT * FROM users";
connection.query(sql, query.params, function (error, results, fields) {
if(error){ callback(error,null)}
if(!error && results){
callback(null,results)
}
});
}
require file :
var getUsers = require('..path to getUsers');
call it this way :
getUsers(err,data)=>{
if(err){
return res.send(err);
}
if(!err && data){
return res.send(data);
}
}
function getUsers(callback) {
console.log("Fetching all user data");
const connection = getConnection();
const sql = "SELECT * FROM users";
connection.query(sql, (err, rows, fields) => {
if (err) {
console.log("Failed to get users data");
res.sendStatus(500);
callback(err, null);
}
console.log("Fetched Users Successfully");
callback(null, rows)
})
}
// USE ABOVE FUNCTION AS BELOW...
getUsers(function(err, rows) {
if(err){
console.error(err);
}
console.log(rows);
})

Node.js crashes after connecting to mysql database

Basically, node.js will run the function perfectly, and it will continue running for ~10 seconds and then it will simply crash. Below is the full code, because I dont know what part is causing the actual error, but I hope someone will see where i'm messing up.
const Discord = require("discord.js");
const client = new Discord.Client();
const config = require("./config.json");
var fs = require('fs');
var mysql = require('mysql');
const path = require('path');
client.on("ready", () => {
console.log('Running...');
});
var con = mysql.createConnection({
host: "",
user: "",
password: "",
database: "",
});
client.on("message", async message => {
if (message.author.bot) return;
if (!message.guild) {
message.reply('Please do not DM the bot.');
return;
};
if (message.content.toLowerCase() == "!script") {
if (message.channel.name != "bot-chat") {
message.author.send('Please only use bot commands in the #bot-chat channel.');
return;
};
con.connect(function(err) {
if (err) throw err;
con.query("SELECT * FROM Users", function (err, result, fields){
if (err) throw err;
result.forEach(function(User){
var UserDiscord = User.DISCORD;
var UserKey = User.SKEY;
if (usr_dc == message.author.username+"#"+message.author.discriminator) {
message.author.sendCode("php",usr_key)
.then()
.catch(console.log);
if (message.guild.name == "Towlie Boi") {
message.author.sendCode("lua",`_G.key='KeyHere'
loadstring(game:HttpGet("https://basehosting.xyz/sCrIpT1"))()`)
.then()
.catch(console.log);
message.channel.send('Script has been sent to your DMs, If you did not receive a DM then you have not allowed the bot to send you DMs');
.then()
.catch(console.log);
};
};
});
});
});
} else if (message.channel.name == 'whitelist') {
if (message.content.substring(0,3).toLowerCase() == '!wl') {
con.connect(function(err) {
if (err) throw err;
con.query("SELECT * FROM Keys", function(err, result, fields) {
if (err) throw err;
result.forEach(function(Key) {
var key = Key.Code;
if (message.content.substring(4) == key) {
var query = "DELETE FROM `Keys` WHERE `Keys`.`Code` = \'"+message.content.substring(4)+"\'";
con.query(query, function(err) {
if (err) throw err;
con.query("INSERT INTO `Users` (`DISCORD`, `SKEY`, `IP`) VALUES ('"+message.author.username+"#"+message.author.discriminator+"', '"+message.content.substring(4)+"', '');", function(err) {
if (err) throw err;
message.reply('Whitelisted Succesfully');
return;
});
});
};
});
});
});
};
};
};
client.login(config.token);
I cant really see a error, as i've just begun coding for node.js and dont know much about javascript, I thought I maybe had to close the connection after I queued the data, but that didnt seem to work either. Thanks for any answers :)
How about connecting database connection from a separate file.
module.exports = {
host : 'localhost',
user : 'root',
password : '',
database : 'bookstore',
charset : 'utf8'
}
and passing on controlling.
var database = require('../models/database.js')();