Nodejs lambda function timing out - mysql

Just trying out Lambda and nodejs.
I have a timeout error that happens every few times.
My nodejs code is below.
var mysql = require('mysql');
var pool = mysql.createPool({
connectionLimit: 100,
host : 'hostname',
user : 'username',
password : 'password',
database : 'database',
port : 3306
});
exports.handler = async (event, context, callback) => {
let request = JSON.parse(event.body);
let sId = request.sid;
let questionnaireId = request.questionnaireId;
pool.getConnection((error, connection) => {
if (error) throw error;
let sql = "INSERT INTO answers (qId, sId,) VALUES(" + questionnaireId + ", " + sid + ")";
connection.query(sql, function(err, result){
if(err) throw err;
console.log("Successfull Insert")
connection.release();
});
});
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify({message : 'success'}),
};
return response;
};
Cloudwatch shows the below message.
"errorMessage": "connect ETIMEDOUT",
"code": "ETIMEDOUT",
Thanks in advance.

Try removing callback argument from your handler. You are returning a promise from your function, so you don't need it.
Defining callback as an argument probably makes aws think you are gonna call it when your function finishes executing, and as you don't it produces a timeout error.

Replace
return response;
with this:
callback(null, response)
Reference: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html

Related

AWS Lambda nodejs mysql query inside loop is not working

module.exports.handler = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
let index = 0;
var client = mysql.createConnection({
host: process.env.rds_host,
user: process.env.rds_username,
password: process.env.rds_password,
port: process.env.rds_port
});
client.connect((err) => {
if (err) throw err;
console.log("Connected!");
});
let array = [];
let queries = ["query1", "query2", "query3", "query4"];
queries.map(q => {
array.push(getQueryResult(client, q));
});
Promise.all(array).then(result => {
console.log(result);
});
callback(null, {});
};
const getQueryResult = async (client, query) => {
return new Promise((resolve, reject) => {
client.query(query, function (err, result) {
if (err) {
reject(err);
}
resolve(result);
});
});
};
Above is my lambda scripts to execute multiple query from mysql. The problem is I didn't get any result and error message from above scripts. Please help me something is missing inside my scripts?
The issue is >> code is not waiting to finish Promise
You can resolve by :
Add callback in then
Promise.all(array).then(result => {
console.log(result);
callback(null, {});
});
OR
Use await
let result = await Promise.all(promiseArray);
console.log(result)
callback(null, {});
Note: Use try-catch to handle error in for await
Also, don't use map to loop array instead use For loop.
There are two or three (potential) issues with your code above:
Your if statement does not get evaluated because of the typeof client predicate does not return true.
your mysql port conflicts with your localhost port (assumption)
Change your if block as such:
// check if `dotenv` has been called if you use it
require('dotenv').config();
// access the state property on mysql object
// if you have closed your connection previously
// this will get evaluated by the parser
if (mysql.state === "disconnected") {
var client = mysql.createConnection({
host: process.env.rds_host,
user: process.env.rds_username,
password: process.env.rds_password,
port: process.env.rds_port // is your port the same as your localhost?
// include your database name here
});
// I suggest you to await your client connection
// since mysql executes sequentially, not asynchronously
client.connect(function(err) => {
if (err) {
console.error('error connecting: ' + err.stack);
return;
}
console.log("Connected!");
});
}
if the error persists, it means that your enviroment variables are not set correctly so your db configuration should be reviewed (see inline comments).

Node.js, Mysql TypeError: Cannot read property 'apikey' of undefined

I am working on a basic auth middleware for a API it uses Node.js Mysql but if someone puts a incorrect key in auth header and sends the request the entire API crashes heres my code the issue is with the callback but I don't know how to fix that.
var express = require('express');
var app = express();
app.get('/', (request, response) => {
response.sendStatus(200);
});
let listener = app.listen(3000, () => {
console.log('Your app is currently listening on port: ' + listener.address().port);
});
var mysql = require('mysql');
var connection = mysql.createConnection({
host : '127.0.0.1',
user : 'root',
database : 'systemdata'
});
connection.connect();
function systemAuth(apikey, callback)
{
connection.query('SELECT apikey FROM systemdata.systemkeys WHERE apikey = ?', [apikey], function(err, result)
{
if (err)
callback(err,null);
else
callback(null,result[0].apikey);
});
}
var auth = function (req, res, next) {
systemAuth(req.headers.apikey, function(err,data){
if (err) {
console.log("ERROR : ",err);
} else {
console.log("result from db is : ",data);
}
if(data == req.headers.apikey) {
next()
}else{
res.status(401).send({"error": "Missing or Invalid API-Key", "apikey": req.headers.apikey, "valid": "false"})
}
})
}
app.use(auth)
You will also have to check whether your result actually contains any rows.
A query not returning any rows is not an error, so err won't be set, if result is an empty array. And accessing an element by an index which does not exist leads to undefined, thus the error you are seeing.
function systemAuth(apikey, callback)
{
connection.query('SELECT apikey FROM systemdata.systemkeys WHERE apikey = ?', [apikey], function(err, result)
{
if (err) // some error with the query
callback(err,null);
else if (!result || result.length == 0) // no matching rows found
callback(new Error("invalid apikey"), null);
else // a matching row is found
callback(null,result[0].apikey);
});
}

Nodejs mysql how to handle timeout and Handshake inactivity timeout

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.

AWS Lambda/API Gateway - Pass an ID to delete a row in mysql with node.js

I wrote a simple restful API in Node.js for different HTTP-Requests which i then tried to realize with Lambda functions. My problem with the DELETE statement in Lambda is that i dont know how to pass an ID through the API gateway to delete a certain row in the mysql table.
With node.js, I just defined it through the route of the URL (ex. /contacts/:id) and then accessed it with .params.id.
How would you pass an a value (the ID) for the same route (/contacts) to then use it in the handler below to delete a the row with that specific ID?
The Code i posted below works fine when u invoke it locally with --data, for example:
serverless invoke local --function delete --data "2"
the same code works too if i deploy the lambda if i pass the testdata
{
"id": "1"
}
and use event.id instead of event.
I realize that this is not the way you do it, but i ve finally run out of ideas and decided to post my issue here. :-)
So, how do i pass and ID with my DELETE HTTP-Request to /contacts through the api gateway and use it in the mysql query?
module.exports.handler = (event, context, callback) =>
{
const mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: ''
});
// Use the connection
connection.query('DELETE FROM sqllambdadb.contacts WHERE id=?', event, function (err, res) {
if (err) throw err;
else if (res.affectedRows>0) callback(err, 'EMail with ID: '+ event+ ' deleted!');
else callback(err, 'No row with ID: '+ event+ ' found!');
});
connection.end();
};
API Gateway maps requests to the input event object
{
"resource": "Resource path",
"path": "Path parameter",
"httpMethod": "Incoming request's method name"
"headers": {Incoming request headers}
"queryStringParameters": {query string parameters }
"pathParameters": {path parameters}
"stageVariables": {Applicable stage variables}
"requestContext": {Request context, including authorizer-returned key-value pairs}
"body": "A JSON string of the request payload."
"isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode"
}
In your example, depending on the segement's alias your using, you'd access the id by either destructuring the appropriate event object, or referencing directly via event.pathParameters.id
const mysql = require('mysql');
module.exports.handler = (event, context, callback) => {
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: ''
});
//Destructing id from event.pathParameters object
var {id} = event.pathParameters
// Use the connection
connection.query('DELETE FROM sqllambdadb.contacts WHERE id=?', id, function (err, res) {
if (err) throw err;
else if (res.affectedRows>0) callback(err, 'EMail with ID: '+ id + ' deleted!');
else callback(err, 'No row with ID: '+ id + ' found!');
});
connection.end();
};
So, i set up the yaml to add a parameter value to my URL:
delete:
handler: src/delete.handler
package:
include:
- node_modules/**
- src/delete.js
- serverlessAPI.iml
events:
- http:
path: contact-management/contacts/{id}
method: delete
cors: true
integration: LAMBDA
request:
parameters:
paths:
id: true
with this config, the id will be saved directly in the event object under event.id so you can simply access it like this:
module.exports.handler = (event, context, callback) =>{
const mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'testt123'
});
var id = event.id;
// Use the connection
connection.query('DELETE FROM sqllambdadb.Contacts WHERE id=?', id, function (err, res) {
if (err) throw err;
else if (res.affectedRows>0) callback(err, 'EMail with ID: '+ id + ' deleted!');
else callback(err, 'No row with ID: '+ id + ' found!');
});
connection.end();
};
you can test it by creating a testcase with the following input:
{
"id": "$input.params('id')"
}

Simple server handling routes and giving error nodejs mysql

I'm trying to write a simple server using nodejs and have the server ship back different queries and/or custom headers/responses based on the routes. However, in the getUsers() function the error keeps getting hit and printing the 'Error querying' to the console instead of printing the email rows. I know the server is connected fine, because I can return a query when I just use the db and return a query with createConnection only using the second example. Any help spotting the error is greatly appreciated. Thanks.
What I'm trying to get done:
var http = require('http');
var mysql = require('mysql');
var url = require('url');
var util = require('util');
var db = mysql.createConnection({
host : "*********",
user : "*********",
password : "*********",
port : '****',
database : '*********'
});
db.connect(function(err) {
console.log('connected');
if (err)
console.error('Error connecting to db' + err.stack);
});
function getUsers() {
db.query('SELECT * FROM users', function(err, rows, fields) {
if (err)
// changed console.error('Error querying');
console.error(err);
if (rows)
console.log('Rows not null');
for (var i in rows) {
console.log(rows[i].email)
}
});
}
var server = http.createServer(function(req, res) {
console.log(req.url);
if (req.url == '/signup') {
console.log("User signing up");
} else if (req.url == '/signin') {
console.log("User signing in");
} else if (req.url == '/new') {
console.log("User request new game");
getUsers();
}
//res.writeHead(200);
//res.end('Hello Http');
});
server.listen(3000);
// changed and commented out db.end();
What does work with querying the db:
var connection = mysql.createConnection({
host : "********",
user : "********",
password : "********",
port : '****',
database : '********'
});
connection.connect();
var queryString = 'SELECT * FROM users';
connection.query(queryString, function(err, rows, fields) {
if (err) throw err;
for (var i in rows) {
console.log('Users: ', rows[i].email);
}
});
connection.end();
The code has been updated with the changes, and the problem was I was closing the database. After changing the error logs as was suggested in the comments, this was the error received.
{ [Error: Cannot enqueue Query after invoking quit.] code: 'PROTOCOL_ENQUEUE_AFTER_QUIT', fatal: false }
I then commented out the
db.end()
and the queries were returned fine.
Thanks for the help.