Module 4: AWS Lambda Functions Review

Module Overview

Review AWS Lambda functions and how to effectively test and deploy serverless applications.

Learning Objectives

  • Review the core concepts of serverless computing and AWS Lambda
    • Event-driven architecture
    • Function as a Service (FaaS)
    • AWS Lambda execution model
  • Understand the AWS Lambda programming model with Java
    • Handler interfaces and implementation patterns
    • Request and response objects
    • Context object
  • Learn effective testing strategies for Lambda functions
    • Unit testing Lambda handlers
    • Mocking AWS services
    • Integration testing with the AWS SDK
  • Explore best practices for Lambda development
    • Function sizing and timeouts
    • Error handling and retries
    • Logging and monitoring
    • Security best practices
  • Review the deployment lifecycle for Lambda functions
    • Packaging Lambda functions
    • Deployment options (console, CLI, SAM, CloudFormation)
    • Version control and aliases
  • Apply test-driven development practices to Lambda function development

AWS Lambda

What is an AWS Lambda function?

AWS Lambda is a server-less computing service provided by Amazon Web Services (AWS). Users of AWS Lambda create functions, self-contained applications written in one of the supported languages and runtimes, and upload them to AWS Lambda, which executes those functions in an efficient and flexible manner. Therefore you don't need to worry about which AWS resources to launch, or how will you manage them. Instead, you need to put the code on Lambda, and it runs.

How does AWS Lambda work?

While using AWS Lambda you will realize that one does not get much visibility into how the system operates. However, we will try to learn on a high level what happens underneath. Each Lambda function runs in its own container. When a function is created, Lambda packages it into a new container and then executes that container. Before the functions start running, each function's container is allocated its necessary RAM and CPU capacity. Once the functions finish running, the RAM allocated at the beginning is multiplied by the amount of time the function spent running. The customers then get charged based on the allocated memory and the amount of run time the function took to complete. One of the distinctive architectural properties of AWS Lambda is that many instances of the same function, or of different functions from the same AWS account, can be executed concurrently. Moreover, the concurrency can vary according to the time of day or the day of the week, and such variation makes no difference to Lambda—you only get charged for the compute your functions use. This makes AWS Lambda a good fit for deploying highly scalable cloud computing solutions.

Advantages of AWS Lambda

  • Pay per use: You pay only for the compute your functions use, plus any network traffic generated. For workloads that scale significantly according to time of day, this type of billing is generally more cost-effective.
  • Fully managed infrastructure: Now that your functions run on the managed AWS infrastructure, you don't need to think about the underlying servers—AWS takes care of this for you. This can result in significant savings on operational tasks such as upgrading the operating system or managing the network layer.
  • Automatic scaling: AWS Lambda creates the instances of your function as they are requested.
  • Integration: Tight integration with other AWS products. AWS Lambda integrates with services like DynamoDB, S3 and API Gateway, allowing you to build functionally complete applications within your Lambda functions.

Limitations of AWS Lambda

  • Cold Start: When a function is started in response to an event, there may be a small amount of latency between the event and when the function runs. If your function hasn't been used in the last 15 minutes, the latency can be as high as 5-10 seconds, making it hard to rely on Lambda for latency-critical applications.
  • Execution time limit: Execution time/run time. A Lambda function will time out after running for 15 minutes.
  • Code package size limit: The zipped Lambda code package should not exceed 50MB in size, and the unzipped version shouldn't be larger than 250MB.

Common use cases

  • Data processing: Lambda functions are optimized for event-based data processing. It is easy to integrate AWS Lambda with datasources like Amazon DynamoDB and trigger a Lambda function for specific kinds of data events. For example, you could employ Lambda to do some work every time an item in DynamoDB is created or updated, thus making it a good fit for things like notifications, counters and analytics.
  • Real time processing: Lambda functions can be used you to perform real-time file processing and real-time stream processing considering it's server-less nature.

AWS Lambda functions in JAVA

Lambda provides runtimes for Java that run your code to process events, hence making it possible to run Java code in AWS Lambda

The Lambda function handler is the method in your function code that processes events. When your function is invoked, Lambda runs the handler method. When the handler exits or returns a response, it becomes available to handle another event. There are there are 3 ways of creating a handler in Java:

  • Creating a custom MethodHandler
  • Implementing the RequestHandler interface
  • Implementing the RequestStreamHandler interface

Based on the choice of creating the handler, the handler needs to be defined in the AWS Lambda configuration in the format:

  • package.Class::method - Full format
  • package.Class - Abbreviated format for functions that implement a handler interface. For example: example.Handler

AWS API Gateway

Introduction

Amazon API Gateway is a fully managed AWS service that makes it easy for developers to create, publish, maintain, monitor, and secure APIs at any scale. APIs act as the "front door" for applications to access data, business logic, or functionality from your backend services. Using API Gateway, you can create RESTful APIs and WebSocket APIs that enable real-time two-way communication applications. API Gateway supports containerized and server-less workloads, as well as web applications.

AWS Lambda with API Gateway

AWS API Gateway allows to make AWS Lambda functions as an endpoint. This allows the request sent to the API Gateway to be directed to Lambda function for processing. The API Gateway also allows to send extra information in form of header, method type and request body which allows Lambda functions to support method types like POST, GET and others.

Code Example: AWS Lambda Function with Testing

// Lambda Function Handler
public class OrderProcessor implements RequestHandler {
    private final OrderService orderService;
    
    // Using dependency injection for testability
    public OrderProcessor(OrderService orderService) {
        this.orderService = orderService;
    }
    
    // Default constructor for AWS Lambda
    public OrderProcessor() {
        this.orderService = new OrderServiceImpl(new DynamoDBOrderRepository());
    }
    
    @Override
    public APIGatewayProxyResponse handleRequest(APIGatewayProxyRequest request, Context context) {
        LambdaLogger logger = context.getLogger();
        logger.log("Processing order request: " + request.getBody());
        
        try {
            // Parse request body
            Order order = new ObjectMapper().readValue(request.getBody(), Order.class);
            
            // Process order
            OrderResult result = orderService.processOrder(order);
            
            // Return success response
            return new APIGatewayProxyResponseBuilder()
                .withStatusCode(200)
                .withBody(new ObjectMapper().writeValueAsString(result))
                .build();
                
        } catch (Exception e) {
            logger.log("Error processing order: " + e.getMessage());
            
            // Return error response
            return new APIGatewayProxyResponseBuilder()
                .withStatusCode(500)
                .withBody("{\"error\": \"" + e.getMessage() + "\"}")
                .build();
        }
    }
}

// Unit Test for Lambda Function
@Test
void shouldProcessOrderSuccessfully() throws Exception {
    // Arrange
    OrderService mockOrderService = mock(OrderService.class);
    OrderResult expectedResult = new OrderResult("12345", "COMPLETED");
    when(mockOrderService.processOrder(any(Order.class))).thenReturn(expectedResult);
    
    OrderProcessor processor = new OrderProcessor(mockOrderService);
    
    APIGatewayProxyRequest request = new APIGatewayProxyRequest();
    request.setBody("{\"orderId\":\"12345\",\"amount\":100.0,\"items\":[\"item1\",\"item2\"]}");
    
    Context mockContext = mock(Context.class);
    when(mockContext.getLogger()).thenReturn(mock(LambdaLogger.class));
    
    // Act
    APIGatewayProxyResponse response = processor.handleRequest(request, mockContext);
    
    // Assert
    assertEquals(200, response.getStatusCode());
    assertTrue(response.getBody().contains("COMPLETED"));
    
    // Verify service was called
    verify(mockOrderService).processOrder(any(Order.class));
}

Guided Project

This Guided Project aims to give you a bigger picture of how AWS Lambda is used while also reviewing the fundamentals of how to set up an AWS Lambda function. You will hear references to API Gateway and DynamoDB - two concepts covered in later sprints. Since this Guided Project uses features you will learn later, no starter code is given, and there's no need to follow along with the code. Instead, do your best to gain insights into what is coming while reviewing how to set up AWS Lambda.

Resources