Las fotos de MercadoFresco ya están en S3, con su ciclo de vida y su versionado. Pero el corazón del
negocio sigue en el servidor de la oficina: la base de datos de pedidos. Ahí están los clientes,
los productos, las cestas, las rutas de reparto y la facturación. Es el dato que no se puede perder,
el que no puede estar caído los viernes a las 19:00, y el que hoy se respalda con un pg_dump a un
disco externo que nadie ha probado restaurar nunca.
Esta lección migra esa base de datos a Amazon RDS (Relational Database Service), el servicio gestionado de bases de datos relacionales de AWS. No es solo "PostgreSQL en la nube": es delegar en AWS el parcheo, las copias, la conmutación por error y la replicación, tareas que en MercadoFresco nadie hacía porque nadie tenía tiempo. Y con las copias automáticas y la restauración a un punto en el tiempo cerramos definitivamente el problema 2, la parte que quedó abierta en 02-02 cuando resolvimos las copias de los discos pero no las de los datos transaccionales.
Contenido
- Qué es un servicio gestionado y qué deja de ser tu problema
- RDS frente a PostgreSQL instalado en una EC2
- Motores disponibles y cuál elige MercadoFresco
- Crear la instancia RDS por consola
- Crear la instancia RDS por CLI
- Multi-AZ: qué es realmente una réplica síncrona en espera
- Réplicas de lectura: los informes de Sara sin castigar producción
- Copias automáticas, retención y restauración a un punto en el tiempo
- Snapshots manuales y el cierre del problema 2
- Grupos de parámetros y ventanas de mantenimiento
- Monitorización: métricas básicas y Performance Insights
- Conectarse desde la aplicación sin credenciales en el código
- Escalado vertical y almacenamiento autoescalable
- Migrar desde el servidor de la oficina
- Costes y cómo parar una instancia de pruebas
Qué es un servicio gestionado y qué deja de ser tu problema
En el servidor de la oficina, PostgreSQL era responsabilidad íntegra de Marta: instalarlo, configurarlo, actualizarlo, vigilarlo, respaldarlo y levantarlo cuando se caía. En RDS, AWS asume una parte concreta y bien delimitada de ese trabajo.
| Tarea | Servidor de la oficina | PostgreSQL en EC2 | Amazon RDS |
|---|---|---|---|
| Comprar y mantener hardware | Marta | AWS | AWS |
| Instalar el sistema operativo | Marta | Tú | AWS |
| Parchear el sistema operativo | Marta (nunca) | Tú | AWS |
| Instalar PostgreSQL | Marta | Tú | AWS |
| Aplicar parches de PostgreSQL | Marta (nunca) | Tú | AWS (en la ventana de mantenimiento) |
| Configurar copias automáticas | Marta (a medias) | Tú | AWS |
| Probar la restauración | Nadie | Tú | AWS proporciona PITR |
| Montar alta disponibilidad | Imposible | Tú (compleja) | AWS (una casilla) |
| Conmutación por error automática | No existe | Tú | AWS (1-2 minutos) |
| Crear réplicas de lectura | Muy laborioso | Tú | AWS (un comando) |
| Cifrado en reposo | No | Tú | AWS (una casilla) |
| Diseñar el esquema y las consultas | Marta y Luis | Tú | Tú |
| Optimizar índices | Marta y Luis | Tú | Tú |
| Seguridad de la aplicación | Marta y Luis | Tú | Tú |
Es el modelo de responsabilidad compartida de la lección 01-01 aplicado a un servicio concreto: la línea se mueve hacia arriba, pero nunca desaparece. RDS no va a arreglar una consulta sin índice ni va a diseñar tus tablas.
Lo que RDS no te deja hacer, y conviene saberlo antes de elegirlo:
- No hay acceso al sistema operativo. Nada de SSH a la máquina de la base de datos. Si tu operativa depende de scripts en el servidor, hay que replantearla.
- No hay superusuario real. El usuario maestro tiene el rol
rds_superuser, potente pero limitado: no puede hacer todo lo que hace unsuperuserde PostgreSQL. - Las extensiones están en lista blanca. Solo puedes instalar las que AWS admite (que son muchas: PostGIS, pg_stat_statements, pgvector…).
- Las versiones tienen calendario de fin de soporte y AWS acabará actualizándote.
RDS frente a PostgreSQL instalado en una EC2
La pregunta legítima de Luis es: "podríamos instalar PostgreSQL en una EC2 y sale más barato, ¿no?". La respuesta honesta es que el precio por hora es menor, y el coste total casi nunca.
| Criterio | PostgreSQL en EC2 | Amazon RDS |
|---|---|---|
| Coste por hora | Menor (solo la instancia + EBS) | ~25-40 % más caro por el servicio gestionado |
| Tiempo de puesta en marcha | Horas o días | 10 minutos |
| Alta disponibilidad | Montar Patroni/repmgr a mano | Casilla Multi-AZ |
| Copias | Scripts propios que hay que mantener | Automáticas, con PITR al segundo |
| Parches | Tu calendario, tu responsabilidad | Ventana de mantenimiento |
| Acceso al SO | Sí, control total | No |
| Extensiones exóticas | Cualquiera | Solo las admitidas |
| Versiones muy antiguas o muy nuevas | Cualquiera | Las que ofrezca AWS |
| Coste de personal | Alto y continuo | Bajo |
Elige EC2 solo si necesitas control del sistema operativo, una extensión no admitida, una versión concreta fuera del catálogo, o si tienes un equipo de administradores de bases de datos que ya hace ese trabajo. Para MercadoFresco, que tiene a Marta a media jornada para todo, RDS no es una opción: es la única opción sensata.
El cálculo que zanja la discusión: una db.t3.small en eu-west-1 cuesta alrededor de 30 USD al
mes frente a unos 20 USD de la EC2 equivalente. Diez dólares de diferencia. Una sola incidencia
nocturna de PostgreSQL al año, con Marta despierta a las 3:00, cuesta más que eso.
Motores disponibles y cuál elige MercadoFresco
| Motor | Cuándo se elige |
|---|---|
| PostgreSQL | Estándar abierto, muy completo, extensiones (PostGIS, pgvector). Elección de MercadoFresco |
| MySQL | Enorme base instalada, aplicaciones PHP heredadas |
| MariaDB | Bifurcación de MySQL con licencia plenamente libre |
| Oracle | Migraciones de sistemas empresariales existentes; licencia cara |
| SQL Server | Ecosistema Microsoft; licencia incluida o propia |
| Db2 | Sistemas heredados de IBM |
| Aurora (PostgreSQL/MySQL compatible) | Motor propio de AWS, hasta 5× MySQL y 3× PostgreSQL, almacenamiento distribuido en 3 AZ. Se ve en la lección 06-03 |
MercadoFresco ya usaba PostgreSQL en el servidor de la oficina, así que RDS PostgreSQL permite migrar sin reescribir consultas. Es la decisión de menor riesgo.
Una aclaración que ahorra confusión: Aurora es también RDS, se gestiona desde la misma consola y comparte casi toda la operativa que veremos aquí, pero su arquitectura de almacenamiento es distinta. En 06-03 valoraremos si a MercadoFresco le compensa migrar a Aurora cuando abra en tres ciudades más. Y la elección general entre relacional, clave-valor o almacén de columnas es la lección 06-01.
Crear la instancia RDS por consola
- Consola → RDS → verifica la región Irlanda (
eu-west-1) → Crear base de datos. - Método: Creación estándar (la fácil oculta opciones que necesitas ver).
- Motor: PostgreSQL, versión 16.x.
- Plantilla: Producción activa Multi-AZ y el almacenamiento provisionado por defecto. Para aprender, elige Capa gratuita y así no gastas; describimos aquí la de producción.
- Configuración:
- Identificador:
mercadofresco-pedidos - Usuario maestro:
mfadmin(no usespostgresniadmin) - Contraseña: marca Administrar credenciales maestras en AWS Secrets Manager. Con esto la contraseña nunca la ves, nunca la escribes y AWS la rota sola. Secrets Manager es la lección 04-03.
- Identificador:
- Configuración de la instancia:
db.t3.small(2 vCPU, 2 GiB). Para producción real de MercadoFresco,db.m6g.large. - Almacenamiento: gp3, 20 GiB, con escalado automático activado y máximo 100 GiB.
- Disponibilidad: Instancia en espera Multi-AZ. Es la casilla que resuelve la mitad del problema 1 para la base de datos.
- Conectividad: VPC por defecto, acceso público = No, grupo de seguridad
sg-mercadofresco-basedatos. La red se ve en el módulo 3 (03-01 VPC, 03-02 grupos de seguridad). - Autenticación: contraseña; opcionalmente autenticación IAM, que elimina las contraseñas.
- Configuración adicional:
- Nombre de base de datos inicial:
pedidos - Copias automáticas: 7 días de retención
- Ventana de copias: 02:00-03:00 UTC (madrugada en España, tráfico mínimo)
- Ventana de mantenimiento: domingo 04:00-05:00 UTC
- Cifrado activado
- Protección contra eliminación: activada
- Performance Insights: activado (7 días son gratuitos)
- Nombre de base de datos inicial:
- Revisa la estimación de coste mensual que muestra la consola y pulsa Crear base de datos.
La creación tarda entre 5 y 15 minutos (más con Multi-AZ, porque construye dos instancias).
Aviso de coste. Una
db.t3.microde una sola AZ entra en la capa gratuita durante 12 meses (750 h/mes, 20 GB de almacenamiento y 20 GB de copias). Multi-AZ duplica el coste de cómputo y no está en la capa gratuita. Si estás practicando, crea la instancia sin Multi-AZ y activa la casilla solo un rato para ver cómo funciona. El apartado final explica cómo pararla y borrarla.
Crear la instancia RDS por CLI
# 1. Grupo de subredes: indica a RDS en qué subredes puede colocar la instancia.
# Deben ser al menos DOS, en DOS AZ distintas: es el requisito de Multi-AZ.
aws rds create-db-subnet-group \
--db-subnet-group-name sng-mercadofresco \
--db-subnet-group-description "Subredes privadas de MercadoFresco en dos AZ" \
--subnet-ids subnet-aaa11111 subnet-bbb22222 \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
Key=Componente,Value=pedidos Key=Propietario,Value=marta \
Key=CentroCoste,Value=operaciones \
--profile mercadofresco-dev --region eu-west-1
# 2. La instancia
aws rds create-db-instance \
--db-instance-identifier mercadofresco-pedidos \
--db-instance-class db.t3.small \
--engine postgres \
--engine-version 16.3 \
--master-username mfadmin \
--manage-master-user-password \
--allocated-storage 20 \
--max-allocated-storage 100 \
--storage-type gp3 \
--storage-encrypted \
--db-subnet-group-name sng-mercadofresco \
--vpc-security-group-ids sg-0abc123def456 \
--no-publicly-accessible \
--multi-az \
--backup-retention-period 7 \
--preferred-backup-window "02:00-03:00" \
--preferred-maintenance-window "sun:04:00-sun:05:00" \
--enable-performance-insights \
--performance-insights-retention-period 7 \
--deletion-protection \
--db-name pedidos \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
Key=Componente,Value=pedidos Key=Propietario,Value=marta \
Key=CentroCoste,Value=operaciones \
--profile mercadofresco-dev --region eu-west-1
# 3. Esperar a que esté disponible (bloquea hasta que termine)
aws rds wait db-instance-available \
--db-instance-identifier mercadofresco-pedidos \
--profile mercadofresco-dev --region eu-west-1
# 4. Obtener el punto de enlace (endpoint) al que se conectará la aplicación
aws rds describe-db-instances \
--db-instance-identifier mercadofresco-pedidos \
--query 'DBInstances[0].{Endpoint:Endpoint.Address,Puerto:Endpoint.Port,
Estado:DBInstanceStatus,MultiAZ:MultiAZ,AZ:AvailabilityZone}' \
--output table \
--profile mercadofresco-dev --region eu-west-1Los parámetros que más importan:
--manage-master-user-password: AWS genera la contraseña, la guarda en Secrets Manager y la rota automáticamente. Nunca la escribes ni la ves. Es la mejor práctica actual y evita el clásico--master-user-password "Password123"en el historial del shell.--no-publicly-accessible: la base de datos no tiene IP pública. Solo se accede desde dentro de la VPC. Innegociable para datos de clientes.--max-allocated-storage 100: activa el escalado automático de almacenamiento. Si el disco llega al 90 % de ocupación, RDS lo amplía solo hasta ese techo.--deletion-protection: impide borrar la instancia por error. Para borrarla de verdad hay que desactivarlo primero, en un paso consciente y separado.
El punto de enlace tendrá esta forma:
Es un nombre DNS, no una IP, y eso es fundamental. Cuando ocurra una conmutación por error, AWS cambiará a qué apunta ese nombre y la aplicación se reconectará al servidor nuevo sin cambiar ni una línea de configuración. Si en algún sitio del código de MercadoFresco hay una IP de base de datos escrita a mano, hay que quitarla ahora.
Multi-AZ: qué es realmente una réplica síncrona en espera
Aquí hay un malentendido muy extendido que conviene deshacer con claridad: la instancia en espera de Multi-AZ no sirve tráfico. No es un segundo servidor que reparte carga. No puedes conectarte a ella. Es una copia sincronizada esperando a que la principal falle.
flowchart TB
APP["Aplicacion de MercadoFresco<br/>instancias EC2 de la tienda"]
EP["Punto de enlace DNS<br/>mercadofresco-pedidos...rds.amazonaws.com"]
APP --> EP
EP --> P
subgraph AZ1["eu-west-1a"]
P["PRINCIPAL<br/>lecturas y escrituras"]
end
subgraph AZ2["eu-west-1b"]
S["EN ESPERA<br/>sin trafico<br/>no accesible"]
end
P -->|"replicacion SINCRONA<br/>cada commit se confirma<br/>en ambas antes de responder"| S
P -.->|"copias automaticas<br/>se toman de la instancia en espera:<br/>sin impacto en produccion"| B["Copias en S3"]
Cómo funciona la replicación síncrona: cuando la aplicación confirma una transacción, PostgreSQL escribe en la principal y espera a que la escritura se haya confirmado también en la instancia en espera antes de responder "hecho" al cliente. Consecuencia doble:
- Ventaja: cero pérdida de datos (RPO = 0). Lo confirmado está en las dos AZ.
- Coste: cada escritura tarda un poco más, porque incluye un viaje de red entre AZ (típicamente 1-2 ms). En cargas con escrituras muy intensas se nota.
Qué pasa exactamente en una conmutación por error
- AWS detecta el fallo (de la instancia, del almacenamiento o de la AZ entera).
- Cambia el registro DNS del punto de enlace para que apunte a la instancia en espera.
- La antigua instancia en espera pasa a ser la principal.
- AWS crea una nueva instancia en espera en la otra AZ, en segundo plano.
Duración típica: 60-120 segundos. Durante ese rato la aplicación recibe errores de conexión. Multi-AZ no es magia invisible: es recuperación rápida, no ausencia de interrupción. La aplicación debe estar preparada:
- Reintentos con espera exponencial en las operaciones de base de datos.
- TTL de DNS bajo en el resolutor del cliente. Un pool de conexiones que cachea la IP para siempre seguirá intentando conectar al servidor caído.
- Pool de conexiones que sepa descartar conexiones muertas (
pool_pre_pingen SQLAlchemy).
Se puede provocar una conmutación a propósito para comprobar que la aplicación aguanta. Hazlo en un entorno de pruebas, y hazlo antes de que ocurra sin avisar:
aws rds reboot-db-instance \
--db-instance-identifier mercadofresco-pedidos-pruebas \
--force-failover \
--profile mercadofresco-dev --region eu-west-1Multi-AZ frente a réplica de lectura
Son cosas distintas y se confunden constantemente:
| Multi-AZ (instancia en espera) | Réplica de lectura | |
|---|---|---|
| Para qué sirve | Alta disponibilidad | Escalar las lecturas |
| Replicación | Síncrona | Asíncrona (hay retardo) |
| ¿Se puede leer de ella? | No | Sí |
| ¿Se puede escribir? | No | No (salvo que la promuevas) |
| Conmutación automática | Sí | No (promoción manual) |
| Ubicación | Otra AZ de la misma región | Misma AZ, otra AZ u otra región |
| Número | 1 (o 2 con el modo clúster Multi-AZ) | Hasta 15 en PostgreSQL |
| Impacto en la latencia de escritura | Sí, la aumenta | No |
| Coste | Duplica el cómputo | Una instancia adicional por réplica |
MercadoFresco necesita las dos, y por razones diferentes: Multi-AZ porque el negocio no puede perder pedidos, y réplicas de lectura porque los informes de Sara están matando la base de datos.
Réplicas de lectura: los informes de Sara sin castigar producción
El problema real, medido: Sara ejecuta cada lunes por la mañana una consulta que agrega los pedidos del mes por barrio y por franja horaria. Tarda 4 minutos y durante ese rato la tienda va lenta, porque el mismo servidor está atendiendo a los clientes.
Una réplica de lectura es una copia asíncrona de la base de datos que acepta consultas de solo lectura. Las escrituras siguen yendo a la principal; las lecturas pesadas se dirigen a la réplica.
aws rds create-db-instance-read-replica \
--db-instance-identifier mercadofresco-pedidos-lectura \
--source-db-instance-identifier mercadofresco-pedidos \
--db-instance-class db.t3.small \
--availability-zone eu-west-1b \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
Key=Componente,Value=analitica Key=Propietario,Value=sara \
Key=CentroCoste,Value=marketing \
--profile mercadofresco-dev --region eu-west-1Fíjate en el etiquetado: Componente=analitica, Propietario=sara, CentroCoste=marketing. La
réplica existe para Sara, así que su coste se imputa a marketing y no a operaciones. Esto es
exactamente lo que hará posible el informe de asignación de costes de la lección 11-02.
Puntos que hay que tener claros:
- La replicación es asíncrona: la réplica va unos segundos por detrás. Vigila la métrica
ReplicaLag. Para informes agregados de ventas es perfectamente aceptable; para "consultar el estado del pedido que acabo de hacer", no. - La aplicación debe dirigir el tráfico. AWS te da un punto de enlace distinto para la réplica; el código decide cuál usa. RDS no reparte solo.
- Una réplica se puede promover a instancia independiente, lo que rompe la replicación de forma irreversible. Es una estrategia válida de migración entre regiones.
- Se pueden crear en otra región, lo que además da recuperación ante desastres geográfica.
En el código de MercadoFresco, la separación queda así:
import os
import psycopg
# Dos cadenas de conexión distintas, dos usos distintos.
DSN_ESCRITURA = os.environ["MF_DB_ESCRITURA"] # apunta a mercadofresco-pedidos
DSN_LECTURA = os.environ["MF_DB_LECTURA"] # apunta a mercadofresco-pedidos-lectura
def crear_pedido(cliente_id: int, lineas: list) -> int:
"""Escritura: SIEMPRE contra la instancia principal."""
with psycopg.connect(DSN_ESCRITURA) as con, con.cursor() as cur:
cur.execute(
"INSERT INTO pedidos (cliente_id, creado_en) VALUES (%s, now()) RETURNING id",
(cliente_id,),
)
pedido_id = cur.fetchone()[0]
cur.executemany(
"INSERT INTO lineas_pedido (pedido_id, producto_id, cantidad) VALUES (%s,%s,%s)",
[(pedido_id, l["producto_id"], l["cantidad"]) for l in lineas],
)
con.commit()
return pedido_id
def informe_ventas_por_barrio(mes: str) -> list:
"""Lectura analitica pesada: contra la REPLICA, para no afectar a la tienda."""
with psycopg.connect(DSN_LECTURA) as con, con.cursor() as cur:
cur.execute(
"""
SELECT c.barrio,
date_trunc('hour', p.creado_en) AS franja,
count(*) AS num_pedidos,
sum(lp.cantidad) AS unidades
FROM pedidos p
JOIN clientes c ON c.id = p.cliente_id
JOIN lineas_pedido lp ON lp.pedido_id = p.id
WHERE to_char(p.creado_en, 'YYYY-MM') = %s
GROUP BY c.barrio, franja
ORDER BY num_pedidos DESC
""",
(mes,),
)
return cur.fetchall()La consulta en SQL puro, para verla completa y entender por qué es cara: recorre todos los pedidos del mes, los une con clientes y líneas de pedido, y agrega por dos dimensiones. Con cientos de miles de filas eso es un recorrido secuencial largo que satura la CPU y expulsa datos calientes de la caché de PostgreSQL. Justo lo que no quieres que pase mientras 900 clientes están comprando.
Copias automáticas, retención y restauración a un punto en el tiempo
Aquí se cierra el problema 2 de MercadoFresco.
RDS realiza dos cosas de forma continua y automática:
- Una copia completa diaria durante la ventana que hayas fijado (02:00-03:00 UTC en nuestro caso). Con Multi-AZ, se toma de la instancia en espera, así que producción no lo nota.
- Copia continua de los registros de transacciones (WAL) a S3, cada 5 minutos.
La combinación de ambas es lo que permite la restauración a un punto en el tiempo (Point-In-Time Recovery, PITR): puedes restaurar la base de datos a cualquier segundo dentro del periodo de retención, no solo a la hora de la copia diaria.
El escenario que justifica todo esto: el martes a las 11:47, un despliegue de Luis ejecuta por error
un UPDATE sin WHERE que pone a cero el precio de todos los productos. A las 11:52 alguien se da
cuenta.
# Ver hasta qué momento se puede restaurar
aws rds describe-db-instances \
--db-instance-identifier mercadofresco-pedidos \
--query 'DBInstances[0].LatestRestorableTime' \
--profile mercadofresco-dev --region eu-west-1
# Restaurar a las 11:46, un minuto antes del desastre.
# IMPORTANTE: crea una instancia NUEVA. La original no se toca.
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier mercadofresco-pedidos \
--target-db-instance-identifier mercadofresco-pedidos-recuperada \
--restore-time 2026-08-04T11:46:00Z \
--db-subnet-group-name sng-mercadofresco \
--vpc-security-group-ids sg-0abc123def456 \
--no-publicly-accessible \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=pruebas \
Key=Componente,Value=pedidos Key=Propietario,Value=marta \
Key=CentroCoste,Value=operaciones \
--profile mercadofresco-dev --region eu-west-1Que la restauración cree una instancia nueva es deliberado y muy útil: puedes verificar los datos antes de tocar producción, o incluso extraer solo la tabla afectada y copiarla, en lugar de volver atrás toda la base de datos y perder los pedidos legítimos de esos cinco minutos.
Sobre la retención:
| Retención | Efecto |
|---|---|
| 0 días | Desactiva las copias automáticas y el PITR. Nunca en producción |
| 1-7 días | Valor por defecto (7). Suficiente para errores que se detectan rápido |
| 8-35 días | Máximo. Para requisitos de cumplimiento o errores de detección lenta |
Las copias automáticas se almacenan gratis hasta el tamaño de la base de datos; más allá se cobran a precio de snapshot. Y hay un detalle crítico: al eliminar la instancia, las copias automáticas se borran con ella. Solo los snapshots manuales sobreviven.
Snapshots manuales y el cierre del problema 2
Un snapshot manual es una copia que tú creas y que vive hasta que tú la borres, incluso si eliminas la instancia. Es la copia para hitos: antes de una migración de esquema, antes de una actualización de versión mayor, al cierre del ejercicio fiscal.
# Antes de aplicar la migración de esquema de la campaña de Navidad
aws rds create-db-snapshot \
--db-instance-identifier mercadofresco-pedidos \
--db-snapshot-identifier mf-pedidos-antes-migracion-navidad-2026 \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
Key=Componente,Value=pedidos Key=Propietario,Value=marta \
Key=CentroCoste,Value=operaciones \
--profile mercadofresco-dev --region eu-west-1
# Copiar el snapshot a otra región para recuperación ante desastres
aws rds copy-db-snapshot \
--source-db-snapshot-identifier \
arn:aws:rds:eu-west-1:111122223333:snapshot:mf-pedidos-antes-migracion-navidad-2026 \
--target-db-snapshot-identifier mf-pedidos-dr-navidad-2026 \
--kms-key-id alias/aws/rds \
--profile mercadofresco-dev --region eu-central-1Comparación de las tres formas de recuperación en RDS:
| Copias automáticas (PITR) | Snapshot manual | AWS Backup | |
|---|---|---|---|
| Quién las crea | RDS, a diario | Tú | Plan centralizado |
| Granularidad | Cualquier segundo | El instante de la creación | Según el plan |
| Retención | 0-35 días | Indefinida | La que definas |
| Sobreviven al borrado de la instancia | No | Sí | Sí |
| Copia entre regiones | No directamente | Sí | Sí |
| Uso típico | Errores humanos recientes | Hitos, retención larga | Gobierno unificado de todos los servicios |
Y así queda cerrado el problema 2 de MercadoFresco, sumando lo de 02-02 y lo de esta lección:
| Antes (servidor de la oficina) | Ahora (AWS) | |
|---|---|---|
| Copia de las fotos | Disco externo manual | Snapshots EBS automáticos con DLM + S3 con versionado |
| Copia de la base de datos | pg_dump cuando alguien se acordaba |
Automática diaria + WAL cada 5 min |
| Restaurar a un momento concreto | Imposible | PITR a cualquier segundo de los últimos 7 días |
| Copia fuera del edificio | No | Snapshots replicados a eu-central-1 |
| Tiempo de restauración | Horas, si el volcado servía | 10-20 minutos |
| Prueba de restauración | Nunca se hizo | Trimestral, en instancia aparte, sin tocar producción |
Con una advertencia que se repite y no sobra: la prueba de restauración trimestral es parte del plan, no un extra. Marta la tiene en el calendario.
Grupos de parámetros y ventanas de mantenimiento
Como no hay acceso al sistema operativo, no puedes editar postgresql.conf. En su lugar, RDS usa
grupos de parámetros: un conjunto con nombre de ajustes que se asocia a una o varias instancias.
El grupo por defecto no se puede modificar, así que lo primero es crear uno propio:
aws rds create-db-parameter-group \
--db-parameter-group-name pg16-mercadofresco \
--db-parameter-group-family postgres16 \
--description "Parametros de PostgreSQL 16 para MercadoFresco" \
--profile mercadofresco-dev --region eu-west-1
aws rds modify-db-parameter-group \
--db-parameter-group-name pg16-mercadofresco \
--parameters \
"ParameterName=log_min_duration_statement,ParameterValue=1000,ApplyMethod=immediate" \
"ParameterName=shared_preload_libraries,ParameterValue=pg_stat_statements,ApplyMethod=pending-reboot" \
"ParameterName=log_connections,ParameterValue=1,ApplyMethod=immediate" \
--profile mercadofresco-dev --region eu-west-1
aws rds modify-db-instance \
--db-instance-identifier mercadofresco-pedidos \
--db-parameter-group-name pg16-mercadofresco \
--apply-immediately \
--profile mercadofresco-dev --region eu-west-1Qué hemos configurado y por qué:
log_min_duration_statement = 1000: registra toda consulta que tarde más de 1 segundo. Es la forma más directa de encontrar las consultas que hunden la tienda los viernes.shared_preload_libraries = pg_stat_statements: activa la extensión que acumula estadísticas por consulta. Base de cualquier trabajo serio de optimización.ApplyMethod:immediatese aplica al momento;pending-rebootrequiere reiniciar la instancia. Un parámetro dinámico marcado como pendiente no está haciendo nada todavía, y esto despista mucho.
Sobre la ventana de mantenimiento: es el intervalo semanal en el que AWS aplica parches del sistema operativo y del motor. Elígela en el valle de tráfico (domingo de madrugada para MercadoFresco, nunca un viernes por la tarde). Con Multi-AZ, el mantenimiento se aplica primero a la instancia en espera, luego se conmuta y después se parchea la otra: la interrupción se reduce al tiempo de la conmutación.
Las actualizaciones de versión menor pueden ser automáticas (--auto-minor-version-upgrade);
las de versión mayor (de PostgreSQL 16 a 17) nunca lo son: requieren tu acción explícita y hay
que probarlas antes en una instancia restaurada desde un snapshot.
Monitorización: métricas básicas y Performance Insights
RDS publica métricas en CloudWatch sin configurar nada. Las que hay que mirar:
| Métrica | Qué indica | Umbral de alarma sugerido |
|---|---|---|
CPUUtilization |
Uso de CPU | > 80 % durante 10 min |
DatabaseConnections |
Conexiones abiertas | > 80 % de max_connections |
FreeableMemory |
Memoria disponible | < 10 % de la RAM total |
FreeStorageSpace |
Espacio libre en disco | < 10 % (o < 5 GB) |
ReadLatency / WriteLatency |
Latencia de disco | > 20 ms sostenido |
ReplicaLag |
Retardo de la réplica de lectura | > 30 s |
DiskQueueDepth |
Operaciones de disco en cola | > 5 sostenido |
Performance Insights va un paso más allá: es un panel que muestra la carga de la base de
datos descompuesta por consulta, por usuario, por host y por tipo de espera. Esa última
dimensión es la que resuelve incidencias: no te dice solo "la CPU está al 90 %", te dice "el 70 % de
la carga está esperando bloqueos de la tabla pedidos, y la consulta responsable es esta".
Los 7 días de retención son gratuitos y merece la pena activarlo siempre. La monitorización a fondo, con alarmas, paneles y agregación de registros, es la lección 05-01.
# Alarma sobre el espacio libre: el fallo más previsible y más evitable
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-pedidos-espacio-bajo \
--alarm-description "Menos de 5 GB libres en la base de datos de pedidos" \
--namespace AWS/RDS --metric-name FreeStorageSpace \
--dimensions Name=DBInstanceIdentifier,Value=mercadofresco-pedidos \
--statistic Average --period 300 --evaluation-periods 2 \
--threshold 5000000000 --comparison-operator LessThanThreshold \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--profile mercadofresco-dev --region eu-west-1Conectarse desde la aplicación sin credenciales en el código
Primero, la comprobación manual desde una instancia EC2 de la tienda (recuerda: la base de datos no es pública, así que hay que conectarse desde dentro de la VPC):
# Recuperar la contraseña generada por AWS desde Secrets Manager.
# Nunca se copia a un fichero ni se escribe en el historial.
export PGPASSWORD=$(aws secretsmanager get-secret-value \
--secret-id "rds!db-a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
--query 'SecretString' --output text \
--profile mercadofresco-dev --region eu-west-1 | python3 -c "import sys,json; print(json.load(sys.stdin)['password'])")
psql -h mercadofresco-pedidos.abc123xyz.eu-west-1.rds.amazonaws.com \
-U mfadmin -d pedidos -p 5432
unset PGPASSWORDUna vez dentro, comprobaciones útiles:
-- Versión del motor
SELECT version();
-- Tamaño de la base de datos
SELECT pg_size_pretty(pg_database_size('pedidos')) AS tamano;
-- Las 5 consultas más lentas (requiere pg_stat_statements activado)
SELECT substring(query, 1, 80) AS consulta,
calls,
round(mean_exec_time::numeric, 2) AS ms_medios,
round(total_exec_time::numeric, 2) AS ms_totales
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 5;
-- Conexiones activas por estado
SELECT state, count(*) FROM pg_stat_activity GROUP BY state;Y así se conecta la aplicación, leyendo el secreto en el arranque y sin que ninguna credencial toque el código ni el repositorio:
"""
Conexión de la tienda de MercadoFresco a RDS PostgreSQL.
Las credenciales se leen de Secrets Manager usando el rol IAM de la instancia EC2:
no hay contraseñas en el código, ni en variables de entorno, ni en el repositorio.
"""
import json
import os
import boto3
import psycopg
from psycopg_pool import ConnectionPool
REGION = "eu-west-1"
ID_SECRETO = os.environ["MF_SECRETO_BD"] # solo el identificador, no la clave
HOST_ESCRITURA = os.environ["MF_BD_HOST_ESCRITURA"]
HOST_LECTURA = os.environ["MF_BD_HOST_LECTURA"]
def _credenciales() -> dict:
"""Lee usuario y contraseña de Secrets Manager.
El rol IAM de la instancia EC2 (o de la Lambda) autoriza esta llamada:
no se necesita ninguna clave de acceso. Detalle en las lecciones 04-01 y 04-03.
"""
cliente = boto3.client("secretsmanager", region_name=REGION)
respuesta = cliente.get_secret_value(SecretId=ID_SECRETO)
return json.loads(respuesta["SecretString"])
def _dsn(host: str) -> str:
c = _credenciales()
# sslmode=require obliga a cifrar la conexión en tránsito.
return (
f"host={host} port=5432 dbname=pedidos "
f"user={c['username']} password={c['password']} "
f"sslmode=require connect_timeout=5"
)
# Pools separados. min_size mantiene conexiones abiertas para evitar el coste
# de establecerlas en cada petición durante el pico de los viernes.
pool_escritura = ConnectionPool(_dsn(HOST_ESCRITURA), min_size=2, max_size=10)
pool_lectura = ConnectionPool(_dsn(HOST_LECTURA), min_size=1, max_size=5)
def estado_pedido(pedido_id: int) -> dict | None:
"""Consulta de solo lectura que SÍ debe ir a la principal.
El cliente acaba de hacer el pedido: el retardo de la réplica podría
hacer que no lo encontrase. Regla: lo que el usuario acaba de escribir,
se lee de la principal.
"""
with pool_escritura.connection() as con, con.cursor() as cur:
cur.execute(
"SELECT id, estado, creado_en, entrega_estimada FROM pedidos WHERE id = %s",
(pedido_id,),
)
fila = cur.fetchone()
if fila is None:
return None
return {
"id": fila[0],
"estado": fila[1],
"creado_en": fila[2].isoformat(),
"entrega_estimada": fila[3].isoformat() if fila[3] else None,
}Las tres reglas que resume este código:
- Ninguna credencial en el código ni en el repositorio. Solo el identificador del secreto.
- Autorización por rol IAM, no por claves de acceso. La instancia EC2 tiene un rol que le permite leer ese secreto concreto y nada más.
sslmode=require: la conexión va cifrada. RDS proporciona el certificado.
Escalado vertical y almacenamiento autoescalable
Escalado vertical (cambiar la clase de instancia). Es una operación con interrupción, salvo con Multi-AZ, donde AWS modifica primero la instancia en espera, conmuta y luego la otra: la parada se reduce a los 60-120 segundos de la conmutación.
aws rds modify-db-instance \
--db-instance-identifier mercadofresco-pedidos \
--db-instance-class db.m6g.large \
--apply-immediately \
--profile mercadofresco-dev --region eu-west-1--apply-immediately aplica el cambio ya, con la interrupción correspondiente. Sin ese
parámetro, el cambio espera a la ventana de mantenimiento, que es lo que quieres en producción salvo
urgencia.
Escalado del almacenamiento. Se puede ampliar en cualquier momento y sin parar, pero no
reducir. Con --max-allocated-storage, RDS lo hace solo:
aws rds modify-db-instance \
--db-instance-identifier mercadofresco-pedidos \
--allocated-storage 50 --max-allocated-storage 200 \
--apply-immediately \
--profile mercadofresco-dev --region eu-west-1Cuándo escalar cada cosa, en el caso de MercadoFresco:
| Síntoma | Causa probable | Acción |
|---|---|---|
| CPU al 90 % con consultas lentas | Falta CPU o faltan índices | Primero mira los índices; luego sube de clase |
FreeableMemory bajo, mucha lectura de disco |
El conjunto de trabajo no cabe en RAM | Clase con más memoria (familia R) |
| Muchas lecturas analíticas | Informes de Sara | Réplica de lectura, no una instancia mayor |
FreeStorageSpace bajando |
Crecimiento normal | Escalado automático de almacenamiento |
| Latencia de escritura alta | IOPS insuficientes | Subir IOPS de gp3 o pasar a io2 |
El orden importa: subir de tamaño es la última opción, no la primera. Un índice que falta cuesta cero euros al mes; una clase de instancia mayor cuesta todos los meses, para siempre.
Migrar desde el servidor de la oficina
Con la base de datos de MercadoFresco (unos pocos GB y una ventana de mantenimiento nocturna aceptable), la migración clásica con volcado y restauración es suficiente.
# ---------- En el servidor de la oficina ----------
# 1. Volcado en formato "custom": comprimido y restaurable en paralelo.
pg_dump -h localhost -U postgres -d pedidos \
--format=custom --compress=9 --verbose \
--file=pedidos-2026-08-04.dump
# 2. Comprobar el volcado antes de confiar en él
pg_restore --list pedidos-2026-08-04.dump | head -20
ls -lh pedidos-2026-08-04.dump
# ---------- Desde una EC2 dentro de la VPC ----------
# 3. Subir el volcado a S3 (bucket privado, cifrado con KMS)
aws s3 cp pedidos-2026-08-04.dump \
s3://mercadofresco-copias-basedatos/copias/base-datos/ \
--profile mercadofresco-dev
# 4. Descargarlo en la EC2 y restaurar contra RDS.
# -j 4 restaura con 4 procesos en paralelo: mucho más rápido.
pg_restore -h mercadofresco-pedidos.abc123xyz.eu-west-1.rds.amazonaws.com \
-U mfadmin -d pedidos \
--no-owner --no-privileges --verbose -j 4 \
pedidos-2026-08-04.dumpPor qué --no-owner --no-privileges: los roles y permisos del servidor de origen no existen en RDS,
y sin esas opciones la restauración se llena de errores por intentar asignar objetos a usuarios
inexistentes.
Verificación obligatoria antes de conmutar el tráfico:
-- Comparar el número de filas de las tablas principales con el origen
SELECT 'pedidos' AS tabla, count(*) FROM pedidos
UNION ALL SELECT 'clientes', count(*) FROM clientes
UNION ALL SELECT 'productos', count(*) FROM productos
UNION ALL SELECT 'lineas_pedido', count(*) FROM lineas_pedido;
-- Comprobar que los índices y las restricciones han llegado
SELECT tablename, indexname FROM pg_indexes
WHERE schemaname = 'public' ORDER BY tablename;
-- Actualizar las estadísticas del planificador: sin esto, las consultas
-- pueden ir absurdamente lentas justo después de restaurar.
ANALYZE VERBOSE;Plan de conmutación de MercadoFresco, un domingo de madrugada:
- 02:00 — Poner la tienda en modo mantenimiento (solo lectura).
- 02:05 —
pg_dumpfinal del servidor de la oficina. - 02:20 —
pg_restoreen RDS yANALYZE. - 02:40 — Verificar recuentos y ejecutar la batería de pruebas de humo.
- 02:50 — Cambiar las variables de entorno de la aplicación al nuevo punto de enlace.
- 03:00 — Quitar el modo mantenimiento y vigilar métricas durante una hora.
- El servidor de la oficina se deja encendido y sincronizado una semana, por si hay que volver atrás.
Cuando no se puede parar la tienda, la herramienta es AWS DMS (Database Migration Service): copia el estado inicial y después replica los cambios en continuo (change data capture), de modo que la ventana de corte se reduce a segundos. Además puede migrar entre motores distintos (de Oracle a PostgreSQL, por ejemplo). Para MercadoFresco es exagerado; para una migración sin parada, es el camino.
Costes y cómo parar una instancia de pruebas
Componentes de la factura de RDS:
| Componente | Precio orientativo (eu-west-1) |
|---|---|
| Horas de instancia | db.t3.micro ≈ 0,018 USD/h · db.t3.small ≈ 0,036 · db.m6g.large ≈ 0,171 |
| Multi-AZ | ×2 sobre las horas de instancia |
| Almacenamiento gp3 | ≈ 0,127 USD/GB-mes |
| Copias | Gratis hasta el tamaño de la base de datos; después ≈ 0,105 USD/GB-mes |
| Réplica de lectura | Como una instancia más |
| Transferencia entre AZ | Gratis dentro de Multi-AZ; de pago hacia internet |
| Performance Insights | 7 días gratis; más retención, de pago |
Coste mensual de la configuración de producción de MercadoFresco:
Instancia db.t3.small Multi-AZ: 0,036 × 2 × 730 = 52,56 USD Almacenamiento 50 GB × 0,127 = 6,35 USD Copias (50 GB, dentro de lo gratuito) = 0,00 USD Réplica de lectura db.t3.small = 26,28 USD -------------------------------------------------------------- Total ≈ 85,19 USD/mes
Unos 78 € al mes por una base de datos con alta disponibilidad, copias automáticas, PITR de 7 días y una réplica para analítica. Comparado con los 6.000 € del servidor original —que no tenía nada de eso— y con el tiempo que Marta dedicaba a mantenerlo, la conversación económica se vuelve fácil.
Parar una instancia de pruebas:
# Se detiene el cómputo (deja de facturarse); el almacenamiento sigue costando.
aws rds stop-db-instance \
--db-instance-identifier mercadofresco-pedidos-pruebas \
--profile mercadofresco-dev --region eu-west-1
aws rds start-db-instance \
--db-instance-identifier mercadofresco-pedidos-pruebas \
--profile mercadofresco-dev --region eu-west-1Dos limitaciones importantes: RDS solo permite tener una instancia parada 7 días; al octavo la arranca sola. Y una instancia Multi-AZ no se puede parar. Para entornos de desarrollo que solo se usan en horario laboral, lo práctico es programar el arranque y la parada, o simplemente borrar y recrear desde un snapshot.
Eliminar por completo cuando termines la práctica:
# 1. Quitar la protección contra eliminación (paso consciente y separado)
aws rds modify-db-instance \
--db-instance-identifier mercadofresco-pedidos-pruebas \
--no-deletion-protection --apply-immediately \
--profile mercadofresco-dev --region eu-west-1
# 2. Eliminar las réplicas primero
aws rds delete-db-instance \
--db-instance-identifier mercadofresco-pedidos-lectura \
--skip-final-snapshot \
--profile mercadofresco-dev --region eu-west-1
# 3. Eliminar la principal, guardando un snapshot final por si acaso
aws rds delete-db-instance \
--db-instance-identifier mercadofresco-pedidos-pruebas \
--final-db-snapshot-identifier mf-pedidos-final-$(date +%Y%m%d) \
--profile mercadofresco-dev --region eu-west-1
# 4. Los snapshots manuales siguen costando. Revísalos y bórralos si no los necesitas.
aws rds describe-db-snapshots --snapshot-type manual \
--query 'DBSnapshots[].{ID:DBSnapshotIdentifier,GB:AllocatedStorage,Fecha:SnapshotCreateTime}' \
--output table \
--profile mercadofresco-dev --region eu-west-1Errores Comunes y Consejos
- Creer que la instancia en espera de Multi-AZ sirve lecturas. No. Para eso están las réplicas de lectura. Es el malentendido número uno de RDS.
- Poner
--publicly-accessiblepara "poder conectarme desde casa". Expone la base de datos a internet. La forma correcta es un bastión, Session Manager o una VPN. - Escribir la contraseña en el comando o en el código. Usa
--manage-master-user-passwordy Secrets Manager (04-03). - Retención de copias a 0 días. Desactiva el PITR. Es la configuración que convierte un error humano recuperable en una pérdida definitiva.
- Olvidar que las copias automáticas se borran con la instancia. Antes de eliminar, saca un snapshot final.
- Cambiar un parámetro
pending-rebooty esperar que actúe. No hace nada hasta reiniciar. - Restaurar y no ejecutar
ANALYZE. Sin estadísticas, el planificador toma decisiones pésimas y parece que RDS "va lento". - Poner la ventana de mantenimiento en hora punta. Domingo de madrugada, nunca viernes por la tarde.
- Subir de clase de instancia antes de mirar las consultas. Un índice que falta cuesta 0 USD al mes; una instancia mayor, todos los meses.
- Leer de la réplica lo que el usuario acaba de escribir. El retardo asíncrono hará que a veces no aparezca. Lecturas críticas, siempre a la principal.
- No probar nunca una conmutación por error. Provócala en un entorno de pruebas y comprueba que la aplicación se reconecta. Descubrirlo en producción sale caro.
- Consejo: activa Performance Insights desde el primer día. Es gratis 7 días y el día que haya una incidencia querrás tener el histórico.
Ejercicios
Ejercicio 1: diseñar la topología de bases de datos
MercadoFresco abre en tres ciudades más (problema 3). La previsión es:
- El pico de los viernes pasa a 2.100 pedidos/hora (escrituras intensas).
- Sara pasa de un informe semanal a un panel que se refresca cada 15 minutos.
- Aparece un requisito nuevo: si se pierde la región
eu-west-1entera, el negocio debe poder operar en menos de 4 horas. - Se mantiene el requisito de no perder ningún pedido confirmado.
Diseña la topología de RDS: qué instancias, de qué tipo, en qué AZ y regiones, y qué mecanismo cubre cada requisito. Indica qué requisito no puede cubrir RDS por sí solo.
Ejercicio 2: recuperar de un desastre con PITR
El jueves a las 16:12, un script de sincronización de catálogo ejecuta por error:
Ha borrado 47.000 líneas de pedidos históricos que sí eran válidas. Nadie se da cuenta hasta el viernes a las 10:30, cuando Sara ve que las cifras de julio no cuadran. En ese tiempo se han registrado unos 1.400 pedidos nuevos que no se pueden perder.
Describe el procedimiento completo de recuperación, con los comandos, y explica por qué no se puede simplemente restaurar la base de datos al jueves a las 16:11.
Ejercicio 3: decidir dónde va cada consulta
Para cada consulta de MercadoFresco, indica si debe ir a la instancia principal o a la réplica de lectura, y justifica en una frase:
- A)
INSERTde un pedido nuevo desde la cesta. - B) "Mis pedidos" de un cliente que acaba de comprar hace 3 segundos.
- C) Informe mensual de ventas agregadas por barrio.
- D) Listado del catálogo de productos en la página principal.
- E) Comprobación de stock justo antes de confirmar un pedido.
- F) Exportación nocturna de todos los pedidos a S3 para analítica.
Soluciones
Solución 1.
| Requisito | Solución | Detalle |
|---|---|---|
| 2.100 pedidos/h de escritura | Escalado vertical de la principal a db.m6g.xlarge (o migración a Aurora, lección 06-03) |
Las escrituras no se pueden repartir entre réplicas: solo hay un escritor. Es el límite del modelo relacional clásico |
| No perder ningún pedido confirmado | Multi-AZ en eu-west-1a / eu-west-1b |
Replicación síncrona: RPO = 0 |
| Panel de Sara cada 15 min | Réplica de lectura db.r6g.large en eu-west-1b |
Familia R por ser carga analítica intensiva en memoria; un retardo de segundos es irrelevante para un panel de 15 minutos |
| Sobrevivir a la pérdida de la región | Réplica de lectura en eu-central-1 + snapshots automáticos copiados a esa región |
Ante desastre, se promueve la réplica a instancia independiente: minutos, muy por debajo de las 4 horas exigidas |
Topología resultante:
eu-west-1a: mercadofresco-pedidos (principal, db.m6g.xlarge) eu-west-1b: instancia en espera Multi-AZ (automática, no accesible) eu-west-1b: mercadofresco-pedidos-lectura (réplica, db.r6g.large) → Sara eu-central-1: mercadofresco-pedidos-dr (réplica entre regiones) → recuperación ante desastres
Lo que RDS no cubre por sí solo: el pico de escrituras de los viernes sigue dependiendo de una sola instancia escritora. Escalar verticalmente tiene un techo. Las soluciones reales están fuera de esta lección: encolar los pedidos con SQS para absorber el pico y escribirlos a ritmo sostenido (módulo 7), cachear las lecturas calientes con ElastiCache (06-05), o repartir la escritura con un modelo distinto de datos (DynamoDB, 06-02). Reconocer ese techo es parte de saber usar RDS.
Solución 2.
Por qué no se puede restaurar sin más al jueves a las 16:11: eso devolvería la base de datos al estado de ese instante, y con ello desaparecerían los 1.400 pedidos registrados entre el jueves por la tarde y el viernes por la mañana. Se arreglaría un problema creando otro peor. La restauración completa solo vale cuando el daño se detecta en minutos.
El procedimiento correcto es restaurar en paralelo y recuperar solo lo borrado:
# 1. Restaurar a una instancia NUEVA en el instante anterior al borrado.
# Producción sigue funcionando sin enterarse.
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier mercadofresco-pedidos \
--target-db-instance-identifier mercadofresco-pedidos-rescate \
--restore-time 2026-08-06T16:11:00Z \
--db-subnet-group-name sng-mercadofresco \
--vpc-security-group-ids sg-0abc123def456 \
--no-publicly-accessible \
--db-instance-class db.t3.small \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=pruebas \
Key=Componente,Value=pedidos Key=Propietario,Value=marta \
Key=CentroCoste,Value=operaciones \
--profile mercadofresco-dev --region eu-west-1
aws rds wait db-instance-available \
--db-instance-identifier mercadofresco-pedidos-rescate \
--profile mercadofresco-dev --region eu-west-1# 2. Extraer SOLO las filas borradas de la instancia de rescate.
pg_dump -h mercadofresco-pedidos-rescate.abc123.eu-west-1.rds.amazonaws.com \
-U mfadmin -d pedidos \
--table=lineas_pedido --data-only --format=custom \
--file=lineas-rescate.dump-- 3. En PRODUCCIÓN: cargar el volcado en una tabla temporal y reinsertar
-- únicamente lo que falta. ON CONFLICT protege de duplicar nada.
CREATE TABLE lineas_pedido_rescate (LIKE lineas_pedido INCLUDING ALL);
-- (aquí se restaura el volcado sobre lineas_pedido_rescate)
BEGIN;
INSERT INTO lineas_pedido
SELECT r.*
FROM lineas_pedido_rescate r
LEFT JOIN lineas_pedido a ON a.id = r.id
WHERE a.id IS NULL
ON CONFLICT (id) DO NOTHING;
-- Verificar ANTES de confirmar
SELECT count(*) FROM lineas_pedido; -- debe haber subido ~47.000
COMMIT;
DROP TABLE lineas_pedido_rescate;# 4. Eliminar la instancia de rescate para dejar de pagarla
aws rds delete-db-instance \
--db-instance-identifier mercadofresco-pedidos-rescate \
--skip-final-snapshot \
--profile mercadofresco-dev --region eu-west-1Lecciones que deja el incidente: el DELETE debía haberse ejecutado dentro de una transacción con
verificación previa del recuento; los scripts de mantenimiento no deben usar el usuario maestro sino
un rol con permisos acotados (04-01); y una alarma sobre variaciones bruscas del número de filas
habría avisado el jueves y no el viernes.
Solución 3.
| Consulta | Destino | Justificación |
|---|---|---|
A) INSERT de pedido |
Principal | Es una escritura. Las réplicas son de solo lectura, no hay alternativa |
| B) "Mis pedidos" tras comprar | Principal | El retardo asíncrono podría hacer que el pedido recién creado no apareciera. Regla: lo que el usuario acaba de escribir, se lee de la principal |
| C) Informe mensual por barrio | Réplica | Consulta pesada sobre datos históricos; unos segundos de retardo son irrelevantes y así no castiga a la tienda |
| D) Catálogo de la página principal | Réplica (y además caché) | Datos que cambian poco y se leen muchísimo. El caso de manual para una réplica; con ElastiCache delante sería aún mejor (06-05) |
| E) Stock antes de confirmar | Principal | Dato crítico y volátil: leer un stock desactualizado provoca vender producto que no existe. Además suele necesitar bloqueo (SELECT ... FOR UPDATE), que solo funciona en la principal |
| F) Exportación nocturna a S3 | Réplica | Lectura masiva en horario valle; se hace en la réplica precisamente para no tocar la principal ni siquiera de noche, cuando corren las copias |
Patrón general: escrituras y lecturas críticas o inmediatas → principal; lecturas analíticas, históricas o tolerantes al retardo → réplica.
Conclusión
La base de datos de pedidos de MercadoFresco ya está en AWS, y con ella el corazón del negocio. Entiendes qué significa un servicio gestionado: AWS asume el hardware, el sistema operativo, los parches del motor, las copias y la conmutación por error; tú sigues siendo responsable del esquema, los índices, las consultas y la seguridad de la aplicación. Sabes también qué renuncias a cambio —acceso al sistema operativo, superusuario real, extensiones fuera de lista— y en qué casos concretos eso obliga a elegir PostgreSQL sobre EC2 en lugar de RDS.
Has creado la instancia mercadofresco-pedidos por consola y por CLI, con las decisiones que
importan tomadas conscientemente: contraseña gestionada en Secrets Manager para no escribirla
nunca, sin acceso público, cifrado activado, protección contra eliminación, escalado automático
de almacenamiento, retención de copias de 7 días y ventanas de copia y mantenimiento colocadas en el
valle de tráfico y nunca un viernes por la tarde.
Has deshecho el malentendido más común de RDS: la instancia en espera de Multi-AZ no sirve tráfico. Es una réplica síncrona que garantiza cero pérdida de datos y conmuta en 60-120 segundos cambiando el DNS del punto de enlace, con la contrapartida de sumar latencia a cada escritura y de exigir que la aplicación reintente. Frente a ella, las réplicas de lectura son asíncronas, sí se pueden consultar y son la herramienta correcta para que el panel de Sara no castigue a producción; has visto en código y en tabla qué consulta va a cada sitio.
Y has cerrado el problema 2 de MercadoFresco. Entre los snapshots de EBS automatizados con DLM
de la lección 02-02, el versionado de S3 de la 02-03 y ahora las copias automáticas de RDS con
registros de transacciones cada cinco minutos, la empresa ha pasado de "un pg_dump a un disco
externo cuando alguien se acuerda" a poder restaurar a cualquier segundo de los últimos siete
días. Has practicado la recuperación real de un borrado masivo restaurando en paralelo y
reinsertando solo lo perdido, sin sacrificar los pedidos posteriores. Sumas a eso los snapshots
manuales que sobreviven al borrado de la instancia, los grupos de parámetros con
log_min_duration_statement y pg_stat_statements para cazar consultas lentas, Performance
Insights para saber en qué espera realmente la base de datos, la conexión desde la aplicación sin
una sola credencial en el código, el escalado vertical y de almacenamiento con su regla de oro
—mira los índices antes de subir de clase— y el plan completo de migración con pg_dump/pg_restore
y su alternativa sin parada, AWS DMS.
Queda una última pieza del módulo. Tenemos servidores que hay que dimensionar, arrancar y pagar aunque estén ociosos. Pero hay tareas de MercadoFresco que no necesitan un servidor esperando: generar la miniatura de una foto cuando se sube, responder a una consulta puntual del estado de un pedido, procesar un fichero. En la lección 02-05, «AWS Lambda», escribiremos código que se ejecuta solo cuando ocurre algo y solo se paga mientras corre, conectaremos por fin el evento de S3 que dejamos configurado en 02-03 para generar las miniaturas del catálogo, y recapitularemos qué piezas de MercadoFresco están ya en la nube y qué falta por construir.
Curso de AWS
Módulo 1: Introducción a AWS
- ¿Qué es AWS?
- Configuración de tu cuenta de AWS
- Infraestructura global de AWS
- Consola de administración de AWS
- AWS CLI y SDKs
Módulo 2: Servicios principales de AWS
Módulo 3: Redes y entrega de contenido
- Amazon VPC
- Grupos de seguridad y listas de control de acceso
- Elastic Load Balancing
- Amazon CloudFront
- Route 53
Módulo 4: Seguridad e identidad
- AWS Identity and Access Management (IAM)
- AWS Key Management Service (KMS)
- Secrets Manager y Parameter Store
- AWS Shield
- AWS WAF
Módulo 5: Monitorización y gestión
- Amazon CloudWatch
- AWS X-Ray y trazabilidad distribuida
- AWS CloudTrail
- AWS Config
- AWS Trusted Advisor
Módulo 6: Bases de datos
- Cómo elegir la base de datos adecuada
- Amazon DynamoDB
- Amazon Aurora
- Amazon Redshift
- Amazon ElastiCache
Módulo 7: Integración de aplicaciones
- Amazon SQS
- Amazon SNS
- Amazon EventBridge
- AWS Step Functions
- Patrones de integración: idempotencia, reintentos y colas de mensajes fallidos
