Amazon RDS Proxy Via Terraform

Stabilizing resource utilization for more effective scaling

Introduction

When dealing with Lambda-based serverless applications that require connectivity to a backend database, it can be challenging to reconcile the difference in architecture between your storage solution and the nature of Lambda. Being serverless means that Lambda can scale aggressively to meet demand, while your database architecture may lag behind this curve – especially if you have budget limitations. Amazon RDS Proxy can help stabilize resource utilization by your RDS database instances so you can more effectively scale with your application’s throughput needs as they evolve quickly.

RDS Connection Proxy

Benefits of Amazon RDS Proxy

Lifted straight from the AWS fact sheet, RDS Proxy reduces strain on your database instance(s) by acting as an intermediate connection pool between your application client and your RDS instance. When accepting incoming client connections, database backends like PostgreSQL or MySQL will have to devote a small amount of CPU and memory to establish the connection and begin communications. If your clients connect to an RDS Proxy instance instead of directly to the RDS database instance, you’re offloading that work to the proxy, which is actively managing a pool of database connections for you. Under-used connections are reused for new, incoming connections, and can be configured to manage a portion of all connections for you – or just a portion. Additionally, the service is multi-AZ compatible, meaning your infrastructure becomes much more agile with respect to handling unexpected outages in AWS regions. If your RDS database instance(s) are similarly configured, you can expect shorter failover times.

Example Terraform Configuration

While working on a customer-facing retail application, we noticed that marketing messages would cause a significant upward skew of CPU utilization on our RDS database cluster. Through CloudWatch alarms and metrics, we noticed that our RDS instance size was having trouble meeting the sudden demand from our flood of incoming Lambda-originated connection requests. After looking into RDS Proxy, our team spent a very small amount of time adding the necessary resources and changes into our Terraform-based configuration:

Step 1: Create the RDS Proxy Resource

resource "aws_db_proxy" "rds_db_proxy" {
  name = "rds-connection-proxy"
  engine_family = "POSTGRESQL"
  idle_client_timeout = 900
  require_tls = true
  role_arn = "arn:aws:iam::0000000000:role/RdsProxyRole"
  vpc_security_group_ids = ["sg-0000000000abcdef"]
  vpc_subnet_ids = ["subnet-0000000000abcdef"]

  auth {
    auth_scheme = "SECRETS"
    iam_auth = "DISABLED"
    secret_arn = "arn:aws:secretsmanager:us-east-1:0000000000:secret:rds-secrets-arn"
  }
}

Initially, we set up the main RDS Proxy resource, which contains basic information such as:

  • The human-readable name of our proxy
  • What database engine it will connect to (currently either MYSQL or POSTGRESQL)
  • How the proxy will retrieve the appropriate credentials (including the appropriate secret credentials ARN), and what the RDS Proxy’s IAM role will be

In our example, we’re using a simple secrets-based authentication approach, where the RDS Proxy will retrieve database credentials via Secrets Manager. This means that the role_arn used by the RDS Proxy will need a policy that allows both RDS and Secrets Manager access.

Another important note is to ensure your RDS Proxy is using a security group that will be allowed to connect to your RDS database instance.

Step 2: Create the Proxy Target Group and Proxy Target

resource "aws_db_proxy_default_target_group" "rds_db_proxy_target_group" {
  db_proxy_name = aws_db_proxy.rds_db_proxy.name

  connection_pool_config {
    connection_borrow_timeout = 120
    max_connections_percent = 100
  }
}

resource "aws_db_proxy_target" "mobile_db_proxy_target" {
  db_cluster_identifier = aws_rds_cluster.db_cluster.id
  db_proxy_name = aws_db_proxy.rds_db_proxy.name
  target_group_name = aws_db_proxy_default_target_group.rds_db_proxy_target_group.name

Next, we need to create the default target group and proxy target – these resources work together to provide information about how and where our RDS Proxy will send connections.

One notable callout in the above configuration is that you can scale the percentage of connections your RDS Proxy will handle. You could theoretically set this to a value lower than 100, which could give you failsafe capacity via a direct connection, for example – should some cataclysmic event occur that would otherwise use all available connection slots.

Step 3: Client Connection Configuration

Finally, in our example we’ll set an environment variable on a Lambda function instance to provide our application code with a dynamic reference to the new database host URL that points to our RDS Proxy instead of directly to the RDS Cluster:

resource "aws_lambda_function" "app_lambda" {
  ...
  environment {
    variables = {
      RDS_HOSTNAME = aws_db_proxy.rds_db_proxy.endpoint
      ...
    }
  }
}

Tracking Measurable Benefits

By implementing an RDS Proxy, we immediately noticed a more stable usage of RDS resources. We’re now able to handle thousands of incoming requests without breaking a sweat. This particular app typically performs around 1 million Lambda invocations every hour, with periodic concurrent executions topping out at ~3,500 – and our RDS cluster doesn’t even break a sweat. The connection count from the RDS Proxy now maxes out at 51 total connections.

Aurora RDS

Further Reading

References for RDS Proxy and Terraform that are relevant:

Introducing the JBS Quick Launch Lab!

FREE 1/2 Day Assessment

Quantify what it will take to implement your next big idea! Our intensive 1/2 day session will deliver tangible timelines, costs, high-level requirements, and recommend architectures that will work best, and all for FREE. Let JBS show you why over 20 years of experience matters.
Yes, I'd Like A FREE Assessment