Initial commit
This commit is contained in:
122
skills/aws-java/aws-sdk-java-v2-rds/references/api-reference.md
Normal file
122
skills/aws-java/aws-sdk-java-v2-rds/references/api-reference.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# AWS RDS API Reference
|
||||
|
||||
## Core API Operations
|
||||
|
||||
### Describe Operations
|
||||
- `describeDBInstances` - List database instances
|
||||
- `describeDBParameterGroups` - List parameter groups
|
||||
- `describeDBSnapshots` - List database snapshots
|
||||
- `describeDBSubnetGroups` - List subnet groups
|
||||
|
||||
### Instance Management
|
||||
- `createDBInstance` - Create new database instance
|
||||
- `modifyDBInstance` - Modify existing instance
|
||||
- `deleteDBInstance` - Delete database instance
|
||||
|
||||
### Parameter Groups
|
||||
- `createDBParameterGroup` - Create parameter group
|
||||
- `modifyDBParameterGroup` - Modify parameters
|
||||
- `deleteDBParameterGroup` - Delete parameter group
|
||||
|
||||
### Snapshots
|
||||
- `createDBSnapshot` - Create database snapshot
|
||||
- `restoreDBInstanceFromDBSnapshot` - Restore from snapshot
|
||||
- `deleteDBSnapshot` - Delete snapshot
|
||||
|
||||
## Key Data Models
|
||||
|
||||
### DBInstance
|
||||
```java
|
||||
String dbInstanceIdentifier() // Instance name
|
||||
String dbInstanceArn() // ARN identifier
|
||||
String engine() // Database engine
|
||||
String engineVersion() // Engine version
|
||||
String dbInstanceClass() // Instance type
|
||||
int allocatedStorage() // Storage size in GB
|
||||
Endpoint endpoint() // Connection endpoint
|
||||
String dbInstanceStatus() // Instance status
|
||||
boolean multiAZ() // Multi-AZ enabled
|
||||
boolean storageEncrypted() // Storage encrypted
|
||||
```
|
||||
|
||||
### DBParameter
|
||||
```java
|
||||
String parameterName() // Parameter name
|
||||
String parameterValue() // Parameter value
|
||||
String description() // Description
|
||||
int applyMethod() // Apply method (immediate/reboot)
|
||||
```
|
||||
|
||||
### CreateDbInstanceRequest Builder
|
||||
```java
|
||||
CreateDbInstanceRequest.builder()
|
||||
.dbInstanceIdentifier(identifier)
|
||||
.engine("postgres") // Database engine
|
||||
.engineVersion("15.2") // Engine version
|
||||
.dbInstanceClass("db.t3.micro") // Instance type
|
||||
.allocatedStorage(20) // Storage size
|
||||
.masterUsername(username) // Admin username
|
||||
.masterUserPassword(password) // Admin password
|
||||
.publiclyAccessible(false) // Public access
|
||||
.storageEncrypted(true) // Storage encryption
|
||||
.multiAZ(true) // High availability
|
||||
.backupRetentionPeriod(7) // Backup retention
|
||||
.deletionProtection(true) // Protection from deletion
|
||||
.build()
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common Exceptions
|
||||
- `DBInstanceNotFoundFault` - Instance doesn't exist
|
||||
- `DBSnapshotAlreadyExistsFault` - Snapshot name conflicts
|
||||
- `InsufficientDBInstanceCapacity` - Instance type unavailable
|
||||
- `InvalidParameterValueException` - Invalid configuration value
|
||||
- `StorageQuotaExceeded` - Storage limit reached
|
||||
|
||||
### Error Response Structure
|
||||
```java
|
||||
try {
|
||||
rdsClient.createDBInstance(request);
|
||||
} catch (RdsException e) {
|
||||
// AWS specific error handling
|
||||
String errorCode = e.awsErrorDetails().errorCode();
|
||||
String errorMessage = e.awsErrorDetails().errorMessage();
|
||||
|
||||
switch (errorCode) {
|
||||
case "DBInstanceNotFoundFault":
|
||||
// Handle missing instance
|
||||
break;
|
||||
case "InvalidParameterValueException":
|
||||
// Handle invalid parameters
|
||||
break;
|
||||
default:
|
||||
// Generic error handling
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Pagination Support
|
||||
|
||||
### List Instances with Pagination
|
||||
```java
|
||||
DescribeDbInstancesRequest request = DescribeDbInstancesRequest.builder()
|
||||
.maxResults(100) // Limit results per page
|
||||
.build();
|
||||
|
||||
String marker = null;
|
||||
do {
|
||||
if (marker != null) {
|
||||
request = request.toBuilder()
|
||||
.marker(marker)
|
||||
.build();
|
||||
}
|
||||
|
||||
DescribeDbInstancesResponse response = rdsClient.describeDBInstances(request);
|
||||
List<DBInstance> instances = response.dbInstances();
|
||||
|
||||
// Process instances
|
||||
|
||||
marker = response.marker();
|
||||
} while (marker != null);
|
||||
```
|
||||
@@ -0,0 +1,382 @@
|
||||
# AWS Lambda Integration with RDS
|
||||
|
||||
## Lambda RDS Connection Patterns
|
||||
|
||||
### 1. Traditional Lambda + RDS Connection
|
||||
|
||||
```java
|
||||
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
|
||||
|
||||
```java
|
||||
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
|
||||
|
||||
```java
|
||||
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
|
||||
|
||||
```java
|
||||
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:**
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```java
|
||||
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:**
|
||||
```java
|
||||
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:**
|
||||
```java
|
||||
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
|
||||
|
||||
```java
|
||||
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);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,325 @@
|
||||
# Spring Boot Integration with AWS RDS
|
||||
|
||||
## Configuration
|
||||
|
||||
### application.properties
|
||||
```properties
|
||||
# AWS Configuration
|
||||
aws.region=us-east-1
|
||||
aws.rds.instance-identifier=mydb-instance
|
||||
|
||||
# RDS Connection (from RDS endpoint)
|
||||
spring.datasource.url=jdbc:postgresql://mydb.abc123.us-east-1.rds.amazonaws.com:5432/mydatabase
|
||||
spring.datasource.username=admin
|
||||
spring.datasource.password=${DB_PASSWORD}
|
||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
||||
|
||||
# JPA Configuration
|
||||
spring.jpa.hibernate.ddl-auto=validate
|
||||
spring.jpa.show-sql=false
|
||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
||||
|
||||
# Connection Pool Configuration
|
||||
spring.datasource.hikari.maximum-pool-size=10
|
||||
spring.datasource.hikari.minimum-idle=5
|
||||
spring.datasource.hikari.idle-timeout=30000
|
||||
spring.datasource.hikari.connection-timeout=20000
|
||||
```
|
||||
|
||||
### AWS Configuration
|
||||
```java
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.rds.RdsClient;
|
||||
|
||||
@Configuration
|
||||
public class AwsRdsConfig {
|
||||
|
||||
@Value("${aws.region}")
|
||||
private String awsRegion;
|
||||
|
||||
@Bean
|
||||
public RdsClient rdsClient() {
|
||||
return RdsClient.builder()
|
||||
.region(Region.of(awsRegion))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Service Layer
|
||||
```java
|
||||
import org.springframework.stereotype.Service;
|
||||
import software.amazon.awssdk.services.rds.RdsClient;
|
||||
import software.amazon.awssdk.services.rds.model.*;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class RdsService {
|
||||
|
||||
private final RdsClient rdsClient;
|
||||
|
||||
public RdsService(RdsClient rdsClient) {
|
||||
this.rdsClient = rdsClient;
|
||||
}
|
||||
|
||||
public List<DBInstance> listInstances() {
|
||||
DescribeDbInstancesResponse response = rdsClient.describeDBInstances();
|
||||
return response.dbInstances();
|
||||
}
|
||||
|
||||
public DBInstance getInstanceDetails(String instanceId) {
|
||||
DescribeDbInstancesRequest request = DescribeDbInstancesRequest.builder()
|
||||
.dbInstanceIdentifier(instanceId)
|
||||
.build();
|
||||
|
||||
DescribeDbInstancesResponse response = rdsClient.describeDBInstances(request);
|
||||
return response.dbInstances().get(0);
|
||||
}
|
||||
|
||||
public String createSnapshot(String instanceId, String snapshotId) {
|
||||
CreateDbSnapshotRequest request = CreateDbSnapshotRequest.builder()
|
||||
.dbInstanceIdentifier(instanceId)
|
||||
.dbSnapshotIdentifier(snapshotId)
|
||||
.build();
|
||||
|
||||
CreateDbSnapshotResponse response = rdsClient.createDBSnapshot(request);
|
||||
return response.dbSnapshot().dbSnapshotArn();
|
||||
}
|
||||
|
||||
public void modifyInstance(String instanceId, String newInstanceClass) {
|
||||
ModifyDbInstanceRequest request = ModifyDbInstanceRequest.builder()
|
||||
.dbInstanceIdentifier(instanceId)
|
||||
.dbInstanceClass(newInstanceClass)
|
||||
.applyImmediately(true)
|
||||
.build();
|
||||
|
||||
rdsClient.modifyDBInstance(request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### REST Controller
|
||||
```java
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import software.amazon.awssdk.services.rds.model.DBInstance;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/rds")
|
||||
public class RdsController {
|
||||
|
||||
private final RdsService rdsService;
|
||||
|
||||
public RdsController(RdsService rdsService) {
|
||||
this.rdsService = rdsService;
|
||||
}
|
||||
|
||||
@GetMapping("/instances")
|
||||
public ResponseEntity<List<DBInstance>> listInstances() {
|
||||
return ResponseEntity.ok(rdsService.listInstances());
|
||||
}
|
||||
|
||||
@GetMapping("/instances/{id}")
|
||||
public ResponseEntity<DBInstance> getInstanceDetails(@PathVariable String id) {
|
||||
return ResponseEntity.ok(rdsService.getInstanceDetails(id));
|
||||
}
|
||||
|
||||
@PostMapping("/snapshots")
|
||||
public ResponseEntity<String> createSnapshot(
|
||||
@RequestParam String instanceId,
|
||||
@RequestParam String snapshotId) {
|
||||
String arn = rdsService.createSnapshot(instanceId, snapshotId);
|
||||
return ResponseEntity.ok(arn);
|
||||
}
|
||||
|
||||
@PutMapping("/instances/{id}")
|
||||
public ResponseEntity<String> modifyInstance(
|
||||
@PathVariable String id,
|
||||
@RequestParam String instanceClass) {
|
||||
rdsService.modifyInstance(id, instanceClass);
|
||||
return ResponseEntity.ok("Instance modified successfully");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Exception Handling
|
||||
```java
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class RdsExceptionHandler {
|
||||
|
||||
@ExceptionHandler(RdsException.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ErrorResponse handleRdsException(RdsException e) {
|
||||
return new ErrorResponse(
|
||||
"RDS_ERROR",
|
||||
e.getMessage(),
|
||||
e.awsErrorDetails().errorCode()
|
||||
);
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ErrorResponse handleGenericException(Exception e) {
|
||||
return new ErrorResponse(
|
||||
"INTERNAL_ERROR",
|
||||
e.getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ErrorResponse {
|
||||
private String code;
|
||||
private String message;
|
||||
private String details;
|
||||
|
||||
// Constructor, getters, setters
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RdsServiceTest {
|
||||
|
||||
@Mock
|
||||
private RdsClient rdsClient;
|
||||
|
||||
@Test
|
||||
void listInstances_shouldReturnInstances() {
|
||||
// Arrange
|
||||
DescribeDbInstancesResponse response = DescribeDbInstancesResponse.builder()
|
||||
.dbInstances(List.of(createTestInstance()))
|
||||
.build();
|
||||
|
||||
when(rdsClient.describeDBInstances()).thenReturn(response);
|
||||
|
||||
RdsService service = new RdsService(rdsClient);
|
||||
|
||||
// Act
|
||||
List<DBInstance> result = service.listInstances();
|
||||
|
||||
// Assert
|
||||
assertEquals(1, result.size());
|
||||
verify(rdsClient).describeDBInstances();
|
||||
}
|
||||
|
||||
private DBInstance createTestInstance() {
|
||||
return DBInstance.builder()
|
||||
.dbInstanceIdentifier("test-instance")
|
||||
.engine("postgres")
|
||||
.dbInstanceStatus("available")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@SpringBootTest
|
||||
@ActiveProfiles = "test"
|
||||
class RdsServiceIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private RdsService rdsService;
|
||||
|
||||
@Test
|
||||
void listInstances_integrationTest() {
|
||||
// This test requires actual AWS credentials and RDS instances
|
||||
// Should only run with proper test configuration
|
||||
assumeTrue(false, "Integration test disabled");
|
||||
|
||||
List<DBInstance> instances = rdsService.listInstances();
|
||||
assertNotNull(instances);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Configuration Management
|
||||
- Use Spring profiles for different environments
|
||||
- Externalize sensitive configuration (passwords, keys)
|
||||
- Use Spring Cloud Config for multi-environment management
|
||||
|
||||
### 2. Connection Pooling
|
||||
```properties
|
||||
# HikariCP Configuration
|
||||
spring.datasource.hikari.maximum-pool-size=20
|
||||
spring.datasource.hikari.minimum-idle=10
|
||||
spring.datasource.hikari.idle-timeout=600000
|
||||
spring.datasource.hikari.connection-timeout=30000
|
||||
spring.datasource.hikari.connection-test-query=SELECT 1
|
||||
```
|
||||
|
||||
### 3. Retry Logic
|
||||
```java
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
|
||||
@Service
|
||||
public class RdsServiceWithRetry {
|
||||
|
||||
private final RdsClient rdsClient;
|
||||
|
||||
@Retryable(value = { RdsException.class },
|
||||
maxAttempts = 3,
|
||||
backoff = @Backoff(delay = 1000))
|
||||
public List<DBInstance> listInstancesWithRetry() {
|
||||
return rdsClient.describeDBInstances().dbInstances();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Monitoring
|
||||
```java
|
||||
import org.springframework.boot.actuator.health.Health;
|
||||
import org.springframework.boot.actuator.health.HealthIndicator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class RdsHealthIndicator implements HealthIndicator {
|
||||
|
||||
private final RdsClient rdsClient;
|
||||
|
||||
public RdsHealthIndicator(RdsClient rdsClient) {
|
||||
this.rdsClient = rdsClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Health health() {
|
||||
try {
|
||||
rdsClient.describeDBInstances();
|
||||
return Health.up()
|
||||
.withDetail("service", "RDS")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
return Health.down()
|
||||
.withDetail("error", e.getMessage())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user