I’m trying to set up some basic CRUD function for a blog to get my feet wet with AWS. This is the lambda function I’m using:
public class StoreDataLambda implements RequestStreamHandler {
public static final Logger LOGGER = LoggerFactory.getLogger(StoreDataLambda.class);
private static final String DB_URL = "<my jdbc url>.us-east-2.rds.amazonaws.com:5432/postgres";
@Override
public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Connection connection = null;
try {
String secretName = "<mySecretName>";
Region region = Region.of("us-east-2");
// Create a Secrets Manager client
SecretsManagerClient client = SecretsManagerClient.builder()
.region(region)
.build();
GetSecretValueRequest getSecretValueRequest = GetSecretValueRequest.builder()
.secretId(secretName)
.build();
GetSecretValueResponse getSecretValueResponse = null;
try {
getSecretValueResponse = client.getSecretValue(getSecretValueRequest);
} catch (Exception e) {
// For a list of exceptions thrown, see
// https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
LOGGER.error("Error occurred while retrieving secret value: " + e.getMessage());
e.printStackTrace();
}
String secret = getSecretValueResponse.secretString();
JsonNode secretJson = mapper.readTree(secret);
String dbUrl = DB_URL;
String dbUsername = secretJson.get("username").asText();
String dbPassword = secretJson.get("password").asText();
LOGGER.info("Attempting to connect to the DB...");
connection = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);
JsonNode jsonInput = mapper.readTree(input);
// Assuming JSON structure: { "title": "value1", "content": "value2" }
String title = jsonInput.get("title").asText();
String content = jsonInput.get("content").asText();
// Store data into PostgreSQL using prepared statement
String sql = "INSERT INTO blog_page.blog_post (title, content) VALUES (?, ?)";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, title);
statement.setString(2, content);
statement.executeUpdate();
// Close resources
statement.close();
connection.close();
} catch (SQLException e) {
// Handle database exceptions
LOGGER.error("Error occurred while connecting to the database: " + e.getMessage());
e.printStackTrace();
} finally {
try {
if (connection != null && !connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
LOGGER.error("Error occurred while closing the connection: " + e.getMessage());
e.printStackTrace();
}
}
}
The lambda function and the DB are both in the same VPC/security group/subnets and I have set up the role for the lambda such that it should provide read/write access to the DB. Whenever I test it in AWS by sending a json, it just times out after 3 seconds.
This is all I get in the logs:
2023-12-12T06:00:11.938Z 3bd24ba5-8e9e-472a-b0ef-458898205451 Task timed out after 3.01 seconds
Any ideas on where to start with troubleshooting would be greatly welcome.
2
Answers
So it turns out the problem was fairly simple.
I was using this jdbc url format:
instead of this...
Correcting that solved the problem. Lambda can now talk to the DB!
I also did some refactoring so now the debug logging works as well. In addition, it is taking quite a bit longer for the Lambda to run than I'd have imagined. The 3 second default just wasn't enough for a cold start.
Merely putting resources "in the same security group" does not ensure connectivity. The rules of the Security Group are applied to each resource individually. There is no concept of resources being ‘in’ a security group.
The preferred configuration is:
Lambda-SG
) that permits all outbound trafficDB-SG
) that permits inbound traffic on the appropriate port (eg port 3306 for MySQL) with asource
set toLambda-SG
That is, the
DB-SG
specifically references theLambda-SG
in the Inbound rule.Alternatively, if you wish to use a single security group (not recommended), then you will need to add a rule to that security group to permit inbound traffic from itself. This will allow all resources that use the security group to communicate with each other.