Translate

miércoles, 25 de febrero de 2026

Bases de Datos Relacionales en la Nube

 ¿Qué es una Base de Datos en la Nube?

Una base de datos en la nube es un servicio gestionado que permite crear, administrar y escalar bases de datos sin necesidad de instalar ni mantener infraestructura física.
Estas se ofrecen bajo el modelo DBaaS (Database as a Service), donde el proveedor se encarga de:

  • Infraestructura (servidores, almacenamiento, red).

  • Backups automáticos.

  • Actualizaciones y parches de seguridad.

  • Alta disponibilidad y recuperación ante fallos.

📘 Ejemplos de servicios DBaaS más usados:

  • AWS RDS (Relational Database Service)

  • Azure SQL Database

  • Google Cloud SQL



🔸 Ejemplo: AWS RDS

Amazon RDS (Relational Database Service) permite desplegar motores como:

  • MySQL

  • PostgreSQL

  • MariaDB

  • SQL Server

  • Oracle

  • Aurora (motor optimizado de AWS)

📘 Ventajas específicas:

  • Backups automáticos diarios.

  • Replicación Multi-AZ (alta disponibilidad).

  • Monitoreo con Amazon CloudWatch.

  • Escalado vertical y horizontal sin reinicios.

Ejemplo de creación (consola AWS):

  1. Seleccionar motor (p. ej. PostgreSQL).

  2. Configurar tamaño de instancia (db.t3.micro, db.m6g.large, etc.).

  3. Elegir almacenamiento y backups automáticos.

  4. Configurar seguridad (VPC, grupos de seguridad).

  5. Conectar vía endpoint DNS generado por RDS.



🔸 Ejemplo: Azure SQL Database

Servicio totalmente administrado basado en Microsoft SQL Server.

📘 Características destacadas:

  • Escalado automático según demanda.

  • Alta disponibilidad integrada (réplicas en múltiples regiones).

  • Soporte para autenticación Azure AD y cifrado transparente (TDE).

  • Integración con Power BI y Azure Data Factory.



🔸 Ejemplo: Cloud SQL (Google Cloud)

Permite ejecutar MySQL, PostgreSQL y SQL Server en Google Cloud Platform.

📘 Ventajas:

  • Backups automáticos y restauración en un clic.

  • Replicación regional.

  • Integración con Google Kubernetes Engine y Cloud Run.

  • Seguridad mediante IAM y VPC Service Controls.



Conceptos de Escalabilidad, Alta Disponibilidad y Failover

🔸 Escalabilidad

Capacidad del sistema para aumentar recursos cuando crece la carga.

Tipo

Descripción

Ejemplo

Escalabilidad vertical (scale-up)

Aumentar recursos de una sola instancia (más CPU, RAM, disco).

Cambiar de db.t3.small a db.t3.large.

Escalabilidad horizontal (scale-out)

Agregar más instancias que comparten la carga.

Replicación de lectura en RDS o read replicas en Cloud SQL.

💡 En entornos modernos, se combinan ambas estrategias para mantener rendimiento y disponibilidad.


🔸 Alta Disponibilidad (High Availability, HA)

Garantiza que la base de datos permanezca accesible incluso ante fallos (hardware, red, mantenimiento).

📘 Mecanismos comunes:

  • Replicación síncrona: se mantiene una copia exacta en otra zona o región.

  • Failover automático: si la instancia principal falla, el sistema conmuta a la réplica.

  • Heartbeat y monitoreo continuo: detectan fallas en milisegundos.

📘 Ejemplo (AWS RDS Multi-AZ):

  • Se crean dos instancias en zonas distintas.

  • Si la principal cae, el sistema activa automáticamente la réplica secundaria.

  • El endpoint de conexión se mantiene igual (transparente para la aplicación).


🔸 Failover

Proceso mediante el cual una instancia secundaria toma el control de manera automática cuando la principal falla.

Tipos de failover:

  • Automático: el servicio gestiona la conmutación (RDS, Azure SQL).

  • Manual: el administrador decide cuándo activar la réplica.

📘 Ejemplo visual (RDS Multi-AZ):

[Primary DB - us-east-1a]  replicación síncrona   [Standby DB - us-east-1b]

              (falla)

       [Failover automático a Standby]


🔹 Seguridad en Bases de Datos en la Nube

La seguridad en entornos cloud combina controles de red, cifrado y gestión de credenciales.
La responsabilidad se comparte entre el proveedor cloud y el usuario (Shared Responsibility Model).


🔸 Grupos de Seguridad (Security Groups / Firewalls)

Actúan como cortafuegos virtuales que controlan qué direcciones IP o redes pueden acceder a la base.

📘 Ejemplo:

  • En AWS, un Security Group puede permitir solo el tráfico:

    • Entrante: puerto 5432 (PostgreSQL)

    • Origen: IP de la aplicación o red privada (VPC)

Buenas prácticas:

  • Nunca exponer la base de datos directamente a Internet.

  • Permitir solo el tráfico interno (por VPC o túnel VPN).

  • Revisar reglas de seguridad periódicamente.


🔸 Cifrado en Reposo (Encryption at Rest)

Protege los datos almacenados en disco.
Los principales proveedores cifran automáticamente usando AES-256 o equivalentes.

📘 Ejemplo:

  • AWS RDS usa KMS (Key Management Service) para manejar claves.

  • Azure SQL Database usa Transparent Data Encryption (TDE).

  • Google Cloud SQL usa Cloud KMS.

✅ Los datos, backups, snapshots y logs quedan cifrados automáticamente.


🔸 Cifrado en Tránsito (Encryption in Transit)

Asegura que los datos viajen cifrados entre la aplicación y la base de datos mediante SSL/TLS.

📘 Ejemplo:

psql "host=mydb.rds.amazonaws.com port=5432 sslmode=require user=admin dbname=appdb"

Buenas prácticas:

  • Exigir conexiones con SSL (require_ssl=on).

  • Usar certificados actualizados.

  • Bloquear conexiones no cifradas en la configuración del SGBD.


🔸 Gestión de Credenciales y Acceso

  • Implementar autenticación IAM / AD cuando sea posible.

  • Evitar guardar contraseñas en código fuente.

  • Usar rotación automática de credenciales (por ejemplo, AWS Secrets Manager).

  • Asignar roles mínimos necesarios (principio de least privilege).


lunes, 23 de febrero de 2026

Macros higiénicas en Elixir: poder metaprogramar sin perder la cordura


Si hay una característica que distingue a Elixir de muchos otros lenguajes modernos, es su sistema de metaprogramación.

Elixir no solo permite escribir código que genera código: lo hace de una forma segura, estructurada y predecible gracias a sus macros higiénicas.

En un ecosistema donde las macros suelen ser sinónimo de caos (como en C o C++), Elixir logra que la metaprogramación sea una herramienta confiable, sin los típicos riesgos de colisión o ambigüedad.

Pero ¿cómo lo logra?

En muchos lenguajes, una macro es básicamente un reemplazo de texto: el compilador copia y pega fragmentos de código antes de compilar.

Ese enfoque funciona, pero también es peligroso.

Las macros del preprocesador de C, por ejemplo, no conocen el contexto del código, lo que puede generar errores difíciles de detectar:


#define square(x) x * x

int y = square(1 + 2); // => 1 + 2 * 1 + 2 => 5 (no 9)


Elixir, en cambio, hereda su modelo de Lisp, donde el código es una estructura de datos manipulable.

Eso significa que una macro no reemplaza texto, sino que transforma el árbol sintáctico abstracto (AST) del programa.

Pero Elixir va más allá: además de permitir manipular el AST, garantiza que las variables y nombres dentro de la macro no se mezclen accidentalmente con las del contexto donde se usa.

A eso se le llama higiene.

Una macro higiénica respeta el ámbito léxico del código.

Cuando definís una macro en Elixir, las variables que declares dentro de ella no interferirán con las variables del lugar donde se expanda la macro.


Veamos un ejemplo simple:


defmodule Ejemplo do

  defmacro saludo do

    quote do

      nombre = "Mundo"

      IO.puts("Hola " <> nombre)

    end

  end

end


nombre = "Elixir"

Ejemplo.saludo()

IO.puts(nombre)


Salida:

Hola Mundo

Elixir


A pesar de que tanto el código de la macro como el código del usuario usan la variable nombre, no hay conflicto.

Elixir “higieniza” los nombres internos durante la expansión para evitar interferencias.

Cada variable dentro del quote se convierte en una entidad única, ligada al módulo donde la macro fue definida, no al contexto donde se usa.


Elixir permite construir y manipular su propio AST a través de dos primitivas esenciales: quote y unquote.

  • quote captura el código sin ejecutarlo, devolviendo su representación estructurada.
  • unquote permite insertar expresiones evaluadas dentro de ese AST.


Por ejemplo:


expr = quote do: a + b

IO.inspect(expr)

# {:+, [context: Elixir, import: Kernel], [:a, :b]}


Una macro se define usando quote para construir el código que se insertará, y unquote para interpolar valores dentro de él.

El resultado es metaprogramación estructurada, no textual.

Esto significa que Elixir “entiende” el código que genera, lo valida, y lo protege contra colisiones de nombres.


Hay ocasiones en las que uno quiere que una macro interactúe con el contexto del usuario, por ejemplo, para definir variables o modificar el entorno del módulo.

En esos casos, Elixir permite romper la higiene intencionalmente con var! o con opciones explícitas en quote.


defmacro define_nombre do

  quote do

    var!(nombre) = "Definido desde macro"

  end

end


define_nombre()

IO.puts(nombre)

# => "Definido desde macro"


Pero hacerlo implica responsabilidad: al romper la higiene, se pierde aislamiento.

Por eso, Elixir te obliga a hacerlo explícitamente —una decisión de diseño que mantiene el sistema seguro por defecto.

Mientras lenguajes como C ofrecen macros puramente textuales y Rust introduce un sistema de macros basado en AST pero sin aislamiento total, Elixir logra una síntesis elegante:

  • la expresividad y poder de Lisp,
  • la seguridad léxica del tipado moderno,
  • y la claridad de un lenguaje que prioriza la legibilidad.


El resultado es un sistema de macros que permite extender el lenguaje sin riesgo de romperlo.