I\'m trying to write a Lambda function using Node.js which connects to my RDS database. The database is working and accessible from my Elastic Beanstalk environment. When I
I am sharing my experience while connecting RDS.
You need to enable
VPC
access for theLambda function
, during which you will assign it a Security Group.
Then, within the Security Group assigned to the RDS instance, you will enable access for the Security Group assigned to the Lambda function.
You can get more info here
While using context will work, you just need to add context.callbackWaitsForEmptyEventLoop = false;
to the handler and then use callback as normal like this:
exports.handler = (event, context) => {
context.callbackWaitsForEmptyEventLoop = false;
var connection = mysql.createConnection({
//connection info
});
connection.connect(function(err) {
if (err) callback(err);
else callback(null, 'Success');
});
};
The answer is here in the docs (took me a few hours to find this): http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-using-old-runtime.html
In the section "Comparing the Context and Callback Methods" it has an "Important" note that explains things.
At the bottom of the note it reads:
Therefore, if you want the same behavior as the context methods, you must set the context object property, callbackWaitsForEmptyEventLoop, to false.
Basically, callback continues to the end of the event loop as opposed to context which ends the event loop. So setting callbackWaitsForEmptyEventLoop makes callback work like context.
I have also faced similar timeout scenario. Issue was not doing connection.end()
after connection.connect()
. Connection.end()
should be done before callback
.
Working Code:
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'host_name',
user : 'root',
password : 'password'
});
module.exports.handler = (event, context, callback) => {
// **Connection to database**
connection.connect(function(err) {
if (err) {
console.error('Database connection failed: ' + err.stack);
return;
}
console.log('Connected to database.');
});
// **Hit DB Query**
connection.query("Query", function(err, rows, fields) {
console.log(rows);
});
//**Close Connection**
connection.end(); ***// Missing this section will result in timeout***
//**Send API Response**
callback(null, {
statusCode: '200',
body: "Success",
headers: {
'Content-Type': 'application/json',
},
});
};
Both the RDS and the Lambda are in the same security group.
That's the key. By default communication within the same security group is not allowed. And you need to explicitly allow it (E.x sg-xxxxx ALL TCP ). This wll only work if your lambda tries to access db by private ip.
If it tries to access it by public IP that it will not work and you need to punch necessary wholes for that as well.
However there is better approach:
3306
in RDS sg for lambdas sg.The problem does not originate from the timeout, but from the way you close the connection. Use .destroy()
instead if you do not want to wait for the callback that OR use the callback correctly when closing the connection in .end(function(err) { //Now call your callback });
See this thread for a more in depth explanation.
I want to thank everyone who helped, the problem turned out to be different than I thought. The callback
in the code doesn't work for some reason even though it's in AMAZON'S OWN DEFAULT SAMPLE.
The working code looks like this:
'use strict';
console.log("Loading getContacts function");
var AWS = require('aws-sdk');
var mysql = require('mysql');
exports.handler = (event, context) => {
var connection = mysql.createConnection({
host : '...',
user : '...',
password : '...',
port : 3306,
database: 'ebdb',
debug : false
});
connection.connect(function(err) {
if (err) context.fail();
else context.succeed('Success');
});
};