refactor: simplify Oracle pool to use only ORACLE_SERVERS format

- Remove legacy pool support (DSN, env vars fallback)
- Use first registered server when server_id not specified
- Show server dropdown even with single server in ORACLE_SERVERS
- Email login only available for 2+ servers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-02-03 15:15:57 +00:00
parent 6718c956f7
commit 2e1ead69e1
2 changed files with 26 additions and 99 deletions

View File

@@ -1,12 +1,11 @@
"""
Oracle Database Connection Pool - Multi-Server Support for ROA2WEB
Supports both single-server (backward compatible) and multi-server configurations.
Uses ORACLE_SERVERS from .env for server configuration.
Pool-uri sunt create lazy (la prima conexiune pe fiecare server) pentru optimizare.
"""
import asyncio
import oracledb
import os
from contextlib import asynccontextmanager
from typing import Optional, Dict, Any
import logging
@@ -21,14 +20,13 @@ class OracleMultiPool:
Supports:
- Multiple Oracle servers with separate pools: {server_id: pool}
- Lazy pool creation (created on first connection)
- Backward compatibility (default server when no server_id specified)
- First registered server used when no server_id specified
- Graceful shutdown of all pools
"""
_instance: Optional['OracleMultiPool'] = None
_pools: Dict[str, oracledb.ConnectionPool]
_pool_configs: Dict[str, Dict[str, Any]]
_pool_lock: asyncio.Lock
_legacy_pool: Optional[oracledb.ConnectionPool] # For backward compatibility
_initialized: bool
def __new__(cls):
@@ -37,77 +35,23 @@ class OracleMultiPool:
cls._instance._pools = {}
cls._instance._pool_configs = {}
cls._instance._pool_lock = asyncio.Lock()
cls._instance._legacy_pool = None
cls._instance._initialized = False
return cls._instance
async def initialize(self, **config):
async def initialize(self):
"""
Initialize pool manager.
For backward compatibility, this can:
1. Create a legacy single pool (if called with individual params)
2. Just mark as initialized (if using lazy multi-pool loading)
Call this after registering servers with register_server().
Pools are created lazily on first connection.
"""
if self._initialized:
logger.debug("Pool manager already initialized")
return
# Check if we have DSN or individual parameters (legacy mode)
dsn = config.get('dsn', os.getenv('ORACLE_DSN'))
user = config.get('user', os.getenv('ORACLE_USER'))
if dsn or user:
# Legacy single-pool mode - create pool immediately
await self._create_legacy_pool(config)
self._initialized = True
logger.info("Oracle pool manager initialized")
async def _create_legacy_pool(self, config: Dict[str, Any]) -> None:
"""Create legacy single pool for backward compatibility."""
dsn = config.get('dsn', os.getenv('ORACLE_DSN'))
if dsn:
# Use DSN connection
self._legacy_pool = oracledb.create_pool(
user=config.get('user', os.getenv('ORACLE_USER')),
password=config.get('password', os.getenv('ORACLE_PASSWORD')),
dsn=dsn,
min=config.get('min_connections', 2),
max=config.get('max_connections', 10),
increment=config.get('increment', 1),
getmode=oracledb.POOL_GETMODE_WAIT
)
else:
# Use individual parameters (host, port, service_name or sid)
service_name = config.get('service_name', os.getenv('ORACLE_SERVICE_NAME'))
sid = config.get('sid', os.getenv('ORACLE_SID'))
pool_params = {
'user': config.get('user', os.getenv('ORACLE_USER')),
'password': config.get('password', os.getenv('ORACLE_PASSWORD')),
'host': config.get('host', os.getenv('ORACLE_HOST', 'localhost')),
'port': config.get('port', int(os.getenv('ORACLE_PORT', '1526'))),
'min': config.get('min_connections', 2),
'max': config.get('max_connections', 10),
'increment': config.get('increment', 1),
'getmode': oracledb.POOL_GETMODE_WAIT
}
if service_name:
pool_params['service_name'] = service_name
logger.info(f"Using SERVICE_NAME: {service_name}")
elif sid:
pool_params['sid'] = sid
logger.info(f"Using SID: {sid}")
else:
pool_params['service_name'] = 'ROA'
logger.info("Using default SERVICE_NAME: ROA")
self._legacy_pool = oracledb.create_pool(**pool_params)
logger.info(f"Legacy Oracle pool created with {self._legacy_pool.opened} connections")
def register_server(
self,
server_id: str,
@@ -185,20 +129,26 @@ class OracleMultiPool:
logger.info(f"Pool created for server '{server_id}' with {pool.opened} connections")
return pool
def _get_first_server_id(self) -> str:
"""Get the first registered server ID."""
if not self._pool_configs:
raise RuntimeError("No servers registered. Call register_server() first.")
return next(iter(self._pool_configs))
@asynccontextmanager
async def get_connection(self, server_id: Optional[str] = None):
"""
Context manager pentru obținerea unei conexiuni din pool.
Args:
server_id: ID-ul serverului. Dacă None, folosește legacy pool sau default.
server_id: ID-ul serverului. Dacă None, folosește primul server înregistrat.
Usage:
# Multi-server mode
# Explicit server
async with oracle_pool.get_connection('romfast') as conn:
...
# Backward compatible (legacy single pool)
# First registered server (when only one server configured)
async with oracle_pool.get_connection() as conn:
...
"""
@@ -207,21 +157,11 @@ class OracleMultiPool:
try:
if server_id is None:
# Backward compatibility: use legacy pool
if self._legacy_pool is None:
# If no legacy pool, try to use 'default' server
if 'default' in self._pool_configs:
pool = await self._get_or_create_pool('default')
else:
raise RuntimeError(
"No pool available. Either initialize() with config "
"or register_server() with server_id='default'."
)
else:
pool = self._legacy_pool
else:
pool = await self._get_or_create_pool(server_id)
# Use first registered server
server_id = self._get_first_server_id()
logger.debug(f"No server_id specified, using first registered: '{server_id}'")
pool = await self._get_or_create_pool(server_id)
connection = pool.acquire()
logger.debug(f"Connection acquired from pool (server_id={server_id})")
yield connection
@@ -238,7 +178,7 @@ class OracleMultiPool:
Args:
query: SQL query string
parameters: Query parameters (dict or tuple)
server_id: Server ID for multi-pool mode (optional)
server_id: Server ID (optional, uses first server if not specified)
"""
async with self.get_connection(server_id) as connection:
logger.debug(f"Executing query on server '{server_id}': {query[:100]}...")
@@ -272,11 +212,6 @@ class OracleMultiPool:
logger.info(f"Closed pool for server '{server_id}'")
else:
# Close all pools (graceful shutdown)
if self._legacy_pool is not None:
self._legacy_pool.close()
self._legacy_pool = None
logger.info("Closed legacy pool")
for srv_id, pool in list(self._pools.items()):
pool.close()
logger.info(f"Closed pool for server '{srv_id}'")
@@ -307,15 +242,6 @@ class OracleMultiPool:
'max': pool.max,
}
else:
# All pools including legacy
if self._legacy_pool:
stats['legacy'] = {
'opened': self._legacy_pool.opened,
'busy': self._legacy_pool.busy,
'min': self._legacy_pool.min,
'max': self._legacy_pool.max,
}
for srv_id, pool in self._pools.items():
stats[srv_id] = {
'opened': pool.opened,
@@ -343,8 +269,8 @@ class OracleMultiPool:
return list(self._pools.keys())
# Backward compatibility: keep old class name as alias
# Backward compatibility alias
OraclePool = OracleMultiPool
# Instance globală pentru folosire în toate aplicațiile
# Global instance
oracle_pool = OracleMultiPool()