fix: implement proper database connection context manager

- Created DBConnection class to manage database connections
- Each connection is now properly created and closed
- Removed global connection pool to fix thread issues
This commit is contained in:
Josh Finlay 2025-01-08 08:48:38 +10:00
parent a0533e1d78
commit a3ecf3a606
1 changed files with 24 additions and 14 deletions

View File

@ -105,17 +105,27 @@ ha_mqtt = HomeAssistantMQTT()
DB_PATH = "gatekeeper.db" # Database file path DB_PATH = "gatekeeper.db" # Database file path
# Database connection handling # Database connection handling
async def get_db(): class DBConnection:
def __init__(self):
self.db = None
async def __aenter__(self):
self.db = await aiosqlite.connect(DB_PATH)
self.db.row_factory = aiosqlite.Row
return self.db
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.db.close()
def get_db():
"""Get a new database connection""" """Get a new database connection"""
db = await aiosqlite.connect(DB_PATH) return DBConnection()
db.row_factory = aiosqlite.Row
return db
# Set up MQTT event logging # Set up MQTT event logging
async def log_mqtt_event(action: str, success: bool = True): async def log_mqtt_event(action: str, success: bool = True):
"""Log MQTT events to the database and log file""" """Log MQTT events to the database and log file"""
logger.info(f"MQTT Event - {action} (Success: {success})") logger.info(f"MQTT Event - {action} (Success: {success})")
async with await get_db() as db: async with get_db() as db:
await db.execute( await db.execute(
"INSERT INTO events (timestamp, action, source, success) VALUES (?, ?, ?, ?)", "INSERT INTO events (timestamp, action, source, success) VALUES (?, ?, ?, ?)",
(datetime.utcnow().isoformat(), action, "MQTT", success) (datetime.utcnow().isoformat(), action, "MQTT", success)
@ -188,7 +198,7 @@ async def update_gate_status():
logger.info(f"Gate status changed to: {'open' if current_status else 'closed'}") logger.info(f"Gate status changed to: {'open' if current_status else 'closed'}")
logger.debug("Updating database with new status") logger.debug("Updating database with new status")
async with await get_db() as db: async with get_db() as db:
await db.execute( await db.execute(
"INSERT INTO gate_status (timestamp) VALUES (?)", "INSERT INTO gate_status (timestamp) VALUES (?)",
(timestamp.isoformat(),) (timestamp.isoformat(),)
@ -258,7 +268,7 @@ async def check_auto_close():
logger.warning(f"Gate has been open for {time_open:.1f} seconds, auto-closing") logger.warning(f"Gate has been open for {time_open:.1f} seconds, auto-closing")
timestamp = current_time.isoformat() timestamp = current_time.isoformat()
async with await get_db() as db: async with get_db() as db:
await db.execute( await db.execute(
"INSERT INTO events (timestamp, action, source, success) VALUES (?, ?, ?, ?)", "INSERT INTO events (timestamp, action, source, success) VALUES (?, ?, ?, ?)",
(timestamp, "auto-close", "system", True) (timestamp, "auto-close", "system", True)
@ -314,7 +324,7 @@ async def trigger():
current_status = GPIO.input(settings.gpio.statusPin) == GPIO.HIGH current_status = GPIO.input(settings.gpio.statusPin) == GPIO.HIGH
# Log event # Log event
async with await get_db() as db: async with get_db() as db:
await db.execute( await db.execute(
"INSERT INTO events (timestamp, action, source, success) VALUES (?, ?, ?, ?)", "INSERT INTO events (timestamp, action, source, success) VALUES (?, ?, ?, ?)",
(timestamp, "trigger gate", "api", success) (timestamp, "trigger gate", "api", success)
@ -333,7 +343,7 @@ async def get_status():
settings = app.state.current_settings or Settings() settings = app.state.current_settings or Settings()
is_open = GPIO.input(settings.gpio.statusPin) == GPIO.HIGH is_open = GPIO.input(settings.gpio.statusPin) == GPIO.HIGH
async with await get_db() as db: async with get_db() as db:
cursor = await db.execute( cursor = await db.execute(
"SELECT timestamp FROM gate_status ORDER BY timestamp DESC LIMIT 1" "SELECT timestamp FROM gate_status ORDER BY timestamp DESC LIMIT 1"
) )
@ -348,7 +358,7 @@ async def get_status():
@app.get("/api/events") @app.get("/api/events")
async def get_events(limit: int = 10, offset: int = 0): async def get_events(limit: int = 10, offset: int = 0):
"""Get recent gate events with pagination""" """Get recent gate events with pagination"""
async with await get_db() as db: async with get_db() as db:
db.row_factory = aiosqlite.Row db.row_factory = aiosqlite.Row
# Get total count # Get total count
@ -375,7 +385,7 @@ async def get_events(limit: int = 10, offset: int = 0):
@app.get("/api/settings") @app.get("/api/settings")
async def get_settings(): async def get_settings():
"""Get current settings""" """Get current settings"""
async with await get_db() as db: async with get_db() as db:
cursor = await db.execute("SELECT key, value FROM settings") cursor = await db.execute("SELECT key, value FROM settings")
rows = await cursor.fetchall() rows = await cursor.fetchall()
settings = {} settings = {}
@ -408,7 +418,7 @@ async def get_settings():
async def update_settings(settings: Settings): async def update_settings(settings: Settings):
"""Update settings""" """Update settings"""
try: try:
async with await get_db() as db: async with get_db() as db:
# Update each setting # Update each setting
for key, value in settings.dict().items(): for key, value in settings.dict().items():
await db.execute( await db.execute(
@ -458,7 +468,7 @@ async def update_settings(settings: Settings):
return {"success": True} return {"success": True}
except Exception as e: except Exception as e:
# Log failure event # Log failure event
async with await get_db() as db: async with get_db() as db:
await db.execute( await db.execute(
"INSERT INTO events (timestamp, action, source, success) VALUES (?, ?, ?, ?)", "INSERT INTO events (timestamp, action, source, success) VALUES (?, ?, ?, ?)",
( (
@ -503,7 +513,7 @@ def setup_gpio():
# Database functions # Database functions
async def init_db(): async def init_db():
"""Initialize the SQLite database""" """Initialize the SQLite database"""
async with await get_db() as db: async with get_db() as db:
# Create events table # Create events table
await db.execute(""" await db.execute("""
CREATE TABLE IF NOT EXISTS events ( CREATE TABLE IF NOT EXISTS events (