12 KiB
12 KiB
AWS Lambda Integration with RDS
Lambda RDS Connection Patterns
1. Traditional Lambda + RDS Connection
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class RdsLambdaHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) {
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
try {
// Get environment variables
String host = System.getenv("ProxyHostName");
String port = System.getenv("Port");
String dbName = System.getenv("DBName");
String username = System.getenv("DBUserName");
String password = System.getenv("DBPassword");
// Create connection string
String connectionString = String.format(
"jdbc:mysql://%s:%s/%s?useSSL=true&requireSSL=true",
host, port, dbName
);
// Execute query
String sql = "SELECT COUNT(*) FROM users";
try (Connection connection = DriverManager.getConnection(connectionString, username, password);
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
int count = resultSet.getInt(1);
response.setStatusCode(200);
response.setBody("{\"count\": " + count + "}");
}
}
} catch (Exception e) {
response.setStatusCode(500);
response.setBody("{\"error\": \"" + e.getMessage() + "\"}");
}
return response;
}
}
2. Lambda with Connection Pooling
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
public class RdsLambdaConfig {
private static DataSource dataSource;
public static synchronized DataSource getDataSource() {
if (dataSource == null) {
HikariConfig config = new HikariConfig();
String host = System.getenv("ProxyHostName");
String port = System.getenv("Port");
String dbName = System.getenv("DBName");
String username = System.getenv("DBUserName");
String password = System.getenv("DBPassword");
config.setJdbcUrl(String.format("jdbc:mysql://%s:%s/%s", host, port, dbName));
config.setUsername(username);
config.setPassword(password);
// Connection pool settings
config.setMaximumPoolSize(5);
config.setMinimumIdle(2);
config.setIdleTimeout(30000);
config.setConnectionTimeout(20000);
config.setMaxLifetime(1800000);
// MySQL-specific settings
config.addDataSourceProperty("useSSL", true);
config.addDataSourceProperty("requireSSL", true);
config.addDataSourceProperty("serverSslCertificate", "rds-ca-2019");
config.addDataSourceProperty("connectTimeout", "30");
dataSource = new HikariDataSource(config);
}
return dataSource;
}
}
3. Using AWS Secrets Manager for Credentials
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest;
import com.amazonaws.services.secretsmanager.model.GetSecretValueResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
public class RdsSecretsHelper {
private static final String SECRET_NAME = "prod/rds/db_credentials";
private static final String REGION = "us-east-1";
public static Map<String, String> getRdsCredentials() {
AWSSecretsManager client = AWSSecretsManagerClientBuilder.standard()
.withRegion(REGION)
.build();
GetSecretValueRequest request = GetSecretValueRequest.builder()
.secretId(SECRET_NAME)
.build();
GetSecretValueResult result = client.getSecretValue(request);
// Parse secret JSON
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> secretMap = objectMapper.readValue(result.getSecretString(), HashMap.class);
Map<String, String> credentials = new HashMap<>();
secretMap.forEach((key, value) -> {
credentials.put(key, value.toString());
});
return credentials;
}
}
4. Lambda with AWS SDK for RDS
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.rds.RdsClient;
import software.amazon.awssdk.services.rds.model.*;
public class RdsManagementLambda implements RequestHandler<ApiRequest, ApiResponse> {
@Override
public ApiResponse handleRequest(ApiRequest request, Context context) {
RdsClient rdsClient = RdsClient.builder()
.region(Region.US_EAST_1)
.build();
try {
switch (request.getAction()) {
case "list-instances":
return listInstances(rdsClient);
case "create-snapshot":
return createSnapshot(rdsClient, request.getInstanceId(), request.getSnapshotId());
case "describe-instance":
return describeInstance(rdsClient, request.getInstanceId());
default:
return new ApiResponse(400, "Unknown action: " + request.getAction());
}
} catch (Exception e) {
context.getLogger().log("Error: " + e.getMessage());
return new ApiResponse(500, "Error: " + e.getMessage());
} finally {
rdsClient.close();
}
}
private ApiResponse listInstances(RdsClient rdsClient) {
DescribeDbInstancesResponse response = rdsClient.describeDBInstances();
return new ApiResponse(200, response.toString());
}
private ApiResponse createSnapshot(RdsClient rdsClient, String instanceId, String snapshotId) {
CreateDbSnapshotRequest request = CreateDbSnapshotRequest.builder()
.dbInstanceIdentifier(instanceId)
.dbSnapshotIdentifier(snapshotId)
.build();
CreateDbSnapshotResponse response = rdsClient.createDBSnapshot(request);
return new ApiResponse(200, "Snapshot created: " + response.dbSnapshot().dbSnapshotIdentifier());
}
private ApiResponse describeInstance(RdsClient rdsClient, String instanceId) {
DescribeDbInstancesRequest request = DescribeDbInstancesRequest.builder()
.dbInstanceIdentifier(instanceId)
.build();
DescribeDbInstancesResponse response = rdsClient.describeDBInstances(request);
return new ApiResponse(200, response.toString());
}
}
class ApiRequest {
private String action;
private String instanceId;
private String snapshotId;
// getters and setters
}
class ApiResponse {
private int statusCode;
private String body;
// constructor, getters
}
Best Practices for Lambda + RDS
1. Security Configuration
IAM Role:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"rds:*"
],
"Resource": "*"
}
]
}
Security Group:
- Use security groups to restrict access
- Only allow Lambda function IP ranges
- Use VPC endpoints for private connections
2. Environment Variables
# Environment variables for Lambda
DB_HOST=mydb.abc123.us-east-1.rds.amazonaws.com
DB_PORT=5432
DB_NAME=mydatabase
DB_USERNAME=admin
DB_PASSWORD=${DB_PASSWORD}
DB_CONNECTION_STRING=jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}
3. Error Handling
import com.amazonaws.services.lambda.runtime.LambdaLogger;
public class LambdaErrorHandler {
public static void handleRdsError(Exception e, LambdaLogger logger) {
if (e instanceof RdsException) {
RdsException rdsException = (RdsException) e;
logger.log("RDS Error: " + rdsException.awsErrorDetails().errorCode());
switch (rdsException.awsErrorDetails().errorCode()) {
case "DBInstanceNotFoundFault":
logger.log("Database instance not found");
break;
case "InvalidParameterValueException":
logger.log("Invalid parameter provided");
break;
case "InstanceAlreadyExistsFault":
logger.log("Instance already exists");
break;
default:
logger.log("Unknown RDS error: " + rdsException.getMessage());
}
} else {
logger.log("Non-RDS error: " + e.getMessage());
}
}
}
4. Performance Optimization
Cold Start Mitigation:
import javax.sql.DataSource;
import java.sql.Connection;
public class RdsConnectionHelper {
private static DataSource dataSource;
private static long lastConnectionTime = 0;
private static final long CONNECTION_TIMEOUT = 300000; // 5 minutes
public static Connection getConnection() throws SQLException {
long currentTime = System.currentTimeMillis();
if (dataSource == null || (currentTime - lastConnectionTime) > CONNECTION_TIMEOUT) {
dataSource = createDataSource();
lastConnectionTime = currentTime;
}
return dataSource.getConnection();
}
private static DataSource createDataSource() {
// Connection pool creation
}
}
Batch Processing:
public class RdsBatchProcessor {
public void processBatch(List<String> userIds) {
String sql = "SELECT * FROM users WHERE user_id IN (?)";
try (Connection connection = getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
// Convert list to SQL IN clause
String placeholders = userIds.stream()
.map(id -> "?")
.collect(Collectors.joining(","));
String finalSql = sql.replace("?", placeholders);
// Set parameters
for (int i = 0; i < userIds.size(); i++) {
statement.setString(i + 1, userIds.get(i));
}
ResultSet resultSet = statement.executeQuery();
// Process results
} catch (SQLException e) {
LambdaErrorHandler.handleRdsError(e, logger);
}
}
}
5. Monitoring and Logging
import com.amazonaws.services.cloudwatch.AmazonCloudWatch;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchClientBuilder;
import com.amazonaws.services.cloudwatch.model.MetricDatum;
import com.amazonaws.services.cloudwatch.model.PutMetricDataRequest;
public class RdsMetricsPublisher {
private static final String NAMESPACE = "RDS/Lambda";
private AmazonCloudWatch cloudWatch;
public RdsMetricsPublisher() {
this.cloudWatch = AmazonCloudWatchClientBuilder.defaultClient();
}
public void publishMetric(String metricName, double value) {
MetricDatum datum = new MetricDatum()
.withMetricName(metricName)
.withUnit("Count")
.withValue(value)
.withTimestamp(new Date());
PutMetricDataRequest request = new PutMetricDataRequest()
.withNamespace(NAMESPACE)
.withMetricData(Collections.singletonList(datum));
cloudWatch.putMetricData(request);
}
}