Powertools for AWS Lambda (Java)

repository·main·Indexed 43 days ago

https://github.com/aws-powertools/powertools-lambda-java

A developer toolkit for implementing serverless best practices in AWS Lambda environments using Java and Kotlin. It provides modules for structured logging, AWS X-Ray tracing, and custom CloudWatch metrics, along with utilities for batch processing (SQS, Kinesis, DynamoDB Streams) and CloudFormation Custom Resources.

Tokens
88.8K
Snippets
210
Records
276
Agent score
56%

What's inside Powertools for AWS Lambda (Java)

  1. Key features of the Kafka utility

    main

    The Powertools Kafka utility simplifies working with Kafka messages in AWS Lambda by providing:

    • Automatic deserialization: Converts binary data (base64-encoded in Lambda events) into usable Java objects using JSON, Avro, or Protocol Buffers.
    • Simplified event handling: Provides a familiar ConsumerRecords interface for handling event records.
    • Key and Value support: Allows for independent deserialization of both the Kafka message key and the message value.
    • ESM Support: Works with Event Source Mapping (ESM) both with and without Schema Registry integration.
    • Error handling: Provides structured error handling for deserialization issues.
  2. Explore Powertools for AWS Lambda (Java) examples

    main

    The examples/ directory contains independent projects demonstrating various Powertools modules. You can copy any subdirectory to use it as a starting point for your own project.

    Available example modules:

    • core-utilities: Demonstrates logging, tracing, and metrics. Supports CDK, SAM, SAM GraalVM, Serverless, Terraform, Gradle, and Kotlin.
    • idempotency: Demonstrates an idempotent HTTP API using SAM and SAM GraalVM.
    • parameters: Shows how to use the parameters module to provide runtime parameters to a function (SAM and SAM GraalVM).
    • serialization: Demonstrates serializing and deserializing API Gateway & SQS payloads (SAM and SAM GraalVM).
    • validation: Demonstrates the validation module for user requests via API Gateway.
    • cloudformation: Shows how to deploy a CloudFormation custom resource.
    • batch: Provides examples for different batch processing deployments.
    • kafka: Provides examples for Kafka event processing.
  3. Key features of the Idempotency utility

    main

    The utility provides the following capabilities:

    • Execution Prevention: Prevents the Lambda handler function from executing more than once on the same event payload during a specified time window.
    • Consistent Results: Ensures the Lambda handler returns the same result when called with the same payload.
    • Granular Key Selection: Allows you to select a specific subset of the event to serve as the idempotency key using JMESPath expressions.
    • Configurable Time Window: Enables setting a time window in which records with the same payload are considered duplicates.
  4. What is the Idempotency utility and how does it work?

    main

    The Idempotency utility converts Lambda functions into idempotent operations, making them safe to retry. An idempotent operation ensures that calling the function multiple times with the same input parameters does not cause additional side effects and returns the same result.

    To achieve this, the utility generates an Idempotency key, which is a hash representation of either the entire event or a specific configured subset of the event. The invocation results are then JSON serialized and stored in a persistence storage layer. If a subsequent invocation arrives with the same key within a defined time window, the utility prevents the handler from executing again and instead returns the stored result.

  5. Combine Large Messages with the Idempotency module

    main

    When combining @LargeMessage and @Idempotent, the @Idempotent annotation takes precedence. This means the Idempotency module will use the initial raw message (containing the S3 pointer) as the basis for the idempotency key, rather than the large unwrapped blob. This is the recommended behavior to ensure consistency.

    Important for Functional API: If using the functional API, you must call LargeMessages.processLargeMessage() inside the @Idempotent method to ensure the idempotency check is performed on the S3 pointer before the large payload is fetched.

    // Recommended pattern for Annotation
    @Idempotent
    @LargeMessage
    private String processRawMessage(@IdempotencyKey SQSEvent.SQSMessage sqsMessage, Context context) {
        // do something with the message
    }
    
    // Recommended pattern for Functional API
    @Idempotent
    private String processRawMessage(@IdempotencyKey SQSEvent.SQSMessage sqsMessage, Context context) {
        return LargeMessages.processLargeMessage(sqsMessage, this::handleProcessedMessage);
    }
  6. Deserialize Kafka Lambda events using @Deserialization

    main

    Powertools for AWS Lambda (Java) allows you to automatically deserialize Kafka Lambda events into strongly typed ConsumerRecords<K, V> objects. This eliminates manual deserialization logic by using the @Deserialization annotation combined with a specific DeserializationType.

    Supported formats include:

    • JSON: Standard JSON serialization.
    • Avro: Apache Avro schema-based serialization.
    • Protobuf: Google Protocol Buffers serialization.
  7. Implement idempotent Kafka processing

    main

    To prevent duplicate processing caused by Lambda retries or failed batches, use the Powertools Idempotency utility. The @Idempotent annotation ensures that a specific operation is only executed once for a given input by tracking the result in a persistence store (like DynamoDB).

    1. Configure the Idempotency utility with a PersistenceStore (e.g., DynamoDBPersistenceStore) in your handler constructor.
    2. Annotate the specific method performing the business logic with @Idempotent.

    The utility uses the JSON representation of the input object to identify unique messages.

    public class IdempotentKafkaHandler implements RequestHandler<ConsumerRecords<String, Payment>, String> {
        public IdempotentKafkaHandler() {
            Idempotency.config()
                .withPersistenceStore(
                    DynamoDBPersistenceStore.builder()
                        .withTableName("IdempotencyTable")
                        .build())
                .configure();
        }
    
        @Override
        @Logging
        @Deserialization(type = DeserializationType.KAFKA_JSON)
        public String handleRequest(ConsumerRecords<String, Payment> records, Context context) {
            for (ConsumerRecord<String, Payment> record : records) {
                Payment payment = record.value();
                processPayment(payment);
            }
            return "OK";
        }
    
        @Idempotent
        private void processPayment(Payment payment) {
            // Business logic here
            PaymentService.process(payment.getPaymentId(), payment.getCustomerId(), payment.getAmount());
        }
    }
  8. Identify and Triage Bugs

    main

    A bug is defined as producing incorrect or unexpected results at runtime that differ from intended behavior.

    Key Criteria:

    • Bugs must be reproducible.
    • Bugs must directly affect customer experience at runtime despite following recommended usage.
    • Not a bug: Documentation snippets, use of internal components, or unadvertised functionalities.

    Triage Actions:

    • If it is not a bug, close it or adjust the title/labels while communicating the reason.
    • If the bug is caused by an upstream dependency, replace the bug label with bug-upstream and ask the author if they want to raise it upstream or if the maintainers should.
    • Assess if an emergency release is required based on impact.
  9. How the Large Messages utility works

    main

    The Large Messages utility is designed to handle SQS and SNS messages that exceed the 1MB payload limit. When using the Amazon SQS/SNS Java Extended Client Libraries, large payloads are offloaded to Amazon S3, and the message body is replaced with a pointer to the S3 object.

    The utility's lifecycle for a large message:

    1. Detection: It identifies if a message contains an S3 pointer via a specific message attribute.
    2. Retrieval: It automatically fetches the content from the S3 bucket and object key specified in the pointer.
    3. Transformation: It updates the message body with the actual content and removes the S3 pointer attribute.
    4. Processing: Your Lambda code receives the full message body as if it were a standard small message.
    5. Cleanup: Once your code processes the message successfully, the utility automatically deletes the object from S3.

    This utility is compatible with versions 1.1.0+ and 2.0.0+ of the SQS/SNS extended client libraries.

  10. Understand the CloudFormation Custom Resource module

    main

    The CloudFormation module in Powertools for AWS Lambda (Java) allows Lambda functions to act as Custom Resources within a CloudFormation stack. This enables the Lambda to respond to lifecycle events such as CREATE, UPDATE, and DELETE.

    In the provided example, the Lambda function manages an S3 bucket lifecycle based on these events:

    • CREATE: Creates the bucket.
    • UPDATE: Re-creates the bucket if the name parameter changes.
    • DELETE: Deletes the bucket.

    For detailed API usage, refer to the official documentation.

  11. How Lambda Metadata caching and SnapStart work

    main

    Caching

    Metadata is cached automatically after the first call to LambdaMetadataClient.get(). Subsequent calls return the same cached instance without making additional HTTP requests. This is safe because metadata like the Availability Zone does not change during a sandbox's lifetime.

    SnapStart Integration

    When using AWS Lambda SnapStart, the function may be restored in a different Availability Zone. The utility integrates with CRaC to automatically invalidate the cache upon restore.

    Best Practice: If using eager loading (module-level initialization) with SnapStart, ensure LambdaMetadataClient is referenced during class initialization so the CRaC hook registers before the snapshot is taken.