ваше сообщение коммита

This commit is contained in:
2025-09-02 17:18:15 +03:00
parent a6360ccd2e
commit 53bb269b85
26 changed files with 580 additions and 243 deletions

View File

@@ -0,0 +1,210 @@
/**
* Сервис для динамического управления подключениями к базе данных
* Позволяет изменять настройки БД без перезапуска приложения
*/
const { Pool } = require('pg');
const logger = require('../utils/logger');
class DatabaseConnectionManager {
constructor() {
this.currentPool = null;
this.currentConfig = null;
this.connectionListeners = [];
this.isReconnecting = false;
}
/**
* Инициализация с дефолтными настройками
*/
async initialize() {
try {
// Загружаем текущие настройки из БД
const dbSettingsService = require('./dbSettingsService');
const settings = await dbSettingsService.getSettings();
if (settings) {
await this.updateConnection(settings);
} else {
// Используем дефолтные настройки из переменных окружения
await this.updateConnection({
db_host: process.env.DB_HOST || 'postgres',
db_port: parseInt(process.env.DB_PORT) || 5432,
db_name: process.env.DB_NAME || 'dapp_db',
db_user: process.env.DB_USER || 'dapp_user',
db_password: process.env.DB_PASSWORD || 'dapp_password'
});
}
logger.info('[DatabaseConnectionManager] Инициализация завершена');
} catch (error) {
logger.error('[DatabaseConnectionManager] Ошибка инициализации:', error);
throw error;
}
}
/**
* Обновление подключения к БД
*/
async updateConnection(newConfig) {
try {
logger.info('[DatabaseConnectionManager] Обновление подключения к БД...');
// Проверяем, изменились ли настройки
if (this.currentConfig && this.configsEqual(this.currentConfig, newConfig)) {
logger.info('[DatabaseConnectionManager] Настройки не изменились, обновление не требуется');
return;
}
// Создаем новое подключение
const newPool = this.createPool(newConfig);
// Тестируем новое подключение
await this.testConnection(newPool);
// Закрываем старое подключение
if (this.currentPool) {
await this.closePool(this.currentPool);
}
// Обновляем текущие настройки
this.currentPool = newPool;
this.currentConfig = { ...newConfig };
logger.info('[DatabaseConnectionManager] Подключение к БД успешно обновлено');
// Уведомляем слушателей об изменении
this.notifyConnectionChange();
} catch (error) {
logger.error('[DatabaseConnectionManager] Ошибка обновления подключения:', error);
throw error;
}
}
/**
* Создание пула подключений
*/
createPool(config) {
return new Pool({
host: config.db_host,
port: config.db_port,
database: config.db_name,
user: config.db_user,
password: config.db_password,
ssl: false,
max: 10,
min: 0,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
maxUses: 7500,
allowExitOnIdle: true,
maxLifetimeSeconds: 0
});
}
/**
* Тестирование подключения
*/
async testConnection(pool) {
const client = await pool.connect();
try {
await client.query('SELECT 1');
logger.info('[DatabaseConnectionManager] Тест подключения успешен');
} finally {
client.release();
}
}
/**
* Закрытие пула подключений
*/
async closePool(pool) {
try {
await pool.end();
logger.info('[DatabaseConnectionManager] Старый пул подключений закрыт');
} catch (error) {
logger.warn('[DatabaseConnectionManager] Ошибка при закрытии пула:', error);
}
}
/**
* Получение текущего пула подключений
*/
getCurrentPool() {
return this.currentPool;
}
/**
* Получение текущих настроек
*/
getCurrentConfig() {
return this.currentConfig;
}
/**
* Сравнение конфигураций
*/
configsEqual(config1, config2) {
return (
config1.db_host === config2.db_host &&
config1.db_port === config2.db_port &&
config1.db_name === config2.db_name &&
config1.db_user === config2.db_user &&
config1.db_password === config2.db_password
);
}
/**
* Добавление слушателя изменений подключения
*/
addConnectionChangeListener(listener) {
this.connectionListeners.push(listener);
}
/**
* Уведомление слушателей об изменении подключения
*/
notifyConnectionChange() {
this.connectionListeners.forEach(listener => {
try {
listener(this.currentConfig);
} catch (error) {
logger.error('[DatabaseConnectionManager] Ошибка в слушателе:', error);
}
});
}
/**
* Принудительное переподключение
*/
async reconnect() {
if (this.isReconnecting) {
logger.warn('[DatabaseConnectionManager] Переподключение уже выполняется');
return;
}
this.isReconnecting = true;
try {
if (this.currentConfig) {
await this.updateConnection(this.currentConfig);
}
} finally {
this.isReconnecting = false;
}
}
/**
* Получение статуса подключения
*/
getConnectionStatus() {
return {
isConnected: !!this.currentPool,
config: this.currentConfig,
isReconnecting: this.isReconnecting,
listenersCount: this.connectionListeners.length
};
}
}
module.exports = new DatabaseConnectionManager();