Saltearse al contenido

Configuración de Base de Datos

Tabla de Contenidos

  1. Propósito
  2. ¿Para quién es esto?
  3. Descripción General de Arquitectura de Base de Datos
  4. Configuración Local de MongoDB
  5. Configuración Cloud de MongoDB
  6. Configuración de Base de Datos
  7. Gestión de Esquemas
  8. Estrategia de Indexación
  9. Respaldo y Restauración
  10. Monitoreo y Mantenimiento
  11. Solución de Problemas

Propósito

Esta guía proporciona instrucciones completas para configurar, configurar y mantener bases de datos MongoDB en la plataforma Algesta. Cubre configuración de desarrollo local, despliegue en la nube, agrupación de conexiones, gestión de esquemas y tareas operacionales como respaldo/restauración.

Siguiendo esta guía, podrás:

  • Configurar MongoDB localmente para desarrollo
  • Configurar instancias de MongoDB Atlas u hospedadas en la nube
  • Entender cadenas de conexión y agrupación
  • Gestionar esquemas de base de datos e índices
  • Realizar operaciones de respaldo y restauración
  • Monitorear salud y rendimiento de base de datos

¿Para quién es esto?

Esta guía es para desarrolladores configurando bases de datos locales, ingenieros DevOps aprovisionando bases de datos en la nube, y administradores de bases de datos gestionando datos de producción. Asume familiaridad con MongoDB, bases de datos de documentos y administración básica de bases de datos.


Descripción General de Arquitectura de Base de Datos

La plataforma Algesta implementa un patrón de base-de-datos-por-servicio, donde cada microservicio posee su base de datos MongoDB dedicada para aislamiento de datos y autonomía.

Estrategia de Base de Datos

MicroservicioNombre de Base de DatosPropósitoTamaño Estimado
Orders MSorders_msCiclo de vida de órdenes, cotizaciones, activos10+ GB
Notifications MSnotifications_msSeguimiento de notificaciones1 GB
Provider MSproviders_msProveedores, subastas, documentos5 GB

Principios Clave:

  • Aislamiento de Datos: Cada servicio tiene acceso exclusivo a su base de datos
  • Autonomía: Los servicios pueden evolucionar esquemas independientemente
  • Consistencia Eventual: Datos entre servicios sincronizados vía eventos (Kafka)
  • Desnormalización: Duplicar datos donde sea necesario para rendimiento

Versión de MongoDB: 7.0+ (especificado en Docker Compose y configuraciones de despliegue) ODM: Mongoose 8.16+ (ver package.json en cada microservicio)

Referencia: Ver Esquemas de Base de Datos para documentación detallada de esquemas.


Configuración Local de MongoDB

Opción 1: Instalación Nativa de MongoDB

macOS (Homebrew):

Ventana de terminal
# Install MongoDB Community Edition
brew tap mongodb/brew
brew install mongodb-community@7.0
# Start MongoDB service
brew services start mongodb-community@7.0
# Verify installation
mongosh --eval "db.version()"
# Expected: 7.0.x

Linux (Ubuntu/Debian):

Ventana de terminal
# Import MongoDB public GPG key
wget -qO- https://www.mongodb.org/static/pgp/server-7.0.asc | sudo tee /etc/apt/trusted.gpg.d/server-7.0.asc
# Add MongoDB repository
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
# Update and install
sudo apt-get update
sudo apt-get install -y mongodb-org
# Start MongoDB
sudo systemctl start mongod
sudo systemctl enable mongod
# Verify
mongosh --eval "db.version()"

Windows: Descargar instalador desde mongodb.com/try/download/community y seguir el instalador GUI.

Verificación:

Ventana de terminal
# Connect to MongoDB
mongosh
# In mongosh shell:
> db.version()
7.0.x
> show dbs
admin 40.00 KiB
config 60.00 KiB
local 40.00 KiB

Opción 2: MongoDB en Docker

Ejecutar Contenedor de MongoDB:

Ventana de terminal
docker run -d \
--name algesta-mongodb \
-p 27017:27017 \
-v algesta-mongo-data:/data/db \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=password \
mongo:7.0

Verificación:

Ventana de terminal
# Verificar contenedor en ejecución
docker ps | grep algesta-mongodb
# Conectar a MongoDB
mongosh "mongodb://admin:password@localhost:27017/?authSource=admin"
# Probar conexión
> db.runCommand({ ping: 1 })
{ ok: 1 }

Persistencia: Los datos se almacenan en el volumen de Docker algesta-mongo-data, sobreviven reinicios del contenedor.

Detener/Iniciar:

Ventana de terminal
docker stop algesta-mongodb
docker start algesta-mongodb

Crear bases de datos

Para desarrollo local, crear bases de datos para cada servicio:

Ventana de terminal
mongosh
# Switch to orders database (creates if doesn't exist)
> use orders_ms
switched to db orders_ms
# Insert a test document to persist database
> db.test.insertOne({ created: new Date() })
# Repeat for other databases
> use notifications_ms
> db.test.insertOne({ created: new Date() })
> use providers_ms
> db.test.insertOne({ created: new Date() })
# Verify databases created
> show dbs
orders_ms 40.00 KiB
notifications_ms 40.00 KiB
providers_ms 40.00 KiB

Configuración Cloud de MongoDB

Opción 1: MongoDB Atlas (Recomendado)

1. Crear Cuenta de Atlas:

2. Configurar Cluster:

  • Proveedor: GCP (coincide con despliegue de Cloud Run)
  • Región: us-east1 (o más cercana a servicios de Cloud Run)
  • Nivel de Cluster: M0 (gratuito, 512 MB) para desarrollo, M10+ para producción

3. Crear Usuario de Base de Datos:

Usuario: algesta_admin
Contraseña: <generar contraseña fuerte>
Roles: Lectura y escritura en cualquier base de datos

4. Acceso de Red:

  • Para desarrollo: Agregar tu dirección IP
  • Para Cloud Run: Agregar 0.0.0.0/0 (permitir todos) o usar VPC Peering

5. Obtener Cadena de Conexión:

mongodb+srv://algesta_admin:<password>@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority

6. Crear bases de datos:

  • Hacer clic en “Browse Collections” → “Add My Own Data”
  • Crear: orders_ms, notifications_ms, providers_ms

Verificación:

Ventana de terminal
# Test connection
mongosh "mongodb+srv://algesta_admin:<password>@cluster0.xxxxx.mongodb.net/"
> show dbs
orders_ms ...
notifications_ms ...
providers_ms ...

Opción 2: GCP Cloud SQL para MongoDB (Futuro)

Nota: GCP no soporta MongoDB nativamente. Usar MongoDB Atlas (región GCP) o auto-hospedado en GCE.

Auto-hospedado en GCE:

  1. Crear instancia de Compute Engine
  2. Instalar MongoDB 7.0
  3. Configurar reglas de firewall (permitir puerto 27017 desde Cloud Run)
  4. Configurar replica set para alta disponibilidad

Configuración de Base de Datos

Cadenas de Conexión

Formato:

mongodb://[username:password@]host[:port]/database[?options]

Ejemplos:

Local (sin autenticación):

MONGODB_URI=mongodb://localhost:27017/orders_ms

Local (con autenticación):

MONGODB_URI=mongodb://admin:password@localhost:27017/orders_ms?authSource=admin

MongoDB Atlas:

MONGODB_URI=mongodb+srv://algesta_admin:password@cluster0.xxxxx.mongodb.net/orders_ms?retryWrites=true&w=majority

Producción (Replica Set):

MONGODB_URI=mongodb://user:pass@mongo1.example.com:27017,mongo2.example.com:27017,mongo3.example.com:27017/orders_ms?replicaSet=rs0&authSource=admin

Configuración de Pool de Conexiones

Ubicación: algesta-ms-orders-nestjs/src/shared/infrastructure/database/database.config.ts

export const getMongoConfig = (): MongoConnectionOptions => {
const isProduction = process.env.NODE_ENV === "production";
const uri =
process.env.MONGODB_URI ?? "mongodb://localhost:27017/documents_ms";
return {
uri,
options: {
maxPoolSize: parseInt(process.env.DB_POOL_MAX ?? "20", 10),
minPoolSize: parseInt(process.env.DB_POOL_MIN ?? "5", 10),
maxIdleTimeMS: parseInt(process.env.DB_POOL_IDLE_TIMEOUT ?? "30000", 10),
connectTimeoutMS: parseInt(
process.env.DB_POOL_CONNECTION_TIMEOUT ?? "2000",
10
),
socketTimeoutMS: parseInt(process.env.DB_SOCKET_TIMEOUT ?? "30000", 10),
serverSelectionTimeoutMS: parseInt(
process.env.DB_SERVER_SELECTION_TIMEOUT ?? "5000",
10
),
bufferCommands: !isProduction,
},
};
};

Configuraciones Clave:

ConfiguraciónDesarrolloProducciónPropósito
maxPoolSize2050Máximo de conexiones en pool
minPoolSize510Mínimo de conexiones mantenidas abiertas
maxIdleTimeMS30000 (30s)30000Cerrar conexiones inactivas
connectTimeoutMS2000 (2s)5000 (5s)Timeout para nuevas conexiones
socketTimeoutMS30000 (30s)45000 (45s)Timeout para operaciones de socket
serverSelectionTimeoutMS5000 (5s)10000 (10s)Timeout para encontrar servidor MongoDB
bufferCommandstruefalseAlmacenar comandos si está desconectado

Ajuste:

  • Aumentar maxPoolSize para servicios de alto tráfico
  • Disminuir connectTimeoutMS para detección rápida de fallos
  • Habilitar retryWrites=true en cadena de conexión para producción

Verificación:

// In main.ts, log connection status
mongoose.connection.on("connected", () => {
console.log("MongoDB connected:", mongoose.connection.name);
console.log("Pool size:", mongoose.connection.db.serverConfig.s.poolSize);
});

Gestión de Esquemas

Definición de Esquemas con Mongoose

Algesta usa esquemas de Mongoose (enfoque code-first), no esquemas manuales de MongoDB.

Ejemplo: algesta-ms-orders-nestjs/src/domain/entities/order.entity.ts

import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Document } from "mongoose";
@Schema({ timestamps: true })
export class Order extends Document {
@Prop({ required: true, unique: true })
orderId: string;
@Prop({ required: true, enum: ["CALL_CENTER", "WEB", "MOBILE", "ADMIN"] })
channel: string;
@Prop({ required: true })
service: string;
@Prop({ required: true })
address: string;
@Prop({ required: true })
city: string;
@Prop({ type: Object })
assignedProvider?: {
providerId: string;
providerName: string;
providerEmail: string;
assignedAt: Date;
};
@Prop({ type: [String] })
assetIds?: string[];
// Mongoose adds createdAt, updatedAt automatically
}
export const OrderSchema = SchemaFactory.createForClass(Order);

Funcionalidades Clave:

  • Decoradores: @Schema(), @Prop() definen esquema
  • Validación: required, enum, unique
  • Documentos Embebidos: type: Object para sub-documentos
  • Arreglos: type: [String] para arreglos
  • Timestamps: timestamps: true agrega createdAt, updatedAt

Evolución de Esquemas

Algesta NO tiene un sistema formal de migraciones (deuda técnica).

Enfoque Actual:

  1. Actualizar esquema de Mongoose en código
  2. Desplegar nuevo código
  3. MongoDB permite evolución sin esquema (documentos antiguos coexisten con nuevos)
  4. Opcional: Ejecutar script manual para actualizar documentos existentes

Ejemplo de Script de Migración:

scripts/migrate-add-status-field.js
const mongoose = require("mongoose");
const { Order } = require("../dist/domain/entities/order.entity");
async function migrate() {
await mongoose.connect(process.env.MONGODB_URI);
const result = await Order.updateMany(
{ status: { $exists: false } },
{ $set: { status: "NEW" } }
);
console.log(`Updated ${result.modifiedCount} documents`);
await mongoose.disconnect();
}
migrate();

Ejecutar Migración:

Ventana de terminal
MONGODB_URI=mongodb://localhost:27017/orders_ms node scripts/migrate-add-status-field.js

Mejor Práctica: Probar migraciones en copia de datos de producción primero.

Mejora Futura: Implementar framework de migraciones (ej., migrate-mongo).


Estrategia de Indexación

Los índices son críticos para el rendimiento de consultas. Algesta usa creación manual de índices (no auto-indexados por Mongoose en producción).

Crear Índices

En MongoDB Shell:

use orders_ms;
// Unique index on orderId
db.orders.createIndex({ orderId: 1 }, { unique: true });
// Compound index for common queries
db.orders.createIndex({ status: 1, createdAt: -1 });
// Index on embedded fields
db.orders.createIndex({ "assignedProvider.providerId": 1 });
// Index on array fields
db.orders.createIndex({ assetIds: 1 });

Verificación:

db.orders.getIndexes();

Salida Esperada:

[
{ "v": 2, "key": { "_id": 1 }, "name": "_id_" },
{ "v": 2, "key": { "orderId": 1 }, "name": "orderId_1", "unique": true },
{
"v": 2,
"key": { "status": 1, "createdAt": -1 },
"name": "status_1_createdAt_-1"
}
]

Índices Recomendados por Servicio

Orders MS (orders_ms):

db.orders.createIndex({ orderId: 1 }, { unique: true });
db.orders.createIndex({ status: 1 });
db.orders.createIndex({ channel: 1 });
db.orders.createIndex({ phoneNumber: 1 });
db.orders.createIndex({ createdAt: -1 });
db.orders.createIndex({ "assignedProvider.providerId": 1 });
db.orders.createIndex({ assetIds: 1 });
db.orders.createIndex({ status: 1, createdAt: -1 }); // Compound

Notifications MS (notifications_ms):

db.notifications.createIndex({ userId: 1 });
db.notifications.createIndex({ status: 1 });
db.notifications.createIndex({ createdAt: -1 });
db.notifications.createIndex({ userId: 1, status: 1 }); // Compound
// TTL Index (auto-delete after 90 days)
db.notifications.createIndex({ createdAt: 1 }, { expireAfterSeconds: 7776000 });

Provider MS (providers_ms):

db.providers.createIndex({ email: 1 }, { unique: true });
db.providers.createIndex({ identification: 1 });
db.providers.createIndex({ isActive: 1 });
db.auction.createIndex({ auctionId: 1 }, { unique: true });
db.auction.createIndex({ orderId: 1 });
db.auction.createIndex({ "auctionBids.providerId": 1 });
db.auction.createIndex({ "auctionBids.isCurrent": 1 });
db.auction.createIndex({ orderId: 1, "auctionBids.providerId": 1 }); // Compound

Monitoreo de Índices

Verificar Uso de Índices:

db.orders.aggregate([{ $indexStats: {} }]);

Perfilado de Consultas Lentas:

// Enable profiling (log queries > 100ms)
db.setProfilingLevel(1, { slowms: 100 });
// View slow queries
db.system.profile.find().sort({ ts: -1 }).limit(10);

Respaldo y Restauración

Estrategias de Respaldo

Desarrollo Local:

Ventana de terminal
# Backup single database
mongodump --uri="mongodb://localhost:27017/orders_ms" \
--gzip \
--archive="backups/orders_ms-$(date +%Y%m%d).gz"
# Backup all databases
mongodump --uri="mongodb://localhost:27017" \
--gzip \
--archive="backups/all-dbs-$(date +%Y%m%d).gz"

Verificación:

Ventana de terminal
ls -lh backups/
# Esperado: orders_ms-20251120.gz (archivo comprimido)

Producción (MongoDB Atlas):

  • Respaldos automatizados habilitados por defecto (cada 24 horas)
  • Retención: 2 días (tier gratuito), configurable para tiers pagos
  • Respaldos manuales: Atlas UI → Backup → Take Snapshot

Procedimientos de Restauración

Restaurar desde Respaldo:

Ventana de terminal
# Restore to local MongoDB
mongorestore --uri="mongodb://localhost:27017/orders_ms" \
--gzip \
--archive="backups/orders_ms-20251120.gz" \
--drop

Flags:

  • --drop: Eliminar colecciones existentes antes de restaurar (destructivo!)
  • --gzip: Descomprimir archivo gzipped
  • --archive: Restaurar desde archivo comprimido

Restaurar a Diferente Base de Datos:

Ventana de terminal
mongorestore --uri="mongodb://localhost:27017/orders_ms_restore" \
--gzip \
--archive="backups/orders_ms-20251120.gz"

Verificación:

Ventana de terminal
mongosh mongodb://localhost:27017/orders_ms_restore
> db.orders.countDocuments()
12345 # Verificar que el conteo de documentos coincida con el respaldo

Exportar/Importar Colecciones Específicas

Exportar a JSON:

Ventana de terminal
mongoexport --uri="mongodb://localhost:27017/orders_ms" \
--collection=orders \
--out=orders-export.json \
--jsonArray

Importar desde JSON:

Ventana de terminal
mongoimport --uri="mongodb://localhost:27017/orders_ms" \
--collection=orders \
--file=orders-export.json \
--jsonArray

Monitoreo y Mantenimiento

Consultas de Monitoreo

Verificar Estadísticas de Base de Datos:

db.orders.stats();

Salida de Ejemplo:

{
"ns": "orders_ms.orders",
"size": 52428800, // 50 MB
"count": 10000,
"avgObjSize": 5242,
"storageSize": 20971520, // 20 MB (compressed)
"totalIndexSize": 4194304, // 4 MB
"indexSizes": {
"_id_": 2097152,
"orderId_1": 1048576,
"status_1_createdAt_-1": 1048576
}
}

Monitorear Conexiones:

db.serverStatus().connections;

Salida de Ejemplo:

{
"current": 12, // Active connections
"available": 51188, // Max connections
"totalCreated": 450
}

Tareas de Mantenimiento

Compactar Colecciones (Recuperar Espacio):

db.runCommand({ compact: "orders" });

Reconstruir Índices:

db.orders.reIndex();

Eliminar Datos Antiguos (Limpieza Manual):

// Delete notifications older than 90 days
db.notifications.deleteMany({
createdAt: { $lt: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000) },
});

Verificar Retraso de Replicación (Replica Sets):

rs.printReplicationInfo();

Solución de Problemas

Problema: No se puede conectar a MongoDB desde servicio NestJS

Diagnóstico:

Ventana de terminal
# Check MongoDB running
mongosh --eval "db.version()"
# Test connection string
mongosh "mongodb://localhost:27017/orders_ms" --eval "db.runCommand({ ping: 1 })"

Soluciones:

  1. Verificar MONGODB_URI en .env:
    MONGODB_URI=mongodb://localhost:27017/orders_ms
  2. Verificar que MongoDB esté escuchando en el puerto correcto:
    Ventana de terminal
    lsof -i :27017
  3. Para MongoDB en Docker, usar host.docker.internal en lugar de localhost desde contenedores
  4. Verificar reglas de firewall (despliegues en la nube)

Problema: Consultas lentas, degradación de rendimiento

Diagnóstico:

// Enable profiling
db.setProfilingLevel(1, { slowms: 100 });
// Find slow queries
db.system.profile
.find({ millis: { $gt: 100 } })
.sort({ ts: -1 })
.limit(10);

Soluciones:

  1. Verificar si existen índices:
    db.orders.getIndexes();
  2. Analizar plan de ejecución de consulta:
    db.orders.find({ status: "NEW" }).explain("executionStats");
  3. Agregar índices faltantes basados en patrones de consulta
  4. Aumentar tamaño del pool de conexiones si las conexiones están agotadas

Problema: Error de clave duplicada en orderId

Diagnóstico:

MongoServerError: E11000 duplicate key error collection: orders_ms.orders index: orderId_1

Causa: Intentando insertar orden con orderId existente.

Soluciones:

  1. Verificar que la lógica de generación de orderId sea única (ej., UUID, basado en timestamp)
  2. Verificar condiciones de carrera en creación de órdenes
  3. Consultar orden existente antes de insertar:
    const existing = await this.orderModel.findOne({ orderId });
    if (existing) throw new ConflictException("Order already exists");

Problema: Pool de conexiones de base de datos agotado

Diagnóstico:

MongoServerError: connection pool is full

Soluciones:

  1. Aumentar maxPoolSize en database.config.ts:
    maxPoolSize: parseInt(process.env.DB_POOL_MAX ?? '50', 10),
  2. Verificar fugas de conexión (cursores no cerrados):
    db.serverStatus().connections.current;
  3. Monitorear consultas activas:
    db.currentOp({ active: true });
  4. Reiniciar servicio para resetear pool

Guías Relacionadas:

Para Soporte:

  • Revisar Documentación de Mongoose: mongoosejs.com
  • Consultar Documentación de MongoDB Atlas: docs.atlas.mongodb.com
  • Contactar equipo DevOps para acceso a base de datos de producción