@vidal747/mariadb-pool-cuenti
v1.0.4
Published
Gestor inteligente de pools de conexiones para MariaDB/MySQL que maneja múltiples bases de datos de empresas (multi-tenant) de forma dinámica y eficiente
Maintainers
Readme
MariaDB Pool Cuenti
Un gestor inteligente de pools de conexiones para MariaDB/MySQL que maneja múltiples bases de datos de empresas (multi-tenant) de forma dinámica y eficiente.
🌟 Características
- Multi-tenant: Maneja conexiones a múltiples bases de datos de empresas
- Pool dinámico: Crea y gestiona pools de conexiones bajo demanda
- Inicialización perezosa: Solo inicializa conexiones cuando se necesitan
- Configuración centralizada: Almacena configuración de servidores en base de datos
- Soporte para proxy: Maneja tanto conexiones directas como pools
- Query helpers: Métodos auxiliares para consultas SQL con parámetros nombrados
- TypeScript: Completamente tipado para mejor experiencia de desarrollo
📦 Instalación
npm install mariadb-pool-cuenti🔧 Configuración
Variables de entorno
Crea un archivo .env en tu proyecto con las siguientes variables:
# Configuración de la base de datos principal
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=tu_password_aqui
DB_NAME=nombre_base_datos
DB_PORT=3306
DB_POOL_SIZE=5
# Configuración de pools de tenant (opcional)
TENANT_POOL_SIZE=10Estructura de base de datos requerida
El paquete necesita estas tablas en tu base de datos principal:
-- Tabla de servidores
CREATE TABLE Servidores (
id INT PRIMARY KEY AUTO_INCREMENT,
ip VARCHAR(255) NOT NULL,
puerto INT NOT NULL DEFAULT 3306,
usuario VARCHAR(255) NOT NULL,
clave VARCHAR(255) NOT NULL,
es_proxy TINYINT(1) DEFAULT 0,
initialSize INT DEFAULT 5,
maxActive INT DEFAULT 10,
maxIdle INT DEFAULT 5,
maxTiempoInatividad INT DEFAULT 300,
maxTiempoEsperaEntregarConecion INT DEFAULT 60
);
-- Tabla de aplicaciones por empresa
CREATE TABLE aplicaciones_empresa (
id INT PRIMARY KEY AUTO_INCREMENT,
id_empresa INT NOT NULL,
id_servidor INT NOT NULL,
nombreBaseDatos VARCHAR(255) NOT NULL,
FOREIGN KEY (id_servidor) REFERENCES Servidores(id)
);🚀 Uso básico
Importación
import dbManager from "mariadb-pool-cuenti";
// o
import { DBManager } from "mariadb-pool-cuenti";Obtener conexión de empresa
// Obtener conexión para una empresa específica
const conn = await dbManager.getConnectionEmpresa(123);
try {
// Realizar consultas
const usuarios = await conn.query("SELECT * FROM usuarios");
console.log(usuarios);
} finally {
// Siempre liberar la conexión
conn.release();
}Usando el pool principal
// Para consultas en la base de datos principal
const conn = await dbManager.getPoolBases();
try {
const empresas = await conn.query("SELECT * FROM aplicaciones_empresa");
console.log(empresas);
} finally {
conn.release();
}📚 API Detallada
DBManager.getInstance()
Obtiene la instancia singleton del gestor.
const dbManager = DBManager.getInstance();getConnectionEmpresa(idEmpresa: number): Promise<ExtendedConnection>
Obtiene una conexión a la base de datos de una empresa específica.
Parámetros:
idEmpresa: ID numérico de la empresa
Retorna: Promise<ExtendedConnection> - Conexión extendida con métodos auxiliares
Ejemplo:
const conn = await dbManager.getConnectionEmpresa(123);
// La conexión ya está configurada para usar la BD de la empresa 123getPoolBases(): Promise<ExtendedConnection>
Obtiene una conexión al pool principal (base de datos de configuración).
Retorna: Promise<ExtendedConnection> - Conexión al pool principal
borrarEmpresaServer(idEmpresa: number): void
Elimina una empresa del cache interno, forzando reconfiguración en próximo acceso.
Parámetros:
idEmpresa: ID de la empresa a eliminar del cache
isReady(): boolean
Verifica si el servicio está inicializado y listo para usar.
Retorna: boolean - true si está listo
closeAll(): Promise<void>
Cierra todas las conexiones y pools de manera ordenada.
🔍 ExtendedConnection
Las conexiones retornadas incluyen métodos auxiliares adicionales:
queryFormat(query: string, params?: QueryParams): string
Formatea una consulta SQL reemplazando parámetros nombrados.
const sql = conn.queryFormat(
"SELECT * FROM usuarios WHERE id = :id AND activo = :activo",
{ id: 123, activo: 1 }
);
// Resultado: "SELECT * FROM usuarios WHERE id = 123 AND activo = 1"query2<T>(query: string, params?: QueryParams): Promise<T>
Ejecuta una consulta SQL con parámetros nombrados.
const usuarios = await conn.query2<Usuario[]>(
"SELECT * FROM usuarios WHERE edad > :edad AND ciudad = :ciudad",
{ edad: 18, ciudad: "Madrid" }
);💡 Ejemplos avanzados
Manejo completo de conexiones
import dbManager from "mariadb-pool-cuenti";
async function obtenerDatosEmpresa(idEmpresa: number) {
let conn;
try {
// Verificar que el servicio esté listo
if (!dbManager.isReady()) {
console.log("Servicio inicializándose...");
}
// Obtener conexión
conn = await dbManager.getConnectionEmpresa(idEmpresa);
// Consulta con parámetros nombrados
const usuarios = await conn.query2<any[]>(
"SELECT * FROM usuarios WHERE activo = :activo",
{ activo: 1 }
);
return usuarios;
} catch (error) {
console.error("Error al obtener datos:", error);
throw error;
} finally {
// Siempre liberar la conexión
if (conn) {
conn.release();
}
}
}Usando con Express.js
import express from "express";
import dbManager from "mariadb-pool-cuenti";
const app = express();
app.get("/empresas/:id/usuarios", async (req, res) => {
const idEmpresa = parseInt(req.params.id);
try {
const conn = await dbManager.getConnectionEmpresa(idEmpresa);
const usuarios = await conn.query2(
"SELECT id, nombre, email FROM usuarios WHERE activo = :activo",
{ activo: 1 }
);
res.json(usuarios);
conn.release();
} catch (error) {
console.error("Error:", error);
res.status(500).json({ error: "Error interno del servidor" });
}
});
// Graceful shutdown
process.on("SIGTERM", async () => {
console.log("Cerrando conexiones...");
await dbManager.closeAll();
process.exit(0);
});Transacciones
async function transferirFondos(
idEmpresa: number,
fromUser: number,
toUser: number,
amount: number
) {
const conn = await dbManager.getConnectionEmpresa(idEmpresa);
try {
await conn.beginTransaction();
// Debitar cuenta origen
await conn.query2(
"UPDATE cuentas SET saldo = saldo - :amount WHERE usuario_id = :userId",
{ amount, userId: fromUser }
);
// Acreditar cuenta destino
await conn.query2(
"UPDATE cuentas SET saldo = saldo + :amount WHERE usuario_id = :userId",
{ amount, userId: toUser }
);
await conn.commit();
console.log("Transferencia completada");
} catch (error) {
await conn.rollback();
console.error("Error en transferencia:", error);
throw error;
} finally {
conn.release();
}
}🏗️ Arquitectura
Flujo de funcionamiento
- Inicialización perezosa: El servicio no se conecta hasta que se solicite una conexión
- Consulta de configuración: Al solicitar una empresa, consulta su configuración en la BD principal
- Creación de pool: Si es la primera vez, crea un pool para ese servidor
- Cache interno: Mantiene cache de configuraciones para evitar consultas repetidas
- Conexión optimizada: Reutiliza pools existentes para empresas del mismo servidor
Tipos de conexión
- Pool (es_proxy = 0): Crea un pool de conexiones reutilizables
- Directa (es_proxy = 1): Crea conexiones directas sin pool
🔒 Mejores prácticas
1. Siempre liberar conexiones
// ✅ Correcto
const conn = await dbManager.getConnectionEmpresa(123);
try {
// usar conexión
} finally {
conn.release(); // Siempre liberar
}
// ❌ Incorrecto - puede causar memory leaks
const conn = await dbManager.getConnectionEmpresa(123);
const data = await conn.query("SELECT * FROM tabla");
// Conexión no liberada2. Manejo de errores
try {
const conn = await dbManager.getConnectionEmpresa(123);
// operaciones
conn.release();
} catch (error) {
if (error.message.includes("no configurada")) {
console.log("Empresa no existe en configuración");
}
throw error;
}3. Graceful shutdown
// En tu aplicación principal
process.on("SIGTERM", async () => {
await dbManager.closeAll();
process.exit(0);
});4. Validación de empresas
// Verificar si una empresa existe antes de usarla
async function validarEmpresa(idEmpresa: number): Promise<boolean> {
try {
const conn = await dbManager.getConnectionEmpresa(idEmpresa);
conn.release();
return true;
} catch (error) {
if (error.message.includes("no configurada")) {
return false;
}
throw error;
}
}🐛 Debugging
Logs del sistema
El paquete emite logs detallados para debugging:
[BASE] Inicializando pool de conexiones principal...
[BASE] Pool principal inicializado correctamente
[TENANT] Obteniendo conexión para empresa 123
[TENANT] Consultando configuración para empresa 123
[TENANT] Creando nuevo servidor 1 para empresa 123
[TENANT] Conexión establecida para empresa 123 en BD: empresa_123Variables de entorno para debugging
# Para más logs de MariaDB
DEBUG=mariadb:*📄 Licencia
MIT
🤝 Contribuir
Las contribuciones son bienvenidas. Por favor:
- Fork el proyecto
- Crea una rama para tu feature
- Commit tus cambios
- Push a tu rama
- Abre un Pull Request
📞 Soporte
Para reportar bugs o solicitar features, por favor abre un issue en el repositorio.
Nota: Este paquete está diseñado para aplicaciones multi-tenant que requieren gestión dinámica de conexiones a bases de datos por empresa.
