Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AWS Lambda RDS connection timeout

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 run the function, it returns a timeout error.

Tried to increase the timeout up to 5 minutes with the exact same result.

The conclusion I came to after some research is that it's probably a security issue but couldn't find the solution in Amazon's documentation or in this answer (which is the only one I could find on the topic).

Here are the security details:

  • Both the RDS and the Lambda are in the same security group.
  • The RDS has All traffic inbound and outbound rules.
  • The Lambda has AmazonVPCFullAccess policy in it's role.

My code is:

'use strict';
console.log("Loading getContacts function");

var AWS = require('aws-sdk');
var mysql = require('mysql');

exports.handler = (event, context, callback) => {

   var connection = mysql.createConnection({
        host     : '...',
        user     : '...',
        password : '...',
        port     : 3306,
        database: 'ebdb',
        debug    :  false
    });

    connection.connect(function(err) {
      if (err) callback(null, 'error ' +err);
      else callback(null, 'Success');
    });

};

The result I'm getting is:

"errorMessage": "2017-03-05T05:57:46.851Z 9ae64c49-0168-11e7-b49a-a1e77ae6f56c Task timed out after 10.00 seconds"
like image 736
Sir Codesalot Avatar asked Mar 05 '17 06:03

Sir Codesalot


People also ask

How do I resolve a lambda timeout issue?

To troubleshoot the retry and timeout issues, first review the logs of the API call to find the problem. Then, change the retry count and timeout settings of the AWS SDK as needed for each use case. To allow enough time for a response to the API call, add time to the Lambda function timeout setting.

How do you increase connection timeout on RDS?

Go to Amazon RDS > Parameter Groups > Create a Postgres parameter group > Select the new parameter group > Click edit parameters > Filter for "alive" > Set tcp_keepalives_idle to 60 > Save. I also filtered for "timeout" and set statement_timeout to 3600000, though this might have been unnecessary.

What happens when AWS Lambda timeout?

When the specified timeout is reached, AWS Lambda terminates execution of your Lambda function. As a best practice, you should set the timeout value based on your expected execution time to prevent your function from running longer than intended.


4 Answers

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.

like image 96
ambaum2 Avatar answered Sep 23 '22 06:09

ambaum2


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:

  1. Create separate security group for your lambda
  2. Allow inbound traffic on port 3306 in RDS sg for lambdas sg.
like image 35
Vor Avatar answered Sep 23 '22 06:09

Vor


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');
    });

};
like image 32
Sir Codesalot Avatar answered Sep 22 '22 06:09

Sir Codesalot


When you originally setup the DB, it will automatically create a security group. defaulting to the IP that you set the DB up with. When you run from lambda this rule blocks traffic. Check out your db error logs and you can confirm it is refusing the connection.

***** could not be resolved: Name or service not known

You need to create a rule in the security group to allow lambda traffic. Go to your RDS instance console and click on the security group, select inbound. There you will see the rules. Then make the call to open up to the world, find the AWS lambda IPs or create a VPC.

like image 34
toonsend Avatar answered Sep 25 '22 06:09

toonsend