further improve db error handling

This commit is contained in:
David Rodenkirchen 2024-08-29 12:05:04 +02:00
parent ac805e96da
commit dc514895df
4 changed files with 84 additions and 68 deletions

View File

@ -6,12 +6,12 @@ from pathlib import Path
from rio import App, Theme, Color, Font, Page, Session from rio import App, Theme, Color, Font, Page, Session
from from_root import from_root from from_root import from_root
from src.ez_lan_manager import pages, init_services, ConfigurationService from src.ez_lan_manager import pages, init_services
from src.ez_lan_manager.helpers.LoggedInGuard import logged_in_guard, not_logged_in_guard from src.ez_lan_manager.helpers.LoggedInGuard import logged_in_guard, not_logged_in_guard
from src.ez_lan_manager.services.DatabaseService import NoDatabaseConnectionError from src.ez_lan_manager.services.DatabaseService import NoDatabaseConnectionError
from src.ez_lan_manager.types.SessionStorage import SessionStorage from src.ez_lan_manager.types.SessionStorage import SessionStorage
logger = logging.getLogger(__name__.split(".")[-1]) logger = logging.getLogger("EzLanManager")
if __name__ == "__main__": if __name__ == "__main__":
theme = Theme.from_colors( theme = Theme.from_colors(
@ -31,38 +31,8 @@ if __name__ == "__main__":
try: try:
services = init_services() services = init_services()
except NoDatabaseConnectionError: except NoDatabaseConnectionError:
configuration_service = ConfigurationService(from_root("config.toml")) logger.fatal("Could not connect to database, exiting...")
lan_info = configuration_service.get_lan_info() sys.exit(1)
app = App(
name="EZ LAN Manager",
pages=[
Page(
name="DbErrorPage",
page_url="",
build=pages.DbErrorPage,
)
],
theme=theme,
default_attachments=[configuration_service],
assets_dir=Path(__file__).parent / "assets",
icon=from_root("src/ez_lan_manager/assets/img/favicon.png"),
meta_tags={
"robots": "INDEX,FOLLOW",
"description": f"Info und Verwaltungs-Seite der LAN Party '{lan_info.name} - {lan_info.iteration}'.",
"og:description": f"Info und Verwaltungs-Seite der LAN Party '{lan_info.name} - {lan_info.iteration}'.",
"keywords": "Gaming, Clan, Guild, Verein, Club, Einfach, Zocken, Genuss, Gesellschaft, Videospiele, "
"Videogames, LAN, Party, EZ, LAN, Manager",
"author": "David Rodenkirchen",
"publisher": "EZ GG e.V.",
"copyright": "EZ GG e.V.",
"audience": "Alle",
"page-type": "Management Application",
"page-topic": "LAN Party",
"expires": "",
"revisit-after": "2 days"
}
)
sys.exit(app.run_as_web_server())
lan_info = services[2].get_lan_info() lan_info = services[2].get_lan_info()
@ -157,6 +127,11 @@ if __name__ == "__main__":
page_url="account", page_url="account",
build=pages.AccountPage, build=pages.AccountPage,
guard=logged_in_guard guard=logged_in_guard
),
Page(
name="DbErrorPage",
page_url="db-error",
build=pages.DbErrorPage,
) )
], ],
theme=theme, theme=theme,

View File

@ -1,23 +1,33 @@
from __future__ import annotations from __future__ import annotations
from asyncio import sleep
from typing import * # type: ignore from typing import * # type: ignore
from rio import Component, event, Spacer, Card, Container, Column, Row, TextStyle, Color, Text from rio import Component, event, Spacer, Card, Container, Column, Row, TextStyle, Color, Text
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager import ConfigurationService from src.ez_lan_manager import ConfigurationService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
class DbErrorPage(Component): class DbErrorPage(Component):
@event.on_window_size_change @event.on_window_size_change
async def on_window_size_change(self): async def on_window_size_change(self) -> None:
await self.force_refresh() await self.force_refresh()
@event.on_mount
async def retry_db_connect(self) -> None:
while not self.session[DatabaseService].is_connected:
await sleep(2)
self.session.navigate_to("./")
def build(self) -> Component: def build(self) -> Component:
content = Card( content = Card(
content=MainViewContentBox( content=MainViewContentBox(
content=Text( content=Text(
text="Ouh-oh, da läuft gerade irgendwas schief mit unserer Datenbank.\n\nUnser Team kümmert sich bereits um das Problem.", text="Ouh-oh, da läuft gerade irgendwas schief.\n\n"
"Unser Team kümmert sich bereits um das Problem.\n\n"
"Du wirst automatisch weitergeleitet sobald das System wieder verfügbar ist.",
margin=2, margin=2,
style=TextStyle( style=TextStyle(
fill=self.session.theme.danger_color, fill=self.session.theme.danger_color,

View File

@ -3,11 +3,20 @@ from rio import Column, Component, event
from src.ez_lan_manager import ConfigurationService, NewsService from src.ez_lan_manager import ConfigurationService, NewsService
from src.ez_lan_manager.components.NewsPost import NewsPost from src.ez_lan_manager.components.NewsPost import NewsPost
from src.ez_lan_manager.pages import BasePage from src.ez_lan_manager.pages import BasePage
from src.ez_lan_manager.services.DatabaseService import NoDatabaseConnectionError
from src.ez_lan_manager.types.News import News
class NewsPage(Component): class NewsPage(Component):
news_posts: list[News] = []
@event.on_populate @event.on_populate
async def on_populate(self) -> None: async def on_populate(self) -> None:
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Neuigkeiten") await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Neuigkeiten")
try:
self.news_posts = self.session[NewsService].get_news()[:8]
except NoDatabaseConnectionError:
self.session.navigate_to("db-error")
def build(self) -> Component: def build(self) -> Component:
posts = [NewsPost( posts = [NewsPost(
@ -16,7 +25,7 @@ class NewsPage(Component):
text=news.content, text=news.content,
date=news.news_date.strftime("%d.%m.%Y"), date=news.news_date.strftime("%d.%m.%Y"),
author=news.author.user_name author=news.author.user_name
) for news in self.session[NewsService].get_news()[:8]] ) for news in self.news_posts]
return BasePage( return BasePage(
content=Column( content=Column(
*posts, *posts,

View File

@ -33,10 +33,31 @@ class DatabaseService:
f"Connecting to database '{self._database_config.db_name}' on " 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}" f"{self._database_config.db_user}@{self._database_config.db_host}:{self._database_config.db_port}"
) )
self._establish_new_connection() self._connection: Optional[mariadb.Connection] = None
self._reestablishment_lock = False
self.establish_new_connection()
@property
def is_connected(self) -> bool:
try:
self._connection.ping()
except Exception:
try:
self.establish_new_connection()
return True
except NoDatabaseConnectionError:
return False
return True
def establish_new_connection(self) -> None:
if self._reestablishment_lock:
return
self._reestablishment_lock = True
if isinstance(self._connection, mariadb.Connection):
self._connection.close()
self._connection = None
def _establish_new_connection(self) -> None:
retries = 0
for _ in range(DatabaseService.MAX_CONNECTION_RETRIES): for _ in range(DatabaseService.MAX_CONNECTION_RETRIES):
try: try:
self._connection = mariadb.connect( self._connection = mariadb.connect(
@ -47,10 +68,11 @@ class DatabaseService:
database=self._database_config.db_name database=self._database_config.db_name
) )
except mariadb.Error: except mariadb.Error:
sleep(0.5) sleep(0.4)
retries += 1
continue continue
self._reestablishment_lock = False
return return
self._reestablishment_lock = False
raise NoDatabaseConnectionError raise NoDatabaseConnectionError
@ -110,7 +132,7 @@ class DatabaseService:
) )
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.create_user(user_name, user_mail, password_hash) return self.create_user(user_name, user_mail, password_hash)
except mariadb.IntegrityError as e: except mariadb.IntegrityError as e:
logger.warning(f"Aborted duplication entry: {e}") logger.warning(f"Aborted duplication entry: {e}")
@ -130,7 +152,7 @@ class DatabaseService:
) )
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.update_user(user) return self.update_user(user)
except mariadb.IntegrityError as e: except mariadb.IntegrityError as e:
logger.warning(f"Aborted duplication entry: {e}") logger.warning(f"Aborted duplication entry: {e}")
@ -147,7 +169,7 @@ class DatabaseService:
) )
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.add_transaction(transaction) return self.add_transaction(transaction)
except Exception as e: except Exception as e:
logger.warning(f"Error adding Transaction: {e}") logger.warning(f"Error adding Transaction: {e}")
@ -164,7 +186,7 @@ class DatabaseService:
self._connection.commit() self._connection.commit()
result = cursor.fetchall() result = cursor.fetchall()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.get_all_transactions_for_user(user_id) return self.get_all_transactions_for_user(user_id)
except mariadb.Error as e: except mariadb.Error as e:
logger.error(f"Error getting all transactions for user: {e}") logger.error(f"Error getting all transactions for user: {e}")
@ -190,7 +212,7 @@ class DatabaseService:
) )
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.add_news(news) return self.add_news(news)
except Exception as e: except Exception as e:
logger.warning(f"Error adding Transaction: {e}") logger.warning(f"Error adding Transaction: {e}")
@ -202,7 +224,7 @@ class DatabaseService:
cursor.execute("SELECT * FROM news INNER JOIN users ON news.news_author = users.user_id WHERE news_date BETWEEN ? AND ?;", (dt_start, dt_end)) 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() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.get_news(dt_start, dt_end) return self.get_news(dt_start, dt_end)
except Exception as e: except Exception as e:
logger.warning(f"Error fetching news: {e}") logger.warning(f"Error fetching news: {e}")
@ -228,7 +250,7 @@ class DatabaseService:
cursor.execute("SELECT * FROM tickets INNER JOIN users ON tickets.user = users.user_id;", ()) cursor.execute("SELECT * FROM tickets INNER JOIN users ON tickets.user = users.user_id;", ())
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.get_tickets() return self.get_tickets()
except Exception as e: except Exception as e:
logger.warning(f"Error fetching tickets: {e}") logger.warning(f"Error fetching tickets: {e}")
@ -251,7 +273,7 @@ class DatabaseService:
cursor.execute("SELECT * FROM tickets INNER JOIN users ON tickets.user = users.user_id WHERE user_id=?;", (user_id, )) cursor.execute("SELECT * FROM tickets INNER JOIN users ON tickets.user = users.user_id WHERE user_id=?;", (user_id, ))
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.get_ticket_for_user(user_id) return self.get_ticket_for_user(user_id)
except Exception as e: except Exception as e:
logger.warning(f"Error fetching ticket for user: {e}") logger.warning(f"Error fetching ticket for user: {e}")
@ -275,7 +297,7 @@ class DatabaseService:
cursor.execute("INSERT INTO tickets (ticket_category, user) VALUES (?, ?)", (category, user_id)) cursor.execute("INSERT INTO tickets (ticket_category, user) VALUES (?, ?)", (category, user_id))
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.generate_ticket_for_user(user_id, category) return self.generate_ticket_for_user(user_id, category)
except Exception as e: except Exception as e:
logger.warning(f"Error generating ticket for user: {e}") logger.warning(f"Error generating ticket for user: {e}")
@ -290,7 +312,7 @@ class DatabaseService:
affected_rows = cursor.rowcount affected_rows = cursor.rowcount
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.change_ticket_owner(ticket_id, new_owner_id) return self.change_ticket_owner(ticket_id, new_owner_id)
except Exception as e: except Exception as e:
logger.warning(f"Error transferring ticket to user: {e}") logger.warning(f"Error transferring ticket to user: {e}")
@ -303,7 +325,7 @@ class DatabaseService:
cursor.execute("DELETE FROM tickets WHERE ticket_id = ?;", (ticket_id, )) cursor.execute("DELETE FROM tickets WHERE ticket_id = ?;", (ticket_id, ))
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.change_ticket_owner(ticket_id) return self.change_ticket_owner(ticket_id)
except Exception as e: except Exception as e:
logger.warning(f"Error deleting ticket: {e}") logger.warning(f"Error deleting ticket: {e}")
@ -319,7 +341,7 @@ class DatabaseService:
cursor.execute("INSERT INTO seats (seat_id, seat_category) VALUES (?, ?);", (seat[0], seat[1])) cursor.execute("INSERT INTO seats (seat_id, seat_category) VALUES (?, ?);", (seat[0], seat[1]))
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.generate_fresh_seats_table(seats) return self.generate_fresh_seats_table(seats)
except Exception as e: except Exception as e:
logger.warning(f"Error generating fresh seats table: {e}") logger.warning(f"Error generating fresh seats table: {e}")
@ -332,7 +354,7 @@ class DatabaseService:
cursor.execute("SELECT seats.*, users.* FROM seats LEFT JOIN users ON seats.user = users.user_id;") cursor.execute("SELECT seats.*, users.* FROM seats LEFT JOIN users ON seats.user = users.user_id;")
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.get_seating_info() return self.get_seating_info()
except Exception as e: except Exception as e:
logger.warning(f"Error getting seats table: {e}") logger.warning(f"Error getting seats table: {e}")
@ -355,7 +377,7 @@ class DatabaseService:
affected_rows = cursor.rowcount affected_rows = cursor.rowcount
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.seat_user(seat_id, user_id) return self.seat_user(seat_id, user_id)
except Exception as e: except Exception as e:
logger.warning(f"Error seating user: {e}") logger.warning(f"Error seating user: {e}")
@ -369,7 +391,7 @@ class DatabaseService:
cursor.execute("SELECT * FROM catering_menu_items;") cursor.execute("SELECT * FROM catering_menu_items;")
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.get_menu_items() return self.get_menu_items()
except Exception as e: except Exception as e:
logger.warning(f"Error fetching menu items: {e}") logger.warning(f"Error fetching menu items: {e}")
@ -393,7 +415,7 @@ class DatabaseService:
cursor.execute("SELECT * FROM catering_menu_items WHERE catering_menu_item_id = ?;", (menu_item_id, )) cursor.execute("SELECT * FROM catering_menu_items WHERE catering_menu_item_id = ?;", (menu_item_id, ))
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.get_menu_item(menu_item_id) return self.get_menu_item(menu_item_id)
except Exception as e: except Exception as e:
logger.warning(f"Error fetching menu items: {e}") logger.warning(f"Error fetching menu items: {e}")
@ -420,7 +442,7 @@ class DatabaseService:
) )
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.add_menu_item(name, info, price, category, is_disabled) return self.add_menu_item(name, info, price, category, is_disabled)
except Exception as e: except Exception as e:
logger.warning(f"Error adding menu item: {e}") logger.warning(f"Error adding menu item: {e}")
@ -441,7 +463,7 @@ class DatabaseService:
cursor.execute("DELETE FROM catering_menu_items WHERE catering_menu_item_id = ?;", (menu_item_id,)) cursor.execute("DELETE FROM catering_menu_items WHERE catering_menu_item_id = ?;", (menu_item_id,))
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.delete_menu_item(menu_item_id) return self.delete_menu_item(menu_item_id)
except Exception as e: except Exception as e:
logger.warning(f"Error deleting menu item: {e}") logger.warning(f"Error deleting menu item: {e}")
@ -458,7 +480,7 @@ class DatabaseService:
affected_rows = cursor.rowcount affected_rows = cursor.rowcount
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.update_menu_item(updated_item) return self.update_menu_item(updated_item)
except Exception as e: except Exception as e:
logger.warning(f"Error updating menu item: {e}") logger.warning(f"Error updating menu item: {e}")
@ -489,7 +511,7 @@ class DatabaseService:
is_delivery=is_delivery is_delivery=is_delivery
) )
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.add_new_order(menu_items, user_id, is_delivery) return self.add_new_order(menu_items, user_id, is_delivery)
except Exception as e: except Exception as e:
logger.warning(f"Error placing order: {e}") logger.warning(f"Error placing order: {e}")
@ -505,7 +527,7 @@ class DatabaseService:
affected_rows = cursor.rowcount affected_rows = cursor.rowcount
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.change_order_status(order_id, status) return self.change_order_status(order_id, status)
except Exception as e: except Exception as e:
logger.warning(f"Error updating menu item: {e}") logger.warning(f"Error updating menu item: {e}")
@ -528,7 +550,7 @@ class DatabaseService:
cursor.execute(query) cursor.execute(query)
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.get_orders(user_id, status) return self.get_orders(user_id, status)
except Exception as e: except Exception as e:
logger.warning(f"Error getting orders: {e}") logger.warning(f"Error getting orders: {e}")
@ -560,7 +582,7 @@ class DatabaseService:
) )
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.get_menu_items_for_order(order_id) return self.get_menu_items_for_order(order_id)
except Exception as e: except Exception as e:
logger.warning(f"Error getting order items: {e}") logger.warning(f"Error getting order items: {e}")
@ -587,7 +609,7 @@ class DatabaseService:
) )
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.set_user_profile_picture(user_id, picture_data) return self.set_user_profile_picture(user_id, picture_data)
except Exception as e: except Exception as e:
logger.warning(f"Error setting user profile picture: {e}") logger.warning(f"Error setting user profile picture: {e}")
@ -602,7 +624,7 @@ class DatabaseService:
return return
return r[0] return r[0]
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.get_user_profile_picture(user_id) return self.get_user_profile_picture(user_id)
except Exception as e: except Exception as e:
logger.warning(f"Error setting user profile picture: {e}") logger.warning(f"Error setting user profile picture: {e}")
@ -615,7 +637,7 @@ class DatabaseService:
cursor.execute("SELECT * FROM users;") cursor.execute("SELECT * FROM users;")
self._connection.commit() self._connection.commit()
except mariadb.InterfaceError: except mariadb.InterfaceError:
self._establish_new_connection() self.establish_new_connection()
return self.get_all_users() return self.get_all_users()
except Exception as e: except Exception as e:
logger.warning(f"Error getting all users: {e}") logger.warning(f"Error getting all users: {e}")