From ac805e96da4297fb871d6849f52c3bdd463d68e3 Mon Sep 17 00:00:00 2001 From: David Rodenkirchen Date: Thu, 29 Aug 2024 11:21:11 +0200 Subject: [PATCH] improve database error handling, implement automatic reconnects, raise specialized errors --- .../services/DatabaseService.py | 121 +++++++++++++++--- 1 file changed, 105 insertions(+), 16 deletions(-) diff --git a/src/ez_lan_manager/services/DatabaseService.py b/src/ez_lan_manager/services/DatabaseService.py index c5cf563..1615436 100644 --- a/src/ez_lan_manager/services/DatabaseService.py +++ b/src/ez_lan_manager/services/DatabaseService.py @@ -1,5 +1,6 @@ import logging -import sys +from time import sleep + from datetime import date, datetime from typing import Optional @@ -25,23 +26,33 @@ class NoDatabaseConnectionError(Exception): pass class DatabaseService: + MAX_CONNECTION_RETRIES = 5 def __init__(self, database_config: DatabaseConfiguration) -> None: self._database_config = database_config - try: - logger.info( - f"Connecting to database '{self._database_config.db_name}' on " - f"{self._database_config.db_user}@{self._database_config.db_host}:{self._database_config.db_port}" - ) - self._connection = mariadb.connect( - user=self._database_config.db_user, - password=self._database_config.db_password, - host=self._database_config.db_host, - port=self._database_config.db_port, - database=self._database_config.db_name - ) - except mariadb.Error as e: - logger.error(f"Error connecting to database: {e}") - raise NoDatabaseConnectionError + logger.info( + f"Connecting to database '{self._database_config.db_name}' on " + f"{self._database_config.db_user}@{self._database_config.db_host}:{self._database_config.db_port}" + ) + self._establish_new_connection() + + def _establish_new_connection(self) -> None: + retries = 0 + for _ in range(DatabaseService.MAX_CONNECTION_RETRIES): + try: + self._connection = mariadb.connect( + user=self._database_config.db_user, + password=self._database_config.db_password, + host=self._database_config.db_host, + port=self._database_config.db_port, + database=self._database_config.db_name + ) + except mariadb.Error: + sleep(0.5) + retries += 1 + continue + return + raise NoDatabaseConnectionError + def _get_cursor(self) -> Cursor: return self._connection.cursor() @@ -98,6 +109,9 @@ class DatabaseService: "VALUES (?, ?, ?)", (user_name, user_mail.lower(), password_hash) ) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.create_user(user_name, user_mail, password_hash) except mariadb.IntegrityError as e: logger.warning(f"Aborted duplication entry: {e}") raise DuplicationError @@ -115,6 +129,9 @@ class DatabaseService: user.user_id) ) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.update_user(user) except mariadb.IntegrityError as e: logger.warning(f"Aborted duplication entry: {e}") raise DuplicationError @@ -129,6 +146,9 @@ class DatabaseService: (transaction.user_id, transaction.value, transaction.is_debit, transaction.transaction_date, transaction.reference) ) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.add_transaction(transaction) except Exception as e: logger.warning(f"Error adding Transaction: {e}") return @@ -143,6 +163,9 @@ class DatabaseService: cursor.execute("SELECT * FROM transactions WHERE user_id=?", (user_id,)) self._connection.commit() result = cursor.fetchall() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.get_all_transactions_for_user(user_id) except mariadb.Error as e: logger.error(f"Error getting all transactions for user: {e}") return [] @@ -166,6 +189,9 @@ class DatabaseService: (news.content, news.title, news.subtitle, news.author.user_id, news.news_date) ) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.add_news(news) except Exception as e: logger.warning(f"Error adding Transaction: {e}") @@ -175,6 +201,9 @@ class DatabaseService: try: cursor.execute("SELECT * FROM news INNER JOIN users ON news.news_author = users.user_id WHERE news_date BETWEEN ? AND ?;", (dt_start, dt_end)) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.get_news(dt_start, dt_end) except Exception as e: logger.warning(f"Error fetching news: {e}") return [] @@ -198,6 +227,9 @@ class DatabaseService: try: cursor.execute("SELECT * FROM tickets INNER JOIN users ON tickets.user = users.user_id;", ()) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.get_tickets() except Exception as e: logger.warning(f"Error fetching tickets: {e}") return [] @@ -218,6 +250,9 @@ class DatabaseService: try: cursor.execute("SELECT * FROM tickets INNER JOIN users ON tickets.user = users.user_id WHERE user_id=?;", (user_id, )) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.get_ticket_for_user(user_id) except Exception as e: logger.warning(f"Error fetching ticket for user: {e}") return @@ -239,6 +274,9 @@ class DatabaseService: try: cursor.execute("INSERT INTO tickets (ticket_category, user) VALUES (?, ?)", (category, user_id)) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.generate_ticket_for_user(user_id, category) except Exception as e: logger.warning(f"Error generating ticket for user: {e}") return @@ -251,6 +289,9 @@ class DatabaseService: cursor.execute("UPDATE tickets SET user = ? WHERE ticket_id = ?;", (new_owner_id, ticket_id)) affected_rows = cursor.rowcount self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.change_ticket_owner(ticket_id, new_owner_id) except Exception as e: logger.warning(f"Error transferring ticket to user: {e}") return False @@ -261,6 +302,9 @@ class DatabaseService: try: cursor.execute("DELETE FROM tickets WHERE ticket_id = ?;", (ticket_id, )) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.change_ticket_owner(ticket_id) except Exception as e: logger.warning(f"Error deleting ticket: {e}") return False @@ -274,6 +318,9 @@ class DatabaseService: for seat in seats: cursor.execute("INSERT INTO seats (seat_id, seat_category) VALUES (?, ?);", (seat[0], seat[1])) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.generate_fresh_seats_table(seats) except Exception as e: logger.warning(f"Error generating fresh seats table: {e}") return @@ -284,6 +331,9 @@ class DatabaseService: try: cursor.execute("SELECT seats.*, users.* FROM seats LEFT JOIN users ON seats.user = users.user_id;") self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.get_seating_info() except Exception as e: logger.warning(f"Error getting seats table: {e}") return results @@ -304,6 +354,9 @@ class DatabaseService: cursor.execute("UPDATE seats SET user = ? WHERE seat_id = ?;", (user_id, seat_id)) affected_rows = cursor.rowcount self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.seat_user(seat_id, user_id) except Exception as e: logger.warning(f"Error seating user: {e}") return False @@ -315,6 +368,9 @@ class DatabaseService: try: cursor.execute("SELECT * FROM catering_menu_items;") self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.get_menu_items() except Exception as e: logger.warning(f"Error fetching menu items: {e}") return results @@ -336,6 +392,9 @@ class DatabaseService: try: cursor.execute("SELECT * FROM catering_menu_items WHERE catering_menu_item_id = ?;", (menu_item_id, )) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.get_menu_item(menu_item_id) except Exception as e: logger.warning(f"Error fetching menu items: {e}") return @@ -360,6 +419,9 @@ class DatabaseService: (name, info, price, category.value, is_disabled) ) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.add_menu_item(name, info, price, category, is_disabled) except Exception as e: logger.warning(f"Error adding menu item: {e}") return @@ -378,6 +440,9 @@ class DatabaseService: try: cursor.execute("DELETE FROM catering_menu_items WHERE catering_menu_item_id = ?;", (menu_item_id,)) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.delete_menu_item(menu_item_id) except Exception as e: logger.warning(f"Error deleting menu item: {e}") return False @@ -392,6 +457,9 @@ class DatabaseService: ) affected_rows = cursor.rowcount self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.update_menu_item(updated_item) except Exception as e: logger.warning(f"Error updating menu item: {e}") return False @@ -420,6 +488,9 @@ class DatabaseService: customer=self.get_user_by_id(user_id), is_delivery=is_delivery ) + except mariadb.InterfaceError: + self._establish_new_connection() + return self.add_new_order(menu_items, user_id, is_delivery) except Exception as e: logger.warning(f"Error placing order: {e}") return @@ -433,6 +504,9 @@ class DatabaseService: ) affected_rows = cursor.rowcount self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.change_order_status(order_id, status) except Exception as e: logger.warning(f"Error updating menu item: {e}") return False @@ -453,6 +527,9 @@ class DatabaseService: try: cursor.execute(query) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.get_orders(user_id, status) except Exception as e: logger.warning(f"Error getting orders: {e}") return fetched_orders @@ -482,6 +559,9 @@ class DatabaseService: (order_id, ) ) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.get_menu_items_for_order(order_id) except Exception as e: logger.warning(f"Error getting order items: {e}") return result @@ -506,6 +586,9 @@ class DatabaseService: (user_id, picture_data) ) self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.set_user_profile_picture(user_id, picture_data) except Exception as e: logger.warning(f"Error setting user profile picture: {e}") @@ -518,6 +601,9 @@ class DatabaseService: if r is None: return return r[0] + except mariadb.InterfaceError: + self._establish_new_connection() + return self.get_user_profile_picture(user_id) except Exception as e: logger.warning(f"Error setting user profile picture: {e}") return None @@ -528,6 +614,9 @@ class DatabaseService: try: cursor.execute("SELECT * FROM users;") self._connection.commit() + except mariadb.InterfaceError: + self._establish_new_connection() + return self.get_all_users() except Exception as e: logger.warning(f"Error getting all users: {e}") return results