#!/usr/bin/env python3 """ Oracle database connection pool manager using oracledb. Manages connections with pooling and health checks. oracledb is the modern, simpler replacement for cx_Oracle. No Oracle Instant Client required - uses Thick or Thin mode. """ import oracledb from logging_config import get_logger from config import get_config logger = get_logger(__name__) class OracleConnector: """Manages Oracle database connections with pooling.""" def __init__(self): """Initialize connection pool.""" self.pool = None self.config = get_config() self._initialize_client_mode() def _initialize_client_mode(self): """ Initialize oracledb client mode. oracledb uses Thin mode by default (no Oracle Instant Client needed). """ try: # oracledb defaults to Thin mode - no initialization needed logger.info("Using oracledb Thin mode (no Oracle Instant Client required)") except Exception as e: logger.warning(f"Oracle client initialization note: {e}") def initialize_pool(self): """Create connection pool.""" try: # Build connection string for oracledb # Format: user/password@host:port/service_name connection_string = ( f"{self.config.db_user}/{self.config.db_password}@" f"{self.config.db_host}:{self.config.db_port}/{self.config.db_service_name}" ) # Create connection pool using oracledb API # Note: oracledb uses 'min' and 'max' for pool sizing self.pool = oracledb.create_pool( dsn=connection_string, min=self.config.db_pool_min, max=self.config.db_pool_max, increment=1, ) logger.info(f"Oracle connection pool initialized: min={self.config.db_pool_min}, max={self.config.db_pool_max}") return True except oracledb.DatabaseError as e: logger.error(f"Failed to initialize connection pool: {e}", exc_info=True) return False except Exception as e: logger.error(f"Unexpected error initializing pool: {e}", exc_info=True) return False def get_connection(self): """Get connection from pool.""" if not self.pool: self.initialize_pool() try: conn = self.pool.acquire() logger.debug("Connection acquired from pool") return conn except oracledb.DatabaseError as e: logger.error(f"Failed to acquire connection: {e}", exc_info=True) raise except Exception as e: logger.error(f"Unexpected error acquiring connection: {e}", exc_info=True) raise def close_pool(self): """Close connection pool.""" if self.pool: try: self.pool.close() logger.info("Connection pool closed") except Exception as e: logger.error(f"Error closing pool: {e}") def test_connection(self): """Test database connectivity.""" try: conn = self.get_connection() cursor = conn.cursor() cursor.execute("SELECT 1 FROM dual") result = cursor.fetchone() cursor.close() conn.close() logger.info("Database connection test successful") return True except Exception as e: logger.error(f"Database connection test failed: {e}") return False def __enter__(self): """Context manager entry.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Context manager exit.""" self.close_pool() # Global connector instance _connector = None def get_connector(): """Get or create global connector instance.""" global _connector if _connector is None: _connector = OracleConnector() return _connector