Search code examples
lambdaamazon-dynamodb.net-6.0aws-sdk-net

Table.LoadTable from Amazon.DynamoDBv2.DocumentModel causing Times out in .NET proxy Lambda Function


I want to read from a DynamoDB table in a Lambda function. However, the function keeps timing out after 3 seconds. The Lambda function is used for a Lambda proxy integration with API Gateway. From troubleshooting through logging I have narrowed the problem down to the Table.LoadTable method.

This is the controller code:

using Microsoft.AspNetCore.Mvc;
using MyWebsite.Quote;

namespace MyWebsite.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class QuoteController : ControllerBase
    {
        private readonly ILogger<QuoteController> _logger;

        public QuoteController(ILogger<QuoteController> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        public ActionResult GetRandomQuote()
        {
            var quoteRetriever = new QuoteRetriever(_logger);
            var quoteData = quoteRetriever.GetRandomQuote();
            return Ok(quoteData);
        }
    }
}

and this is the class where the LoadTable method is called:

using Amazon;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DocumentModel;
using MyWebsite.Controllers;

namespace MyWebsite.Quote
{
    public class QuoteRetriever
    {
        private readonly ILogger<QuoteController> _logger;
        private static readonly AmazonDynamoDBClient DynamoDbClient = new AmazonDynamoDBClient();

        public QuoteRetriever(ILogger<QuoteController> logger)
        {
            _logger = logger;
        }

        public QuoteData GetRandomQuote()
        {
            // Get information about highest quoteNr
            // and missing Nrs
            const string tableInfo = "TableInfo";

            Document tableInfoItem;
            Table table;
            _logger.LogInformation("Reached GetRandomQuote.");
            try
            {
                _logger.LogInformation("About to set table.");
                // The problem has to be here.
                table = Table.LoadTable(DynamoDbClient, "QuotesTable");
                _logger.LogInformation("Set table.");

                tableInfoItem = table.GetItemAsync(tableInfo).Result;
                _logger.LogInformation("Retrieved tableInfoItem.");
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.LogInformation("Error Message: " + e.Message);
                _logger.LogInformation("Error Stacktrace: " + e.StackTrace);
                throw;
            }
            _logger.LogInformation("Successfully retrieved TableInfo!");

            var highestQuoteNr = tableInfoItem["HighestQuoteNumber"].AsInt();
            var missingQuoteNumbers = tableInfoItem["MissingQuoteNumbers"].AsListOfPrimitive();

            // Randomize Nr
            var random = new Random();
            var randomQuoteNumber = random.Next(0, highestQuoteNr);
            if (missingQuoteNumbers.Any())
            {
                while (missingQuoteNumbers.Contains(randomQuoteNumber))
                {
                    randomQuoteNumber = random.Next(0, highestQuoteNr + 1);
                }
            }

            var randomQuote = table.GetItemAsync(randomQuoteNumber.ToString()).Result;

            var quoteData = new QuoteData
            {
                QuoteNumber = randomQuoteNumber,
                Quote = randomQuote["Quote"].AsString(),
                Quotee = randomQuote["Quotee"].AsString()
            };

            return quoteData;
        }
    }
}

I have made sure that the table and the Lambda is in the same region, and have granted the Lambda full access to the table through CDK:

quotesTable.grantFullAccess(handler);

The Lambda is running as a .NET 6 minimal API. Here is the Program.cs file:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();

// Add AWS Lambda support. When application is run in Lambda Kestrel is swapped out as the web server with Amazon.Lambda.AspNetCoreServer. This
// package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core.
builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);

var app = builder.Build();


app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.MapGet("/", () => "Welcome to running ASP.NET Core Minimal API on AWS Lambda");

app.Run();

The code reaches the "About to set table." comment, but after that it times out. The weird thing is that when I was running the code locally and passed BasicAWSCredentials("accessKey", "secretKey") and the region explicitly to the AmazonDynamoDBClient it worked. But when I removed those arguments to the client and deployed to AWS it has not been working

Tried to read DynamoDB table from Lambda function running .NET 6 minimal API, but the function times out after 3 seconds at Table.LoadTable(DynamoDBClient, "TableName").


Solution

  • Lambda runs your code for a set amount of time before timing out, defaulting to 3 seconds which seems to be the culprit here. This would also explain why your code works perfectly fine locally, as it wouldn't be restricted to the 3 second timeout and would be allowed to run for longer.

    Increase the timeout duration for your function to something more suitable for your workload, up to a maximum of 15 minutes, via the console/CLI/SDK/IaC etc.

    Please note: you won't pay exttra for setting a higher timeout value, but that naturally means that your Lambda functions will be allowed to run for longer if they want to i.e. you will pay more if your Lambda needs to complete within 10 minutes as opposed to 25 seconds.


    I'd also recommend replacing .Result with async & await syntax for cleaner code.