AWS Lambda RDS connection timeout

33,000

Solution 1

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

};

Solution 2

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.

Solution 3

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.

Solution 4

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.

Solution 5

It took me around 2 days to figure out the exact issue. In my case, both RDS and Lambda function was in Same VPC, Subnet and security group and added the Required Roles but still was getting Socket timeout exception. I was able to solve the issue by changing inbound and outbound rule by following the below link -

https://aws.amazon.com/premiumsupport/knowledge-center/connect-lambda-to-an-rds-instance/

Share:
33,000
Sir Codesalot
Author by

Sir Codesalot

Updated on May 26, 2021

Comments

  • Sir Codesalot
    Sir Codesalot almost 3 years

    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"
    
  • Mark B
    Mark B about 7 years
    Please read the question. Everything you mentioned is covered in the question already.
  • Sir Codesalot
    Sir Codesalot about 7 years
    Created a separate security group for the Lambda and have All traffic inbound on the RDS but still the same problem...
  • Sir Codesalot
    Sir Codesalot about 7 years
    Lambda and RDS are both on the same (default) VPC
  • Quad64Bit
    Quad64Bit about 7 years
    I fought this for over an hour - close to two. I thought my firewall rules were hosed. OMG, how can simply deleting the callback line fix everything? Anyway, good tip, I had done the same thing. Must be some kind of callback deadlock or something.
  • fireant
    fireant almost 7 years
    You need to end the connection before calling the callback. As the connection remains open, the lambda times out. Need to add something like this to the callback of .connect(), connection.end(function (err) { callback(null, response);});.
  • AngelSalazar
    AngelSalazar about 6 years
    your answer solved my problem, do you know how can I find my AWS Lambda IP? @toonsend
  • toonsend
    toonsend about 6 years
    I don't think it works that way. You'll need to set up a VPC. docs.aws.amazon.com/lambda/latest/dg/vpc.html
  • Gary Benade
    Gary Benade almost 6 years
    Legend! thank you, this is the correct answer: context.callbackWaitsForEmptyEventLoop = false;
  • hawkip
    hawkip over 5 years
    Lifesaver. Who would have thought that AWS would prevent comms within the same security group by default? NONE of the AWS tutorials mention this, they are clear you need your Lambda and RDS in the same group, but fail to mention you'll need to enable them to communicate. (My preferred way is to add an InBound rule to allow all TCP traffic from within the same security group, but the suggestion to create a new one for Lambda and enable it would also work of course.)
  • jarodsmk
    jarodsmk about 5 years
    Came across this answer - just want to point out that the callback param is optional depending on your NodeJS version, as per the AWS Docs: docs.aws.amazon.com/lambda/latest/dg/…
  • RenanSS
    RenanSS almost 5 years
    Not all heroes wear cape! thank you. context.callbackWaitsForEmptyEventLoop = false; is correct context.callbackWaitsForEmptyEventLoop = false; const response = { statusCode: 200, body: JSON.stringify({ headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': true, }, message: await mysqlConnector.create('table_name', {test: 50}), input: event, }), }; callback(null, response);
  • fedemengo
    fedemengo almost 3 years
    Thank god, I've been struggling with this for a week now