bookmyservice-mhs / app /nosql.py
MukeshKapoor25's picture
refactor(config): Improve configuration management and code readability
9be6e36
import os
import motor.motor_asyncio
import redis.asyncio as redis
from redis.exceptions import RedisError
from dotenv import load_dotenv
import logging
from datetime import datetime
from app.config.config import settings, validate_configuration
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
# Load environment variables from .env file (fallback)
load_dotenv()
# Validate configuration on import
try:
validate_configuration()
except ValueError as e:
logger.error(f"Configuration validation failed: {e}")
# In production, we want to fail fast if configuration is missing
raise
# MongoDB configuration using settings
MONGO_URI = settings.MONGO_URI
DB_NAME = settings.DB_NAME
# Redis configuration using settings
CACHE_URI = settings.CACHE_URI
CACHE_K = settings.CACHE_K
# Parse Redis host and port safely
try:
if ':' in CACHE_URI:
CACHE_HOST, CACHE_PORT = CACHE_URI.split(":", 1)
CACHE_PORT = int(CACHE_PORT)
else:
CACHE_HOST = CACHE_URI
CACHE_PORT = 6379 # Default Redis port
except ValueError as e:
logger.error(f"Invalid Redis URI format: {CACHE_URI}")
raise ValueError(f"Invalid Redis configuration: {e}")
# Initialize MongoDB client with secure connection
try:
# Ensure SSL is enabled for production
if not MONGO_URI.startswith('mongodb://localhost') and 'ssl=true' not in MONGO_URI:
logger.warning("MongoDB connection may not be using SSL. Consider enabling SSL for production.")
client = motor.motor_asyncio.AsyncIOMotorClient(
MONGO_URI,
serverSelectionTimeoutMS=5000, # 5 second timeout
connectTimeoutMS=10000, # 10 second connection timeout
maxPoolSize=50, # Connection pool size
retryWrites=True # Enable retryable writes
)
db = client[DB_NAME]
logger.info(f"βœ… MongoDB client initialized for database: {DB_NAME}")
except Exception as e:
logger.error(f"❌ Failed to initialize MongoDB client: {e}")
# Don't log the full URI to avoid credential exposure
logger.error("Please check your MongoDB configuration.")
raise
# Initialize Redis client with secure connection
try:
redis_client = redis.Redis(
host=CACHE_HOST,
port=CACHE_PORT,
username="default",
password=CACHE_K,
decode_responses=True,
socket_timeout=5, # 5 second socket timeout
socket_connect_timeout=5, # 5 second connection timeout
retry_on_timeout=True,
health_check_interval=30 # Health check every 30 seconds
)
logger.info("βœ… Redis client initialized")
except Exception as e:
logger.error(f"❌ Failed to initialize Redis client: {e}")
# Don't log credentials
logger.error("Please check your Redis configuration.")
raise
# Connection health check functions
async def check_mongodb_health() -> bool:
"""Check MongoDB connection health"""
try:
await client.admin.command('ping')
return True
except Exception as e:
logger.error(f"MongoDB health check failed: {e}")
return False
async def check_redis_health() -> bool:
"""Check Redis connection health"""
try:
await redis_client.ping()
return True
except Exception as e:
logger.error(f"Redis health check failed: {e}")
return False
async def get_database_status() -> dict:
"""Get database connection status"""
return {
"mongodb": await check_mongodb_health(),
"redis": await check_redis_health(),
"timestamp": datetime.utcnow().isoformat()
}
# Graceful shutdown functions
async def close_database_connections():
"""Close all database connections gracefully"""
try:
if client:
client.close()
logger.info("MongoDB connection closed")
except Exception as e:
logger.error(f"Error closing MongoDB connection: {e}")
try:
if redis_client:
await redis_client.close()
logger.info("Redis connection closed")
except Exception as e:
logger.error(f"Error closing Redis connection: {e}")