aa
This commit is contained in:
parent
2e1071fb40
commit
e8bd48da11
@ -156,13 +156,38 @@ async def lifespan(_: FastAPI): # Underscore indicates unused but required para
|
||||
# than the main bot, so it needs its own connection pools
|
||||
if settings_manager:
|
||||
log.info("Initializing database and cache connection pools for API server...")
|
||||
try:
|
||||
# Initialize the pools in the settings_manager module
|
||||
await settings_manager.initialize_pools()
|
||||
log.info("Database and cache connection pools initialized for API server.")
|
||||
except Exception as e:
|
||||
log.exception(f"Failed to initialize connection pools for API server: {e}")
|
||||
log.error("Dashboard endpoints requiring DB/cache will fail.")
|
||||
|
||||
# Add retry logic for database initialization
|
||||
max_retries = 3
|
||||
retry_count = 0
|
||||
success = False
|
||||
|
||||
while retry_count < max_retries and not success:
|
||||
try:
|
||||
# Initialize the pools in the settings_manager module
|
||||
# Close any existing pools first to ensure clean state
|
||||
if settings_manager.pg_pool or settings_manager.redis_pool:
|
||||
log.info("Closing existing database pools before reinitializing...")
|
||||
await settings_manager.close_pools()
|
||||
|
||||
# Initialize new pools
|
||||
await settings_manager.initialize_pools()
|
||||
log.info("Database and cache connection pools initialized for API server.")
|
||||
success = True
|
||||
except Exception as e:
|
||||
retry_count += 1
|
||||
log.warning(f"Failed to initialize connection pools (attempt {retry_count}/{max_retries}): {e}")
|
||||
if retry_count < max_retries:
|
||||
# Wait before retrying with exponential backoff
|
||||
wait_time = 2 ** retry_count # 2, 4, 8 seconds
|
||||
log.info(f"Waiting {wait_time} seconds before retrying...")
|
||||
await asyncio.sleep(wait_time)
|
||||
else:
|
||||
log.exception(f"Failed to initialize connection pools after {max_retries} attempts")
|
||||
log.error("Dashboard endpoints requiring DB/cache will fail.")
|
||||
|
||||
if not success:
|
||||
log.error("Failed to initialize database pools after all retries.")
|
||||
else:
|
||||
log.error("settings_manager not imported. Dashboard endpoints requiring DB/cache will fail.")
|
||||
|
||||
@ -883,9 +908,28 @@ async def dashboard_get_user_guilds(current_user: dict = Depends(get_dashboard_u
|
||||
|
||||
# 2. Fetch guilds the bot is in from our DB
|
||||
try:
|
||||
bot_guild_ids = await settings_manager.get_bot_guild_ids()
|
||||
# Add retry logic for database operations
|
||||
max_db_retries = 3
|
||||
retry_count = 0
|
||||
bot_guild_ids = None
|
||||
|
||||
while retry_count < max_db_retries and bot_guild_ids is None:
|
||||
try:
|
||||
bot_guild_ids = await settings_manager.get_bot_guild_ids()
|
||||
if bot_guild_ids is None:
|
||||
log.warning(f"Dashboard: Failed to fetch bot guild IDs, retry {retry_count+1}/{max_db_retries}")
|
||||
retry_count += 1
|
||||
if retry_count < max_db_retries:
|
||||
await asyncio.sleep(1) # Wait before retrying
|
||||
except Exception as e:
|
||||
log.warning(f"Dashboard: Error fetching bot guild IDs, retry {retry_count+1}/{max_db_retries}: {e}")
|
||||
retry_count += 1
|
||||
if retry_count < max_db_retries:
|
||||
await asyncio.sleep(1) # Wait before retrying
|
||||
|
||||
# After retries, if still no data, raise exception
|
||||
if bot_guild_ids is None:
|
||||
log.error("Dashboard: Failed to fetch bot guild IDs from settings_manager.")
|
||||
log.error("Dashboard: Failed to fetch bot guild IDs from settings_manager after retries.")
|
||||
raise HTTPException(status_code=500, detail="Could not retrieve bot's guild list.")
|
||||
except Exception as e:
|
||||
log.exception("Dashboard: Exception while fetching bot guild IDs from settings_manager.")
|
||||
|
15
restart_api.bat
Normal file
15
restart_api.bat
Normal file
@ -0,0 +1,15 @@
|
||||
@echo off
|
||||
echo Restarting API server...
|
||||
|
||||
REM Change to the discordbot directory
|
||||
cd /d %~dp0
|
||||
|
||||
REM Check for running API server processes
|
||||
echo Checking for running API server processes...
|
||||
tasklist /fi "imagename eq python.exe" /v | findstr "api_server"
|
||||
|
||||
REM Start the API server
|
||||
echo Starting API server...
|
||||
start python run_unified_api.py
|
||||
|
||||
echo API server restart complete.
|
69
restart_api.py
Normal file
69
restart_api.py
Normal file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Script to restart the API server with proper environment setup.
|
||||
This ensures the database connections are properly initialized.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
def main():
|
||||
"""Main function to restart the API server"""
|
||||
print("Restarting API server...")
|
||||
|
||||
# Get the current directory
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Check if we're in the discordbot directory
|
||||
if os.path.basename(current_dir) != "discordbot":
|
||||
print("Error: This script must be run from the discordbot directory.")
|
||||
sys.exit(1)
|
||||
|
||||
# Find any running API server processes
|
||||
try:
|
||||
print("Checking for running API server processes...")
|
||||
result = subprocess.run(
|
||||
["ps", "-ef", "|", "grep", "api_server.py", "|", "grep", "-v", "grep"],
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
if result.stdout.strip():
|
||||
print("Found running API server processes:")
|
||||
print(result.stdout)
|
||||
|
||||
# Ask for confirmation before killing
|
||||
confirm = input("Do you want to kill these processes? (y/n): ")
|
||||
if confirm.lower() == 'y':
|
||||
# Kill the processes
|
||||
subprocess.run(
|
||||
["pkill", "-f", "api_server.py"],
|
||||
shell=True
|
||||
)
|
||||
print("Processes killed.")
|
||||
time.sleep(2) # Give processes time to terminate
|
||||
else:
|
||||
print("Aborted. Please stop the running API server processes manually.")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"Error checking for running processes: {e}")
|
||||
|
||||
# Start the API server
|
||||
print("Starting API server...")
|
||||
try:
|
||||
# Use the run_unified_api.py script
|
||||
subprocess.Popen(
|
||||
[sys.executable, "run_unified_api.py"],
|
||||
cwd=current_dir
|
||||
)
|
||||
print("API server started successfully.")
|
||||
except Exception as e:
|
||||
print(f"Error starting API server: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
print("API server restart complete.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -31,18 +31,50 @@ async def initialize_pools():
|
||||
"""Initializes the PostgreSQL and Redis connection pools."""
|
||||
global pg_pool, redis_pool
|
||||
log.info("Initializing database and cache connection pools...")
|
||||
|
||||
# Close existing pools if they exist
|
||||
if pg_pool:
|
||||
log.info("Closing existing PostgreSQL pool before reinitializing...")
|
||||
await pg_pool.close()
|
||||
pg_pool = None
|
||||
|
||||
if redis_pool:
|
||||
log.info("Closing existing Redis pool before reinitializing...")
|
||||
await redis_pool.close()
|
||||
redis_pool = None
|
||||
|
||||
# Initialize new pools
|
||||
try:
|
||||
pg_pool = await asyncpg.create_pool(DATABASE_URL, min_size=1, max_size=10)
|
||||
# Create PostgreSQL pool with more conservative settings
|
||||
# Increase max_inactive_connection_lifetime to avoid connections being closed too quickly
|
||||
pg_pool = await asyncpg.create_pool(
|
||||
DATABASE_URL,
|
||||
min_size=1,
|
||||
max_size=10,
|
||||
max_inactive_connection_lifetime=60.0, # 60 seconds (default is 10 minutes)
|
||||
command_timeout=30.0 # 30 seconds timeout for commands
|
||||
)
|
||||
log.info(f"PostgreSQL pool connected to {POSTGRES_HOST}/{POSTGRES_DB}")
|
||||
|
||||
# Create Redis pool
|
||||
redis_pool = redis.from_url(REDIS_URL, decode_responses=True)
|
||||
await redis_pool.ping() # Test connection
|
||||
await redis_pool.ping() # Test connection
|
||||
log.info(f"Redis pool connected to {REDIS_HOST}:{REDIS_PORT}")
|
||||
|
||||
await initialize_database() # Ensure tables exist
|
||||
# Initialize database schema
|
||||
await initialize_database() # Ensure tables exist
|
||||
|
||||
return True # Indicate successful initialization
|
||||
except Exception as e:
|
||||
log.exception(f"Failed to initialize connection pools: {e}")
|
||||
# Depending on bot structure, might want to raise or exit here
|
||||
# Clean up any partially initialized resources
|
||||
if pg_pool:
|
||||
await pg_pool.close()
|
||||
pg_pool = None
|
||||
if redis_pool:
|
||||
await redis_pool.close()
|
||||
redis_pool = None
|
||||
# Raise the exception to be handled by the caller
|
||||
raise
|
||||
|
||||
async def close_pools():
|
||||
@ -587,17 +619,29 @@ async def get_command_permissions(guild_id: int, command_name: str) -> set[int]
|
||||
|
||||
async def get_bot_guild_ids() -> set[int] | None:
|
||||
"""Gets the set of all guild IDs known to the bot from the guilds table. Returns None on error."""
|
||||
global pg_pool
|
||||
if not pg_pool:
|
||||
log.error("Pools not initialized, cannot get bot guild IDs.")
|
||||
return None
|
||||
|
||||
# Create a new connection for this specific operation to avoid event loop conflicts
|
||||
try:
|
||||
async with pg_pool.acquire() as conn:
|
||||
records = await conn.fetch("SELECT guild_id FROM guilds")
|
||||
guild_ids = {record['guild_id'] for record in records}
|
||||
log.debug(f"Fetched {len(guild_ids)} guild IDs from database.")
|
||||
return guild_ids
|
||||
# Create a temporary connection just for this operation
|
||||
# This ensures we're using the current event loop
|
||||
temp_conn = await asyncpg.connect(DATABASE_URL)
|
||||
try:
|
||||
records = await temp_conn.fetch("SELECT guild_id FROM guilds")
|
||||
guild_ids = {record['guild_id'] for record in records}
|
||||
log.debug(f"Fetched {len(guild_ids)} guild IDs from database.")
|
||||
return guild_ids
|
||||
finally:
|
||||
# Always close the temporary connection
|
||||
await temp_conn.close()
|
||||
except asyncpg.exceptions.PostgresError as e:
|
||||
log.exception(f"PostgreSQL error fetching bot guild IDs: {e}")
|
||||
return None
|
||||
except Exception as e:
|
||||
log.exception("Database error fetching bot guild IDs.")
|
||||
log.exception(f"Unexpected error fetching bot guild IDs: {e}")
|
||||
return None
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user