demand response
This commit is contained in:
208
microservices/demand-response-service/database.py
Normal file
208
microservices/demand-response-service/database.py
Normal file
@@ -0,0 +1,208 @@
|
||||
"""
|
||||
Database configuration and connection management for Demand Response Service
|
||||
"""
|
||||
|
||||
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
|
||||
import redis.asyncio as redis
|
||||
import logging
|
||||
import os
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Configuration from environment variables
|
||||
MONGO_URL = os.getenv("MONGO_URL", "mongodb://localhost:27017")
|
||||
DATABASE_NAME = os.getenv("DATABASE_NAME", "energy_dashboard_demand_response")
|
||||
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")
|
||||
|
||||
# Global database clients
|
||||
_mongo_client: AsyncIOMotorClient = None
|
||||
_database: AsyncIOMotorDatabase = None
|
||||
_redis_client: redis.Redis = None
|
||||
|
||||
|
||||
async def connect_to_mongo():
|
||||
"""Initialize MongoDB connection and create indexes"""
|
||||
global _mongo_client, _database
|
||||
|
||||
try:
|
||||
logger.info(f"Connecting to MongoDB at {MONGO_URL}")
|
||||
_mongo_client = AsyncIOMotorClient(MONGO_URL)
|
||||
_database = _mongo_client[DATABASE_NAME]
|
||||
|
||||
# Test connection
|
||||
await _database.command("ping")
|
||||
logger.info(f"Successfully connected to MongoDB database: {DATABASE_NAME}")
|
||||
|
||||
# Create indexes
|
||||
await create_indexes()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to connect to MongoDB: {e}")
|
||||
raise
|
||||
|
||||
|
||||
async def close_mongo_connection():
|
||||
"""Close MongoDB connection"""
|
||||
global _mongo_client
|
||||
|
||||
if _mongo_client:
|
||||
_mongo_client.close()
|
||||
logger.info("MongoDB connection closed")
|
||||
|
||||
|
||||
async def get_database() -> AsyncIOMotorDatabase:
|
||||
"""Get database instance"""
|
||||
if _database is None:
|
||||
await connect_to_mongo()
|
||||
return _database
|
||||
|
||||
|
||||
async def connect_to_redis():
|
||||
"""Initialize Redis connection"""
|
||||
global _redis_client
|
||||
|
||||
try:
|
||||
logger.info(f"Connecting to Redis at {REDIS_URL}")
|
||||
_redis_client = redis.from_url(REDIS_URL, decode_responses=True)
|
||||
|
||||
# Test connection
|
||||
await _redis_client.ping()
|
||||
logger.info("Successfully connected to Redis")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to connect to Redis: {e}")
|
||||
raise
|
||||
|
||||
|
||||
async def close_redis_connection():
|
||||
"""Close Redis connection"""
|
||||
global _redis_client
|
||||
|
||||
if _redis_client:
|
||||
await _redis_client.close()
|
||||
logger.info("Redis connection closed")
|
||||
|
||||
|
||||
async def get_redis() -> redis.Redis:
|
||||
"""Get Redis client instance"""
|
||||
if _redis_client is None:
|
||||
await connect_to_redis()
|
||||
return _redis_client
|
||||
|
||||
|
||||
async def create_indexes():
|
||||
"""Create MongoDB indexes for optimal query performance"""
|
||||
db = await get_database()
|
||||
|
||||
logger.info("Creating MongoDB indexes...")
|
||||
|
||||
try:
|
||||
# Indexes for demand_response_invitations collection
|
||||
await db.demand_response_invitations.create_index("event_id", unique=True)
|
||||
await db.demand_response_invitations.create_index([("event_time", 1), ("status", 1)])
|
||||
await db.demand_response_invitations.create_index("status")
|
||||
await db.demand_response_invitations.create_index("created_at")
|
||||
await db.demand_response_invitations.create_index("response")
|
||||
logger.info("Created indexes for demand_response_invitations collection")
|
||||
|
||||
# Indexes for demand_response_events collection
|
||||
await db.demand_response_events.create_index("event_id", unique=True)
|
||||
await db.demand_response_events.create_index([("start_time", 1), ("status", 1)])
|
||||
await db.demand_response_events.create_index([("status", 1), ("start_time", 1)])
|
||||
await db.demand_response_events.create_index("status")
|
||||
await db.demand_response_events.create_index("invitation_id")
|
||||
logger.info("Created indexes for demand_response_events collection")
|
||||
|
||||
# Indexes for demand_response_responses collection
|
||||
await db.demand_response_responses.create_index([("event_id", 1), ("device_id", 1)], unique=True)
|
||||
await db.demand_response_responses.create_index("event_id")
|
||||
await db.demand_response_responses.create_index("device_id")
|
||||
await db.demand_response_responses.create_index("responded_at")
|
||||
logger.info("Created indexes for demand_response_responses collection")
|
||||
|
||||
# Indexes for flexibility_snapshots collection (with TTL for auto-cleanup)
|
||||
await db.flexibility_snapshots.create_index([("timestamp", -1)])
|
||||
await db.flexibility_snapshots.create_index(
|
||||
"timestamp",
|
||||
expireAfterSeconds=7776000 # 90 days TTL
|
||||
)
|
||||
logger.info("Created indexes for flexibility_snapshots collection")
|
||||
|
||||
# Indexes for auto_response_config collection (singleton document)
|
||||
await db.auto_response_config.create_index("config_id", unique=True)
|
||||
logger.info("Created indexes for auto_response_config collection")
|
||||
|
||||
# Indexes for device_instructions collection
|
||||
await db.device_instructions.create_index("device_id", unique=True)
|
||||
await db.device_instructions.create_index("updated_at")
|
||||
logger.info("Created indexes for device_instructions collection")
|
||||
|
||||
logger.info("All MongoDB indexes created successfully")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating indexes: {e}")
|
||||
# Don't raise - indexes may already exist
|
||||
|
||||
|
||||
async def initialize_default_config():
|
||||
"""Initialize default auto-response configuration if it doesn't exist"""
|
||||
db = await get_database()
|
||||
|
||||
try:
|
||||
# Check if default config exists
|
||||
existing_config = await db.auto_response_config.find_one({"config_id": "default"})
|
||||
|
||||
if not existing_config:
|
||||
default_config = {
|
||||
"config_id": "default",
|
||||
"enabled": False,
|
||||
"max_reduction_percentage": 20.0,
|
||||
"response_delay_seconds": 300,
|
||||
"min_notice_minutes": 60,
|
||||
"created_at": None,
|
||||
"updated_at": None
|
||||
}
|
||||
|
||||
await db.auto_response_config.insert_one(default_config)
|
||||
logger.info("Created default auto-response configuration")
|
||||
else:
|
||||
logger.info("Auto-response configuration already exists")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error initializing default config: {e}")
|
||||
|
||||
|
||||
# Utility functions for common database operations
|
||||
|
||||
async def get_collection(collection_name: str):
|
||||
"""Get a collection by name"""
|
||||
db = await get_database()
|
||||
return db[collection_name]
|
||||
|
||||
|
||||
async def health_check() -> dict:
|
||||
"""Check database connections health"""
|
||||
status = {
|
||||
"mongodb": False,
|
||||
"redis": False
|
||||
}
|
||||
|
||||
try:
|
||||
# Check MongoDB
|
||||
db = await get_database()
|
||||
await db.command("ping")
|
||||
status["mongodb"] = True
|
||||
except Exception as e:
|
||||
logger.error(f"MongoDB health check failed: {e}")
|
||||
|
||||
try:
|
||||
# Check Redis
|
||||
redis_client = await get_redis()
|
||||
await redis_client.ping()
|
||||
status["redis"] = True
|
||||
except Exception as e:
|
||||
logger.error(f"Redis health check failed: {e}")
|
||||
|
||||
return status
|
||||
Reference in New Issue
Block a user