0

I have a C# Lambda Function that works on the first execution but fails on the second. I created some logs to help me troubleshoot where the issue is but I'm struggling to figure out what is actually happening.

First Execution Log:

START RequestId: XXXXXXXXXXXXXXXXXXXXXXXXXXXXX Version: $LATEST

 1. Connecion to Database is Open
 2. Running Query: SELECT * FROM Employee3
 3. Returning Results
 4. Close connection
 5. Close reader
 6. Dispose cmd
 7. Configure encoding and string to stream
END RequestId: XXXXXXXXXXXXXXXXXXXXXXXX
REPORT RequestId: XXXXXXXXXXXXXXXXXXXXXXXX  Duration: 10097.73 ms   Billed Duration: 10100 ms   Memory Size: 128 MB Max Memory Used: 41 MB  

Response: (i know this isn't valid JSON, which I am ultimately aiming for, but that will be the next item I tackle once I figure this out)

Connecion to Database is Open
Joe
Bob
Mary

Second Execution Log:

    START RequestId: XXXXXXXXXXXXXXXXXXXXXXXXXXXXX Version: $LATEST

     1. Connecion to Database is Open
     2. Running Query: SELECT * FROM Employee3
       Connection must be valid and open.: InvalidOperationException
       at MySql.Data.MySqlClient.MySqlCommand.Throw(Exception ex)
       at MySql.Data.MySqlClient.MySqlCommand.CheckState()
       at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
       at LambdaDBTest.Function.returnRows(MySqlConnection connection)
       at LambdaDBTest.Function.FunctionHandler(Stream input, ILambdaContext context)
       at lambda_method(Closure , Stream , Stream , ContextInfo )


    END RequestId: XXXXXXXXXXXXXXXXXXXXXXXX
    REPORT RequestId: XXXXXXXXXXXXXXXXXXXXXXXX  Duration: 1203.12 ms    Billed Duration: 1300 ms    Memory Size: 128 MB Max Memory Used: 47 MB

The following is the code I am using:

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MySql.Data.MySqlClient;
using Amazon.Lambda.Core;
using Amazon.Lambda.Serialization;
using System.Text;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializerAttribute(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

namespace LambdaDBTest
{
    public class Function
    {
        /// <summary>
        /// A simple function that takes a string and does a ToUpper
        /// </summary>
        /// <param name="input"></param>
        /// <param name="context"></param>
        /// <returns></returns>

        public Stream FunctionHandler(Stream input, ILambdaContext context)
        {
            var dbCon = DBConnection.Instance();

            dbCon.Server = "AWS-URI";
            dbCon.DatabaseName = "DBNAME";
            dbCon.User = "DBUSER";
            dbCon.Password = "DBPASS";
            string textInput;
            if (dbCon.IsConnect())
            {
                textInput = "Connecion to Database is Open";
                LambdaLogger.Log("\n 1. " + textInput);
                textInput += returnRows(dbCon.Connection);
            }
            else
            {
                textInput = "\n Connecion to Database is NOT Open";
            }
            dbCon.Close();
            LambdaLogger.Log("\n 7. Configure encoding and string to stream\n");
            input = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(textInput));
            return input;
        }

        public static MemoryStream GenerateStreamFromString(string value)
        {
            return new MemoryStream(Encoding.UTF8.GetBytes(value ?? ""));
        }

        static string returnRows(MySqlConnection connection)
        {
            string output = null;
            string query = @"SELECT * FROM Employee3";
            LambdaLogger.Log("\n 2. Running Query: " + query);
            var cmd = new MySqlCommand(query, connection);
            var reader = cmd.ExecuteReader();
            LambdaLogger.Log("\n 3. Returning Results");
            while (reader.Read())
            {
                output += "\n" + (string)reader["Name"];
            }
                LambdaLogger.Log("\n 4. Close connection");
            connection.Close();


                LambdaLogger.Log("\n 5. Close reader");
            reader.Close();

                LambdaLogger.Log("\n 6. Dispose cmd");
            cmd.Dispose();
            return output + "\r";
        }
    }
}

So the code is very basic, and a bit of a cut and paste job using the MySql.Data 6.10.1-beta Nu-Get Package for MySQL and AWS Lambda.

I cannot understand why I am getting an issue when it executes on the first attempt but not the second. There is zero state saving in Lambda as I understand it.

Hopefully I'm doing something stupid or missing something obvious which I will be happy to be notified about. If you need additional information, let me know.

EDITED CODE:

So a fix was found as shown below:

public Stream FunctionHandler(Stream input, ILambdaContext context)
        {
            var dbCon = DBConnection.Instance();

            dbCon.Server = "######.rds.amazonaws.com";
            dbCon.DatabaseName = "#####";
            dbCon.User = "#####";
            dbCon.Password = "######";
            string textInput;
            if (dbCon.IsConnect())
            {
                textInput = "Connecion to Database is Open";
                LambdaLogger.Log("\n 1. " + textInput);
                textInput += returnRows(dbCon.Connection);
            }
            else
            {
                textInput = "\n Connecion to Database is NOT Open";
            }

            //    LambdaLogger.Log("\n 7. Close Database connection");
            //dbCon.Close();
            LambdaLogger.Log("\n 8. Configure encoding and string to stream\n");
            input = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(textInput));
            return input;
        }

        public static MemoryStream GenerateStreamFromString(string value)
        {
            return new MemoryStream(Encoding.UTF8.GetBytes(value ?? ""));
        }

        static string returnRows(MySqlConnection connection)
        {
            string output = null;
            string query = @"SELECT * FROM Employee3";
            LambdaLogger.Log("\n 2. Running Query: " + query);
            var cmd = new MySqlCommand(query, connection);
            var reader = cmd.ExecuteReader();
            LambdaLogger.Log("\n 3. Returning Results");
            while (reader.Read())
            {
                output += "\n" + (string)reader["Name"];
            }

            //    LambdaLogger.Log("\n 4. Close connection");
            //connection.Close();


                LambdaLogger.Log("\n 5. Close reader");
            reader.Close();

            //    LambdaLogger.Log("\n 6. Dispose cmd");
            //cmd.Dispose();
            return output + "\r";
        }

The process reader was frozen by Lambda, so whilst I did not need to need to close MySQL connection due to connection pooling of the .net MySQL Driver, i did in fact need to invoke reader.Close(); to kill the frozen process. Thank you Udo Held :)

1
  • 1
    might want to change your username and password now Commented Apr 13, 2017 at 13:34

1 Answer 1

2

I've got a suspicion. The code looks like you are opening and closing the connection each time which is good. However, the your driver might not actually close the connection, but just return it to the pool. AWS freezes the instances between requests. So if a connection to the database is held open by the pool, it actually could get disconnected due to your code being frozen by AWS.

Looking at the .net MySQL driver documentation it states "The Connector/Net supports connection pooling for better performance and scalability with database-intensive applications. This is enabled by default."

You didn't provide a connection string, but you should probably try to disable connection pooling adding something like Pooling=false. There are other options you might want to try as well.

Sign up to request clarification or add additional context in comments.

1 Comment

You could well be on the right track here. The database is hosted in an RDS instance so that is not something I think I have access to. I just updated my code to remove all the connection close statements and then recieved the following error "errorMessage": "There is already an open DataReader associated with this Connection which must be closed first....this is definitely a clue :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.