mirror of
https://github.com/taylanbakircioglu/haproxy-openmanager.git
synced 2026-09-12 05:48:58 +00:00
1ea1c6a29f
This commit consolidates multiple improvements from internal development: ## Agent Stability Improvements - Add database connection pooling (min=10, max=50) for better performance - Prevent config reapply on agent restart by fetching last_applied_version from database - Optimize SSL fetch to only run when config changes (98% API call reduction) - Make SSL_SYNC_TIMESTAMP_FILE agent-specific to prevent race conditions - Fix agent offline display issue due to database connection bottleneck - 10x faster heartbeat response (200ms → 20ms) ## Bulk Import UPSERT Support - Parse endpoint detects existing entities (New/Existing status) - Bulk-create supports UPDATE for existing backends/frontends (merge strategy) - New servers can be added to existing backends - Existing servers preserved (no deletion in MVP) - Field-by-field value comparison (only changed fields updated) - Pending apply conflict prevention (409 error) - Fixed duplicate key error on server INSERT - Backend marked PENDING when servers added ## Apply Management Fixes - Fixed deleted entities not showing (include_inactive parameter) - Backend/Frontend GET endpoints support inactive entities for Apply Management - All pending changes now visible - Phantom backend bug protection maintained ## Backend Delete Improvements - Automatically clean ACL/use_backend rules from frontends - Prevents HAProxy validation errors after backend deletion - Frontend references automatically updated ## UI/UX Improvements - Cluster selector status dot auto-refreshes every 30 seconds - Real-time agent health monitoring (no page refresh needed) - Parse message shows only NEW entities (cleaner) - Status labels: 'Update' → 'Existing' (clearer meaning) - Multi-line parse success messages - Detailed summary breakdown with tooltips Technical Changes: - backend/database/connection.py: Connection pool implementation - backend/main.py: Pool initialization and cleanup - backend/routers/*: UPSERT logic, field comparison, include_inactive - backend/utils/agent_scripts/*: Applied version tracking, SSL optimization - frontend/src/components/*: UI improvements, status indicators - frontend/src/contexts/ClusterContext.js: Auto-refresh agent health Impact: - Supports 50+ concurrent agents (previously ~10) - Zero config reapply on restart/upgrade - Bulk import handles existing entities correctly - All pending changes visible in Apply Management - Real-time cluster health status - No HAProxy validation errors after backend delete
86 lines
2.7 KiB
Python
86 lines
2.7 KiB
Python
import asyncpg
|
|
import redis
|
|
import logging
|
|
from config import DATABASE_URL, REDIS_URL
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Redis client instance
|
|
redis_client = redis.Redis.from_url(REDIS_URL)
|
|
|
|
# Database connection pool instance
|
|
_connection_pool = None
|
|
|
|
async def init_database_pool():
|
|
"""
|
|
Initialize database connection pool
|
|
|
|
This replaces the previous per-request connection approach with a connection pool
|
|
to improve performance and prevent connection exhaustion under high load.
|
|
|
|
Pool configuration:
|
|
- min_size=10: Maintain at least 10 connections ready
|
|
- max_size=50: Allow up to 50 concurrent connections
|
|
- command_timeout=60: Queries timeout after 60 seconds
|
|
- max_inactive_connection_lifetime=300: Recycle idle connections after 5 minutes
|
|
"""
|
|
global _connection_pool
|
|
if _connection_pool is None:
|
|
try:
|
|
_connection_pool = await asyncpg.create_pool(
|
|
DATABASE_URL,
|
|
min_size=10,
|
|
max_size=50,
|
|
command_timeout=60,
|
|
max_inactive_connection_lifetime=300
|
|
)
|
|
logger.info("✅ Database connection pool initialized (min=10, max=50)")
|
|
except Exception as e:
|
|
logger.error(f"❌ Failed to initialize database connection pool: {e}")
|
|
raise
|
|
return _connection_pool
|
|
|
|
async def get_database_connection():
|
|
"""
|
|
Get a database connection from the connection pool
|
|
|
|
This function now uses connection pooling instead of creating new connections.
|
|
Connections are automatically returned to the pool when closed.
|
|
"""
|
|
global _connection_pool
|
|
try:
|
|
if _connection_pool is None:
|
|
await init_database_pool()
|
|
return await _connection_pool.acquire()
|
|
except Exception as e:
|
|
logger.error(f"Failed to acquire database connection from pool: {e}")
|
|
raise
|
|
|
|
async def close_database_connection(conn):
|
|
"""
|
|
Release a database connection back to the pool
|
|
|
|
The connection is returned to the pool for reuse, not actually closed.
|
|
"""
|
|
global _connection_pool
|
|
try:
|
|
if _connection_pool and conn:
|
|
await _connection_pool.release(conn)
|
|
except Exception as e:
|
|
logger.error(f"Failed to release database connection to pool: {e}")
|
|
|
|
async def close_database_pool():
|
|
"""
|
|
Close the database connection pool gracefully
|
|
|
|
Should be called during application shutdown.
|
|
"""
|
|
global _connection_pool
|
|
if _connection_pool:
|
|
await _connection_pool.close()
|
|
logger.info("Database connection pool closed")
|
|
_connection_pool = None
|
|
|
|
def get_redis_client():
|
|
"""Get the Redis client instance"""
|
|
return redis_client |