# Design Doc: RDS Proxy para Lambda + RDS sem derreter o banco

Funções Lambda em alta concorrência abrem centenas de conexões diretas ao RDS, esgotando o pool e derrubando o banco. Este documento propõe o uso do RDS Proxy como camada de multiplexação, detalha armadilhas reais de pinning, compara alternativas como Data API e poolers na aplicação, e define quando o Proxy não é a resposta certa.

- URL: https://fernando.moretes.com/studies/design-doc-rds-proxy-pooling-serverless

- Markdown: https://fernando.moretes.com/studies/design-doc-rds-proxy-pooling-serverless/study.md?lang=pt

- Type: Design Doc / RFC

- Company: Serverless + RDS (cenário)

- Domain: Dados

- Date: 2026-02-15

- Tags: rds-proxy, lambda, serverless, connection-pooling, aws-rds, data-platform, iam-auth, postgresql

- Reading time: 12 min

---

Cada invocação Lambda abre sua própria conexão com o banco. Em produção, isso não é um detalhe — é uma bomba-relógio. Este RFC define a arquitetura correta para workloads serverless sobre RDS, com honestidade sobre custos, armadilhas e os cenários em que o RDS Proxy não resolve.

## O Problema: Connection Storms em Ambientes Serverless

O modelo de execução do Lambda é fundamentalmente incompatível com o modelo de conexão tradicional de bancos relacionais. Um processo de aplicação convencional — seja um contêiner ou uma VM — inicializa um pool de conexões uma única vez na subida do processo e reutiliza essas conexões ao longo de toda a sua vida útil. O banco dimensiona seu `max_connections` em função do número de instâncias de aplicação, que cresce de forma previsível e controlada.

O Lambda quebra essa premissa. Cada execution environment (o sandbox isolado que executa uma função) mantém, no máximo, uma conexão com o banco. Quando a concorrência escala de 10 para 500 invocações simultâneas em questão de segundos — comportamento completamente normal em picos de tráfego ou em processamento de filas SQS com batch size alto — o banco recebe 490 novas conexões em rajada. Isso é o que a literatura chama de *connection storm*.

O PostgreSQL, por exemplo, aloca memória por conexão (tipicamente 5–10 MB por processo worker). Uma instância `db.t3.medium` tem 2 GB de RAM e um `max_connections` padrão de cerca de 170. Com Lambda em alta concorrência, esse limite é atingido em segundos. O banco começa a rejeitar novas conexões com `FATAL: remaining connection slots are reserved`, queries legítimas falham, e o ciclo de retry do Lambda agrava o problema — cada retry tenta abrir mais uma conexão contra um banco já saturado.

O problema se repete durante cold starts: quando um novo execution environment é inicializado, ele precisa estabelecer o handshake TCP, o handshake TLS e o protocolo de autenticação do banco antes de executar a primeira query. Em PostgreSQL com SSL obrigatório, esse overhead pode somar 50–150ms só de setup de conexão, adicionados à latência percebida pelo usuário final. Em cenários de failover — quando o RDS promove a réplica para primária — todas as conexões existentes são derrubadas simultaneamente, e o Lambda tenta reconectar em massa, criando um segundo storm exatamente no momento em que o banco está mais vulnerável.

## A Solução Proposta: RDS Proxy como Camada de Multiplexação

O RDS Proxy é um proxy gerenciado pela AWS que fica entre os clientes (Lambda, ECS, EC2) e a instância RDS. Ele mantém um pool de conexões persistentes e autenticadas com o banco, e multiplexa as conexões dos clientes sobre esse pool menor. Do ponto de vista do banco, ele enxerga apenas as conexões do Proxy — um número fixo e controlado. Do ponto de vista do Lambda, ele enxerga um endpoint que aceita conexões como se fosse o banco diretamente.

O mecanismo central é o *connection multiplexing*: quando uma invocação Lambda termina sua transação e libera a conexão, o Proxy não fecha essa conexão com o banco — ele a devolve ao pool e a reutiliza para a próxima invocação que precisar. Isso reduz drasticamente o número de conexões ativas no banco. Em um cenário com 500 Lambdas simultâneos, o banco pode ver apenas 20–50 conexões vindas do Proxy, dependendo da duração das transações e do padrão de acesso.

Além do pooling, o Proxy oferece dois benefícios operacionais críticos: **failover acelerado** e **autenticação centralizada**. No failover, o Proxy detecta a indisponibilidade da instância primária e redireciona as conexões para a nova primária em aproximadamente 20–30 segundos — significativamente mais rápido do que o tempo que levaria para o DNS propagar a mudança e para todas as instâncias Lambda reconectarem individualmente. As conexões do pool do Proxy com o banco são restabelecidas internamente, e os clientes que usam o endpoint do Proxy experimentam uma interrupção muito menor.

Para autenticação, o Proxy suporta IAM Authentication: em vez de armazenar credenciais de banco no código ou em variáveis de ambiente, a função Lambda assume uma IAM Role e obtém um token temporário (válido por 15 minutos) via `generate_db_auth_token`. O Proxy valida esse token contra o IAM e autentica a conexão no banco usando as credenciais armazenadas no Secrets Manager — que ele rotaciona automaticamente sem interromper as conexões existentes do pool. Isso elimina a classe inteira de vulnerabilidades de credenciais hardcoded e simplifica a rotação de senhas em produção.

A configuração mínima viável envolve: criar o Proxy no mesmo VPC da instância RDS, associar um secret do Secrets Manager com as credenciais do banco, criar um IAM Policy que permite `rds-db:connect` para o ARN do Proxy, e alterar o endpoint da aplicação Lambda de `rds-instance-endpoint` para `proxy-endpoint`. O Proxy é transparente para o driver de banco — o código da aplicação não muda.

## Arquitetura: Lambda + RDS Proxy + RDS (fluxo de conexão e failover)

Fluxo de conexão multiplexada entre invocações Lambda concorrentes e o banco RDS via Proxy, incluindo autenticação IAM, Secrets Manager e comportamento de failover Multi-AZ.

### ⚡ Lambda Tier

- Lambda Fn env #1 (compute)
- Lambda Fn env #2 (compute)
- Lambda Fn env #N (concorrente) (compute)

### 🔐 Auth / Secrets

- IAM rds-db:connect token (security)
- Secrets Manager DB credentials (auto-rotation) (security)

### 🔀 Proxy Layer

- RDS Proxy Connection Pool (multiplexing) (network)

### 🗄️ RDS Multi-AZ

- RDS Primary (AZ-a) (data)
- RDS Standby (AZ-b) [failover target] (data)

### Fluxos

- lambda1 -> iam: gera token IAM
- lambda2 -> iam: gera token IAM
- lambdaN -> iam: gera token IAM
- lambda1 -> rdsproxy: conexão TCP/TLS
- lambda2 -> rdsproxy: conexão TCP/TLS
- lambdaN -> rdsproxy: conexão TCP/TLS
- rdsproxy -> secretsmanager: busca credenciais
- rdsproxy -> rds_primary: pool persistente
(N conexões << concorrência Lambda)
- rds_primary -> rds_standby: replicação síncrona
- rdsproxy -> rds_standby: failover automático
(~20-30s)

## Armadilhas Reais: Pinning e Outros Comportamentos Contraintuitivos

O RDS Proxy não é uma bala de prata. O benefício de multiplexação depende de uma premissa crítica: que o Proxy consiga reutilizar conexões entre diferentes clientes. Quando ele não consegue — quando uma conexão fica presa a um cliente específico — isso se chama **pinning**, e é a principal causa de frustração com o Proxy em produção.

O Proxy faz pinning de uma conexão quando detecta que o estado da sessão foi modificado de forma que não pode ser compartilhado com outro cliente. Os casos mais comuns são:

**1. Transações abertas ou longas:** Qualquer `BEGIN` sem `COMMIT`/`ROLLBACK` correspondente mantém a conexão pinada até o fim da transação. Isso é esperado e correto — o Proxy não pode reutilizar uma conexão no meio de uma transação. O problema ocorre quando a aplicação usa transações longas (processamento de batch, operações de múltiplos passos) ou, pior, quando o ORM abre uma transação implicitamente e não a fecha corretamente em caso de erro.

**2. Estado de sessão com `SET` statements:** Comandos como `SET search_path`, `SET TIME ZONE`, `SET LOCAL`, variáveis de sessão do MySQL (`SET @variable`) e configurações de `pg_catalog` modificam o estado da conexão. O Proxy detecta esses comandos e pina a conexão, porque não pode garantir que outro cliente receba uma conexão com o mesmo estado. Isso é especialmente problemático com ORMs que emitem `SET` statements automaticamente na inicialização da sessão.

**3. Prepared statements:** No PostgreSQL, prepared statements nomeados (`PREPARE stmt AS ...`) são estado de sessão e causam pinning. Prepared statements sem nome (o padrão da maioria dos drivers modernos) são tratados diferentemente e geralmente não causam pinning.

**4. Funções e procedimentos que alteram estado de sessão:** Qualquer stored procedure que execute `SET` internamente pode causar pinning sem que o código da aplicação seja o responsável direto.

A consequência prática do pinning é que o número de conexões no banco se aproxima do número de Lambdas concorrentes — exatamente o problema que o Proxy deveria resolver. Para diagnosticar, o CloudWatch expõe a métrica `DatabaseConnectionsCurrentlySessionPinned`. Se essa métrica for consistentemente alta (acima de 20–30% das conexões), o benefício do Proxy está sendo desperdiçado.

A solução para pinning passa por: (1) garantir que transações sejam curtas e sempre fechadas explicitamente, (2) mover configurações de sessão para a string de conexão ou para o nível de banco/role em vez de `SET` statements em runtime, (3) auditar o ORM para entender quais statements ele emite automaticamente, e (4) usar o modo `TRANSACTION` do Proxy (padrão) em vez de `SESSION` apenas quando o padrão de acesso é realmente transacional e stateless.

Há também uma limitação menos óbvia: o RDS Proxy **não suporta todas as funcionalidades do banco**. Para PostgreSQL, não há suporte a `pg_notify`/`LISTEN`, replicação lógica, ou conexões via `pg_hba.conf` com métodos de autenticação não suportados. Para MySQL, há restrições em multi-statements e alguns comandos administrativos. Antes de adotar o Proxy em um sistema existente, é necessário auditar o uso dessas features.

## Objetivos e Não-Objetivos deste RFC

- ✅ OBJETIVO: Eliminar connection storms em workloads Lambda com alta concorrência sobre RDS PostgreSQL ou MySQL
- ✅ OBJETIVO: Reduzir o número de conexões ativas no banco para um valor fixo e controlado, independente da concorrência Lambda
- ✅ OBJETIVO: Implementar autenticação sem credenciais hardcoded via IAM Auth + Secrets Manager com rotação automática
- ✅ OBJETIVO: Reduzir o impacto de failovers Multi-AZ nas funções Lambda em execução
- ✅ OBJETIVO: Definir quando o RDS Proxy NÃO é a solução adequada e quais alternativas usar
- ❌ NÃO-OBJETIVO: Substituir RDS por Aurora Serverless v2 ou DynamoDB — essa decisão está fora do escopo

## Referência Rápida: Cenário e Stack

- **Cenário:** Serverless (Lambda) + RDS — padrão comum em APIs, processamento de eventos, ETL leve
- **Bancos suportados:** RDS MySQL 5.6/5.7/8.0, RDS PostgreSQL 10.x–16.x, Aurora MySQL, Aurora PostgreSQL
- **Concorrência Lambda típica (problema):** 100–1000 invocações simultâneas abrindo conexões diretas ao RDS
- **max_connections típico (db.t3.medium PostgreSQL):** ~170 conexões (limitado por RAM: ~5-10 MB/conexão)
- **Custo do RDS Proxy:** $0.015/vCPU-hora da instância RDS associada (estimativa: ~$11/mês para db.t3.medium)
- **Redução de conexões no banco (estimativa):** 60–90% menos conexões ativas no banco vs. Lambda direto (sem pinning)
- **Failover com Proxy vs. sem Proxy:** ~20–30s (Proxy) vs. ~60–120s (DNS propagation + reconnect storm)
- **Stack de autenticação:** IAM Role → generate_db_auth_token (token 15min) → RDS Proxy → Secrets Manager → RDS

## Alternativas: RDS Proxy vs. Data API vs. Pooler na Aplicação

### RDS Proxy (proposta)

**Pros**
- Multiplexação transparente — zero mudança no código da aplicação
- IAM Auth + rotação automática de credenciais via Secrets Manager
- Failover acelerado (~20–30s) com reconexão gerenciada pelo Proxy
- Totalmente gerenciado — sem operação de infraestrutura adicional

**Cons**
- Custo adicional (~$0.015/vCPU-hora) — não justificado para workloads de baixa concorrência
- Pinning pode anular o benefício se a aplicação usar estado de sessão ou transações longas
- Latência adicional de ~1ms por query (overhead do proxy hop)
- Não suporta todas as features do banco (LISTEN/NOTIFY, replicação lógica)

**Verdict:** Escolha correta para Lambda com alta concorrência, transações curtas e padrão stateless

### RDS Data API (Aurora Serverless v1/v2)

**Pros**
- HTTP/HTTPS — sem conexão TCP persistente, completamente serverless
- Sem gerenciamento de pool ou pinning — cada chamada é stateless por design
- Ideal para Lambda com concorrência extremamente alta e queries simples

**Cons**
- Disponível apenas para Aurora Serverless — não funciona com RDS padrão
- Latência significativamente maior por query (~5–20ms overhead vs. ~1ms do Proxy)
- Sem suporte a transações longas ou streaming de resultados grandes
- Custo de Aurora Serverless v2 pode ser maior que RDS + Proxy para workloads estáveis

**Verdict:** Válido se já usar Aurora Serverless e o padrão for queries simples e stateless. Não migrar RDS para Aurora só por isso.

### Pooler na Aplicação (PgBouncer / HikariCP via Lambda Layer)

**Pros**
- Controle total sobre configuração do pool (timeouts, tamanho, modo de pooling)
- Sem custo adicional de infraestrutura além do Lambda
- PgBouncer em modo transaction pooling pode ser mais eficiente que RDS Proxy em cenários específicos

**Cons**
- Pool por execution environment Lambda — cada sandbox tem seu próprio pool, sem compartilhamento entre invocações paralelas
- Não resolve o problema fundamental: 500 Lambdas = 500 pools independentes
- PgBouncer como sidecar requer infraestrutura adicional (ECS, EC2) — não é serverless
- Operação e manutenção da infraestrutura de proxy recai sobre o time

**Verdict:** Não resolve o connection storm em Lambda. Válido apenas se o pooler for externo (não no Lambda) e gerenciado como serviço dedicado.

## Decisão: Adotar RDS Proxy com IAM Auth para workloads Lambda + RDS

**Status:** proposed

**Contexto**

Workload Lambda com concorrência > 50 invocações simultâneas acessando RDS PostgreSQL/MySQL. Conexões diretas esgotam max_connections em picos. Credenciais de banco armazenadas em variáveis de ambiente sem rotação automática.

**Decisão**

Introduzir RDS Proxy entre Lambda e RDS. Configurar IAM Authentication no Proxy. Migrar credenciais para Secrets Manager com rotação automática de 30 dias. Monitorar DatabaseConnectionsCurrentlySessionPinned no CloudWatch.

**Consequências**
- Custo adicional de ~$0.015/vCPU-hora do RDS — justificado pela eliminação de outages por connection storm
- Necessidade de auditar o ORM para identificar SET statements e transações implícitas que causam pinning
- Lambda precisa de permissão IAM explícita (rds-db:connect) e acesso de rede ao Proxy no mesmo VPC
- Endpoint da aplicação muda de RDS direto para Proxy endpoint — mudança de configuração, não de código

## Plano de Rollout

1. **Fase 0 — Auditoria (Semana 1)** — Mapear todas as funções Lambda que acessam RDS. Identificar ORMs e drivers em uso. Auditar SET statements automáticos, uso de transações longas, LISTEN/NOTIFY e prepared statements nomeados. Medir max_connections atual e pico de DatabaseConnections no CloudWatch. Definir baseline de latência P50/P99 das queries.

2. **Fase 1 — Infraestrutura do Proxy (Semana 2)** — Criar secret no Secrets Manager com credenciais do banco. Criar RDS Proxy no mesmo VPC da instância RDS, associando o secret. Configurar Security Groups: Lambda SG → Proxy SG (porta 5432/3306), Proxy SG → RDS SG (porta 5432/3306). Criar IAM Policy com rds-db:connect para o ARN do Proxy e anexar à Lambda Execution Role. Testar conectividade do Proxy ao banco via console AWS.

3. **Fase 2 — Validação em Staging (Semana 3)** — Alterar variável de ambiente DB_HOST nas funções Lambda de staging para o Proxy endpoint. Executar testes de carga simulando pico de concorrência (100–500 invocações simultâneas). Monitorar DatabaseConnectionsCurrentlySessionPinned, DatabaseConnections, ClientConnections no CloudWatch. Validar que o número de conexões no banco não excede o target definido. Medir latência P99 — overhead do Proxy deve ser < 2ms em condições normais.

4. **Fase 3 — Rollout Gradual em Produção (Semana 4)** — Usar Lambda aliases e weighted routing para migrar 10% do tráfego para o endpoint do Proxy. Monitorar métricas por 24h. Escalar para 50%, depois 100% se métricas estiverem dentro do baseline. Manter o endpoint direto ao RDS como fallback por 2 semanas antes de descomissionar. Configurar alarme CloudWatch em DatabaseConnectionsCurrentlySessionPinned > 30% como sinal de regressão.

5. **Fase 4 — Rotação de Credenciais e Hardening (Semana 5)** — Ativar rotação automática de 30 dias no Secrets Manager. Remover variáveis de ambiente com credenciais hardcoded das funções Lambda. Configurar RDS Proxy com require_tls=true. Revisar IAM Policies para mínimo privilégio: rds-db:connect apenas para o Proxy ARN específico, não wildcard. Documentar runbook de troubleshooting de pinning para o time de operações.

> **Riscos e Quando NÃO Usar o RDS Proxy:** **Risco 1 — Pinning silencioso:** O maior risco operacional é o pinning não detectado. Se o ORM emite SET statements na inicialização da sessão (SQLAlchemy, Hibernate, Sequelize fazem isso), o Proxy pina cada conexão e o benefício de multiplexação desaparece. A métrica DatabaseConnectionsCurrentlySessionPinned precisa estar em um dashboard de observabilidade desde o dia 1, não como afterthought.

**Risco 2 — Custo em workloads de baixa concorrência:** Para funções Lambda com concorrência < 20–30 invocações simultâneas, o custo do Proxy (~$11/mês para db.t3.medium) pode não ser justificado. Nesses casos, aumentar max_connections via parâmetro de instância ou migrar para uma instância maior pode ser suficiente.

**Risco 3 — Transações longas:** Workloads que processam batches grandes dentro de uma única transação (ETL, migrações, relatórios) manterão a conexão pinada durante toda a duração da transação. O Proxy não ajuda nesses casos — e pode até aumentar a latência pelo hop adicional. Para esses padrões, considerar execução fora do Lambda (ECS Fargate, Glue) ou quebrar as transações em unidades menores.

**Risco 4 — LISTEN/NOTIFY e replicação lógica:** Se o sistema usa PostgreSQL LISTEN/NOTIFY para pub/sub ou replicação lógica para CDC (Change Data Capture), o Proxy não suporta essas features. Conexões para esses fins devem ir diretamente ao banco, e a arquitetura precisa contemplar dois endpoints distintos.

**Risco 5 — Latência de cold start do Proxy:** O próprio Proxy tem um tempo de inicialização quando criado. Em ambientes de desenvolvimento/staging que são criados e destruídos frequentemente, o overhead de provisionar o Proxy pode ser relevante. Em produção, o Proxy é persistente e esse risco não se aplica.

## Avaliação: AWS Well-Architected Framework

- **security**: IAM Authentication elimina credenciais hardcoded. Secrets Manager com rotação automática reduz a janela de exposição de credenciais comprometidas. Security Groups com least-privilege network access entre Lambda, Proxy e RDS. TLS obrigatório em todas as conexões.
- **reliability**: Failover Multi-AZ acelerado (~20–30s vs. ~60–120s sem Proxy). Pool persistente do Proxy absorve picos de reconexão. Eliminação de connection storms que causavam degradação em cascata.

> **Minha Perspectiva: O Proxy é Correto, mas Não é Mágica:** Depois de lidar com sistemas financeiros onde um connection storm às 9h da manhã significava transações recusadas e clientes ligando para a central, aprendi a respeitar o problema de conexão com banco em ambientes serverless. O RDS Proxy é a solução certa para o problema certo — e é exatamente aqui que a maioria dos times erra: adotam o Proxy sem entender o mecanismo de pinning, e ficam surpresos quando o número de conexões no banco não cai como esperado.

Minha recomendação prática: antes de criar o Proxy, passe uma tarde auditando o que o seu ORM faz ao inicializar uma sessão. SQLAlchemy com `schema_translate_map`, Hibernate com `SET search_path`, Sequelize com timezone configuration — todos emitem SET statements que causam pinning. Isso não é um bug do Proxy; é uma característica documentada. A solução geralmente é mover essas configurações para o nível de role/database no PostgreSQL (`ALTER ROLE app_user SET search_path = myschema`) em vez de fazer no runtime da aplicação.

Sobre o custo: para uma instância db.t3.medium, o Proxy custa ~$11/mês. Se você está tendo connection storms que causam degradação ou outages, esse custo é trivial. Se você está com < 30 invocações simultâneas e nunca viu max_connections sendo atingido, não precisa do Proxy — não adicione complexidade sem problema real para resolver.

O benefício de IAM Auth + Secrets Manager é, na minha opinião, frequentemente subestimado. Em auditorias de segurança de sistemas financeiros, credenciais de banco hardcoded em variáveis de ambiente são um achado recorrente e crítico. O Proxy resolve isso de forma elegante e sem mudança de código na aplicação. Mesmo que o connection pooling não fosse necessário, eu consideraria o Proxy só pelo benefício de segurança em sistemas que lidam com dados sensíveis.

Por fim: se o seu padrão de acesso inclui transações longas, processamento de batch ou LISTEN/NOTIFY, o Lambda não é o compute certo para esse workload — e o Proxy não vai salvar uma arquitetura fundamentalmente equivocada. Nesses casos, ECS Fargate com PgBouncer externo ou Aurora Serverless v2 com Data API são escolhas mais honestas.

## Métricas de Sucesso e Targets

- **DatabaseConnections (RDS) em pico:** Redução de ≥ 60% vs. baseline pré-Proxy (sem pinning)
- **DatabaseConnectionsCurrentlySessionPinned:** < 20% das conexões ativas — alarme em > 30%
- **Erros de conexão Lambda (FATAL: remaining connection slots):** Zero ocorrências em produção após rollout completo
- **Latência P99 de queries (overhead do Proxy):** < 2ms de overhead adicional vs. conexão direta em condições normais
- **Tempo de recuperação em failover Multi-AZ:** < 35s de interrupção percebida pelo Lambda (target: < 30s)
- **Credenciais hardcoded em variáveis de ambiente Lambda:** Zero — 100% migrado para IAM Auth + Secrets Manager

## Veredicto

O RDS Proxy é a solução correta para o problema de connection storm em workloads Lambda de alta concorrência sobre RDS. A proposta está aprovada com as seguintes condições: (1) a auditoria de pinning na Fase 0 é obrigatória — não opcional — antes do rollout em produção; (2) a métrica DatabaseConnectionsCurrentlySessionPinned deve estar em dashboard de observabilidade desde o primeiro dia; (3) o custo adicional é justificado apenas para concorrência > 30 invocações simultâneas com padrão de acesso transacional e stateless.

Para workloads com transações longas, processamento de batch ou uso de LISTEN/NOTIFY, o Proxy não é a solução — o problema está na escolha do compute, não na camada de conexão. Nesses casos, mover o workload para ECS Fargate ou usar Aurora Serverless v2 com Data API são as alternativas corretas.

O benefício de segurança do IAM Auth + Secrets Manager é independente do benefício de pooling e deve ser adotado em qualquer sistema que lide com dados sensíveis, independente do nível de concorrência. Credenciais hardcoded em variáveis de ambiente Lambda são um risco de segurança inaceitável em sistemas de produção.

## Referências

- [Amazon RDS Proxy — AWS Documentation](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html)
- [Using Amazon RDS Proxy with AWS Lambda — AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/services-rds-tutorial.html)

## Fontes do caso

- [Amazon RDS Proxy — AWS docs](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html)
- [AWS — Using RDS Proxy with Lambda](https://docs.aws.amazon.com/lambda/latest/dg/services-rds-tutorial.html)
