diff --git a/code/online-part-2a/FS25 Seminarplatz Vergabe.ipynb b/code/online-part-2a/FS25 Seminarplatz Vergabe.ipynb deleted file mode 100644 index a1b651e..0000000 --- a/code/online-part-2a/FS25 Seminarplatz Vergabe.ipynb +++ /dev/null @@ -1,240 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Seminarplatz-Vergabe\n", - "\n", - "In folgendem Problem geht es darum, dass eine Gruppe von Studierenden einen oder mehrere Seminarplätze in einem Kurs bekommen sollen. Es 8 Seminargruppen mit insgesamt 40 Plätzen, diese sind in der Tabelle `seminar_sessions` abgelegt.\n", - "\n", - "Bei einer Anmeldung einer einzelnen Person wird die Anzahl der Plätze in der Tabelle `seminar_sessions` um 1 reduziert und ein Eintrag in der Tabelle `seminar_registrations` erstellt. Dies darf natürlich nur passieren, wenn noch Plätze verfügbar sind.\n", - "\n", - "Die Anzahl der vergebenen Plätze in der Tabelle `seminar_registrations` und die Anzahl der verfügbaren Plätze in der Tabelle `seminar_sessions` müssen konsistent sein, also in der Summe 40 ergeben.\n", - "\n", - "In einem Lasttest mit mehreren Threads zeigt sich: Die Anzahl der vergebenen Plätze in der Tabelle `seminar_registrations` und die Anzahl der verfügbaren Plätze in der Tabelle `seminar_sessions` sind nicht konsistent! \n", - "\n", - "### Ihre Aufgabe:\n", - "Führen Sie geeignete Transaktionen ein, um die Konsistenz zu gewährleisten!\n", - "\n", - "Implementierungen Sie dazu zunächst die Funktionen, die mit TODO gekennzeichnet sind, und passen Sie die Funktion `reserve_seat` an, um die Konsistenz zu gewährleisten.\n", - "\n", - "Am Code des Lasttests müssen Sie nichts ändern, er dient nur dazu, das Problem zu demonstrieren.\n", - "\n", - "### Bemerkungen:\n", - "\n", - "- nicht jeder Durchlauf verursacht Inkonsistenzen, manchmal müssen Sie den Code mehrmals ausführen, um Inkonsistenzen zu finden\n", - "- auch Deadlocks können auftreten, diese müssen auch vermieden werden\n", - "- die User-Tabelle wurde weggelassen, da sie für das Problem nicht relevant ist\n", - "- an manchen Stellen ist der Code absichtlich etwas ineffizient gehalten, um die Inkonsistenzen zu begünstigen (z.B. würden Probleme mit Inkonsistenzen seltener auftreten, wenn die Query `UPDATE seminar_sessions SET seats = seats-1` lauten würde).\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import psycopg2\n", - "import psycopg2.extras" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Datenbankverbindung herstellen\n", - "def get_connection():\n", - " # TODO Datenbankverbindung herstellen - geben Sie hier Ihre Zugangsdaten ein\n", - " conn = psycopg2.connect(\"dbname=... user=... password=...\")\n", - " \n", - " # TODO später autocommit ausschalten\n", - " conn.autocommit = True\n", - "\n", - " return conn" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Tabelle erzeugen - immer wenn diese Zelle ausgeführt wird, wird die Tabelle neu erzeugt\n", - "conn = get_connection()\n", - "cursor = conn.cursor()\n", - "cursor.execute(\"\"\"\n", - " DROP TABLE IF EXISTS seminar_registrations CASCADE;\n", - " DROP TABLE IF EXISTS seminar_sessions;\n", - " \n", - " CREATE TABLE IF NOT EXISTS seminar_sessions (\n", - " id SERIAL PRIMARY KEY, \n", - " seats INTEGER NOT NULL\n", - " );\n", - " \n", - " INSERT INTO seminar_sessions (seats) VALUES \n", - " (4), (4), (5), (5), (4), (10), (5), (3);\n", - " \n", - " \n", - " CREATE TABLE IF NOT EXISTS seminar_registrations (\n", - " user_id INTEGER NOT NULL,\n", - " session_id INTEGER NOT NULL,\n", - " FOREIGN KEY (session_id) REFERENCES seminar_sessions(id)\n", - " ); \n", - "\"\"\")\n", - "conn.commit()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Hilfsfunktion: Alle Seminar-Sessions ausgeben\n", - "def show_sessions():\n", - " # TODO alle Seminar-Sessions auf der Console ausgeben\n", - " pass\n", - "\n", - "# Anzahl aller freien Plätze bestimmen\n", - "def count_free_seats():\n", - " # TODO Anzahl freier Plätze bestimmen (Summe über die Spalte \"seats\")\n", - " # und mit return zurückgeben\n", - " pass\n", - "\n", - "# Anzahl der belegten Plätze bestimmen\n", - "def count_occupied_seats():\n", - " # TODO Anzahl belegter Plätze bestimmen (Anzahl der Einträge in der Tabelle seminar_registrations)\n", - " # und mit return zurückgeben\n", - " pass\n", - "\n", - "# Testaufrufe\n", - "\n", - "# show_sessions()\n", - "# print(\"freie Plätze\", count_free_seats())\n", - "# print(\"belegte Plätze\", count_occupied_seats())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Diese Funktion soll einen Platz reservieren und True zurückgeben, wenn es geklappt hat.\n", - "# Wenn kein Platz mehr frei ist, soll False zurückgegeben werden.\n", - "\n", - "# TODO: Führen Sie geeignete Transaktionssteuerung ein, um sicherzustellen, dass die Funktion korrekt arbeitet.\n", - "\n", - "def reserve_seat(conn, user_id, session_id):\n", - " with conn.cursor() as cursor:\n", - " cursor.execute(\"SELECT seats FROM seminar_sessions WHERE id = %s\", (session_id,))\n", - " seats = cursor.fetchone()[0]\n", - "\n", - " if seats > 0:\n", - " cursor.execute(\"UPDATE seminar_sessions SET seats = %s WHERE id = %s\", (seats-1, session_id,))\n", - " cursor.execute(\"INSERT INTO seminar_registrations (user_id, session_id) VALUES (%s, %s)\", (user_id, session_id))\n", - " return True\n", - " else:\n", - " return False\n", - " \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import random \n", - "\n", - "# 20 zufällige Anmeldungen durchführen - diese Funktion wird später als Thread gestartet\n", - "def random_seat_reservation(n=20):\n", - " # jeder Thread hat seine eigene Verbindung\n", - " conn = get_connection()\n", - " \n", - " # n mal einen zufälligen Platz für eine zufällige User-ID reservieren\n", - " for _ in range(n):\n", - " random_user_id = random.randint(1, 100)\n", - " random_session_id = random.randint(1, 8)\n", - "\n", - " if reserve_seat(conn, random_user_id, random_session_id):\n", - " print(\"User\", random_user_id, \"hat sich für Session\", random_session_id, \"angemeldet\")\n", - " else:\n", - " print(\"Session\", random_session_id, \"ist ausgebucht\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import threading\n", - "\n", - "# Diese Funktion gibt Informationen über die Belegung der Seminare aus\n", - "def print_info():\n", - " print('-'*20)\n", - " \n", - " free_seats = count_free_seats()\n", - " occupied_seats = count_occupied_seats()\n", - "\n", - " print(\"Freie Plätze:\", free_seats)\n", - " print(\"Belegte Plätze:\", occupied_seats)\n", - " print(\"Gesamt:\", free_seats + occupied_seats)\n", - " \n", - " if free_seats + occupied_seats == 40:\n", - " print(\"Platzanzahl ist konsistent!\")\n", - " else:\n", - " print(\"Platzanzahl ist inkonsistent!\")\n", - "\n", - " print('-'*20)\n", - "\n", - "# -------------------------------------------------------------------------\n", - "# Mini-Lasttest, 5 Threads starten, die jeweils 20 Reservierungen vornehmen\n", - "# -------------------------------------------------------------------------\n", - "\n", - "print(\"Vor dem Lasttest:\")\n", - "print_info()\n", - "\n", - "threads = [ threading.Thread(target=random_seat_reservation) for _ in range(5)]\n", - "\n", - "# Threads starten\n", - "for t in threads:\n", - " t.start()\n", - "\n", - "# Auf das Ende der Threads warten\n", - "for t in threads:\n", - " t.join()\n", - "\n", - " \n", - "print(\"Nach dem Lasttest:\")\n", - "print_info()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/code/online-part-2a/FS25 _Seminarplatz_Vergabe.ipynb b/code/online-part-2a/FS25 _Seminarplatz_Vergabe.ipynb new file mode 100644 index 0000000..2236c9b --- /dev/null +++ b/code/online-part-2a/FS25 _Seminarplatz_Vergabe.ipynb @@ -0,0 +1,391 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Seminarplatz-Vergabe\n", + "\n", + "In folgendem Problem geht es darum, dass eine Gruppe von Studierenden einen oder mehrere Seminarplätze in einem Kurs bekommen sollen. Es 8 Seminargruppen mit insgesamt 40 Plätzen, diese sind in der Tabelle `seminar_sessions` abgelegt.\n", + "\n", + "Bei einer Anmeldung einer einzelnen Person wird die Anzahl der Plätze in der Tabelle `seminar_sessions` um 1 reduziert und ein Eintrag in der Tabelle `seminar_registrations` erstellt. Dies darf natürlich nur passieren, wenn noch Plätze verfügbar sind.\n", + "\n", + "Die Anzahl der vergebenen Plätze in der Tabelle `seminar_registrations` und die Anzahl der verfügbaren Plätze in der Tabelle `seminar_sessions` müssen konsistent sein, also in der Summe 40 ergeben.\n", + "\n", + "In einem Lasttest mit mehreren Threads zeigt sich: Die Anzahl der vergebenen Plätze in der Tabelle `seminar_registrations` und die Anzahl der verfügbaren Plätze in der Tabelle `seminar_sessions` sind nicht konsistent! \n", + "\n", + "### Ihre Aufgabe:\n", + "Führen Sie geeignete Transaktionen ein, um die Konsistenz zu gewährleisten!\n", + "\n", + "Implementierungen Sie dazu zunächst die Funktionen, die mit TODO gekennzeichnet sind, und passen Sie die Funktion `reserve_seat` an, um die Konsistenz zu gewährleisten.\n", + "\n", + "Am Code des Lasttests müssen Sie nichts ändern, er dient nur dazu, das Problem zu demonstrieren.\n", + "\n", + "### Bemerkungen:\n", + "\n", + "- nicht jeder Durchlauf verursacht Inkonsistenzen, manchmal müssen Sie den Code mehrmals ausführen, um Inkonsistenzen zu finden\n", + "- auch Deadlocks können auftreten, diese müssen auch vermieden werden\n", + "- die User-Tabelle wurde weggelassen, da sie für das Problem nicht relevant ist\n", + "- an manchen Stellen ist der Code absichtlich etwas ineffizient gehalten, um die Inkonsistenzen zu begünstigen (z.B. würden Probleme mit Inkonsistenzen seltener auftreten, wenn die Query `UPDATE seminar_sessions SET seats = seats-1` lauten würde).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "import psycopg2\n", + "import psycopg2.extras" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "# Datenbankverbindung herstellen\n", + "def get_connection():\n", + " conn = psycopg2.connect(\n", + " host=\"localhost\",\n", + " port=5432,\n", + " dbname=\"postgres\",\n", + " user=\"postgres\",\n", + " password=\"postgres\",\n", + " )\n", + "\n", + " conn.autocommit = False\n", + " return conn" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "# Tabelle erzeugen - immer wenn diese Zelle ausgeführt wird, wird die Tabelle neu erzeugt\n", + "conn = get_connection()\n", + "cursor = conn.cursor()\n", + "cursor.execute(\"\"\"\n", + " DROP TABLE IF EXISTS seminar_registrations CASCADE;\n", + " DROP TABLE IF EXISTS seminar_sessions;\n", + "\n", + " CREATE TABLE IF NOT EXISTS seminar_sessions (\n", + " id SERIAL PRIMARY KEY,\n", + " seats INTEGER NOT NULL\n", + " );\n", + "\n", + " INSERT INTO seminar_sessions (seats) VALUES\n", + " (4), (4), (5), (5), (4), (10), (5), (3);\n", + "\n", + "\n", + " CREATE TABLE IF NOT EXISTS seminar_registrations (\n", + " user_id INTEGER NOT NULL,\n", + " session_id INTEGER NOT NULL,\n", + " FOREIGN KEY (session_id) REFERENCES seminar_sessions(id)\n", + " );\n", + "\"\"\")\n", + "conn.commit()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Session 1: 4 free seats\n", + "Session 2: 4 free seats\n", + "Session 3: 5 free seats\n", + "Session 4: 5 free seats\n", + "Session 5: 4 free seats\n", + "Session 6: 10 free seats\n", + "Session 7: 5 free seats\n", + "Session 8: 3 free seats\n", + "freie Plätze 40\n", + "belegte Plätze 0\n" + ] + } + ], + "source": [ + "# Hilfsfunktion: Alle Seminar-Sessions ausgeben\n", + "\n", + "# Task: Alle Seminar-Sessions auf der Console ausgeben\n", + "def show_sessions():\n", + " with (\n", + " get_connection() as conn,\n", + " conn.cursor(cursor_factory=psycopg2.extras.DictCursor) as cur,\n", + " ):\n", + " cur.execute(\"SELECT id, seats FROM seminar_sessions ORDER BY id\")\n", + " for row in cur.fetchall():\n", + " print(f\"Session {row['id']}: {row['seats']} free seats\")\n", + "\n", + "# Anzahl aller freien Plätze bestimmen\n", + "# Task: Anzahl freier Plätze bestimmen (Summe über die Spalte \"seats\")\n", + "def count_free_seats():\n", + " with get_connection() as conn, conn.cursor() as cur:\n", + " cur.execute(\"SELECT COALESCE(SUM(seats), 0) FROM seminar_sessions\")\n", + " return cur.fetchone()[0]\n", + "\n", + "# Anzahl der belegten Plätze bestimmen\n", + "# Task: Anzahl belegter Plätze bestimmen (Anzahl der Einträge in der Tabelle seminar_registrations)\n", + "def count_occupied_seats():\n", + " with get_connection() as conn, conn.cursor() as cur:\n", + " cur.execute(\"SELECT COUNT(*) FROM seminar_registrations\")\n", + " return cur.fetchone()[0]\n", + "\n", + "# Testaufrufe\n", + "\n", + "# show_sessions()\n", + "show_sessions()\n", + "print(\"freie Plätze\", count_free_seats())\n", + "print(\"belegte Plätze\", count_occupied_seats())" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "# Diese Funktion reserviert einen Platz in einer Transaktion.\n", + "def reserve_seat(conn, user_id, session_id):\n", + " \"\"\"Versucht, einen Seminarplatz für den gegebenen Benutzer in der angegebenen Session zu reservieren.\n", + " Gibt True zurück, wenn die Reservierung erfolgreich war, sonst False. Die Operation wird in einer\n", + " transaktionalen Einheit ausgeführt, um Inkonsistenzen zu vermeiden.\"\"\"\n", + " with conn:\n", + " with conn.cursor() as cur:\n", + " cur.execute(\"SELECT seats FROM seminar_sessions WHERE id = %s FOR UPDATE\", (session_id,))\n", + " seats = cur.fetchone()[0]\n", + " if seats <= 0:\n", + " return False\n", + " cur.execute(\"UPDATE seminar_sessions SET seats = seats - 1 WHERE id = %s\", (session_id,))\n", + " cur.execute(\n", + " \"INSERT INTO seminar_registrations (user_id, session_id) VALUES (%s, %s)\",\n", + " (user_id, session_id)\n", + " )\n", + " return True\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "# 20 zufällige Anmeldungen durchführen - diese Funktion wird später als Thread gestartet\n", + "def random_seat_reservation(n=20):\n", + " # jeder Thread hat seine eigene Verbindung\n", + " conn = get_connection()\n", + "\n", + " # n mal einen zufälligen Platz für eine zufällige User-ID reservieren\n", + " for _ in range(n):\n", + " random_user_id = random.randint(1, 100)\n", + " random_session_id = random.randint(1, 8)\n", + "\n", + " if reserve_seat(conn, random_user_id, random_session_id):\n", + " print(\"User\", random_user_id, \"hat sich für Session\", random_session_id, \"angemeldet\")\n", + " else:\n", + " print(\"Session\", random_session_id, \"ist ausgebucht\")" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Vor dem Lasttest:\n", + "--------------------\n", + "Freie Plätze: 40\n", + "Belegte Plätze: 0\n", + "Gesamt: 40\n", + "Platzanzahl ist konsistent!\n", + "--------------------\n", + "User 25 hat sich für Session 3 angemeldet\n", + "User 25 hat sich für Session 4 angemeldet\n", + "User 31 hat sich für Session 8 angemeldet\n", + "User 91 hat sich für Session 2 angemeldet\n", + "User 66 hat sich für Session 1 angemeldet\n", + "User 61 hat sich für Session 3 angemeldet\n", + "User 53 hat sich für Session 7 angemeldet\n", + "User 70 hat sich für Session 4 angemeldet\n", + "User 98 hat sich für Session 3 angemeldet\n", + "User 42 hat sich für Session 5 angemeldet\n", + "User 22 hat sich für Session 7 angemeldet\n", + "User 71 hat sich für Session 3 angemeldet\n", + "User 50 hat sich für Session 2 angemeldet\n", + "User 26 hat sich für Session 1 angemeldet\n", + "User 53 hat sich für Session 5 angemeldet\n", + "User 54 hat sich für Session 4 angemeldet\n", + "User 72 hat sich für Session 1 angemeldet\n", + "User 3 hat sich für Session 3 angemeldet\n", + "User 95 hat sich für Session 7 angemeldet\n", + "User 83 hat sich für Session 4 angemeldet\n", + "Session 3 ist ausgebucht\n", + "User 90 hat sich für Session 7 angemeldet\n", + "User 78 hat sich für Session 1 angemeldet\n", + "User 14 hat sich für Session 6 angemeldet\n", + "User 84 hat sich für Session 7 angemeldet\n", + "Session 1 ist ausgebucht\n", + "User 75 hat sich für Session 8 angemeldet\n", + "Session 7 ist ausgebucht\n", + "Session 3 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "User 97 hat sich für Session 4 angemeldet\n", + "User 93 hat sich für Session 6 angemeldet\n", + "User 28 hat sich für Session 2 angemeldet\n", + "User 92 hat sich für Session 8 angemeldet\n", + "User 88 hat sich für Session 5 angemeldet\n", + "Session 7 ist ausgebucht\n", + "User 64 hat sich für Session 2 angemeldet\n", + "User 14 hat sich für Session 6 angemeldet\n", + "Session 7 ist ausgebucht\n", + "User 74 hat sich für Session 5 angemeldet\n", + "Session 3 ist ausgebucht\n", + "Session 1 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "Session 2 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "Session 5 ist ausgebucht\n", + "Session 4 ist ausgebucht\n", + "Session 1 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "Session 4 ist ausgebucht\n", + "Session 3 ist ausgebucht\n", + "Session 2 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "Session 3 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "Session 3 ist ausgebucht\n", + "Session 8 ist ausgebucht\n", + "Session 8 ist ausgebucht\n", + "Session 3 ist ausgebucht\n", + "User 49 hat sich für Session 6 angemeldet\n", + "Session 3 ist ausgebucht\n", + "Session 1 ist ausgebucht\n", + "User 23 hat sich für Session 6 angemeldet\n", + "Session 2 ist ausgebucht\n", + "Session 1 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "Session 2 ist ausgebucht\n", + "Session 8 ist ausgebucht\n", + "Session 2 ist ausgebucht\n", + "Session 8 ist ausgebucht\n", + "Session 5 ist ausgebucht\n", + "User 9 hat sich für Session 6 angemeldet\n", + "Session 3 ist ausgebucht\n", + "Session 5 ist ausgebucht\n", + "Session 4 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "User 13 hat sich für Session 6 angemeldet\n", + "Session 2 ist ausgebucht\n", + "Session 1 ist ausgebucht\n", + "Session 2 ist ausgebucht\n", + "Session 1 ist ausgebucht\n", + "Session 8 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "Session 5 ist ausgebucht\n", + "User 73 hat sich für Session 6 angemeldet\n", + "Session 8 ist ausgebucht\n", + "Session 8 ist ausgebucht\n", + "Session 3 ist ausgebucht\n", + "Session 1 ist ausgebucht\n", + "Session 2 ist ausgebucht\n", + "Session 4 ist ausgebucht\n", + "Session 5 ist ausgebucht\n", + "Session 4 ist ausgebucht\n", + "Session 3 ist ausgebucht\n", + "Session 2 ist ausgebucht\n", + "Session 1 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "Session 7 ist ausgebucht\n", + "Session 5 ist ausgebucht\n", + "Nach dem Lasttest:\n", + "--------------------\n", + "Freie Plätze: 2\n", + "Belegte Plätze: 38\n", + "Gesamt: 40\n", + "Platzanzahl ist konsistent!\n", + "--------------------\n" + ] + } + ], + "source": [ + "import threading\n", + "\n", + "# Diese Funktion gibt Informationen über die Belegung der Seminare aus\n", + "def print_info():\n", + " print('-'*20)\n", + "\n", + " free_seats = count_free_seats()\n", + " occupied_seats = count_occupied_seats()\n", + "\n", + " print(\"Freie Plätze:\", free_seats)\n", + " print(\"Belegte Plätze:\", occupied_seats)\n", + " print(\"Gesamt:\", free_seats + occupied_seats)\n", + "\n", + " if free_seats + occupied_seats == 40:\n", + " print(\"Platzanzahl ist konsistent!\")\n", + " else:\n", + " print(\"Platzanzahl ist inkonsistent!\")\n", + "\n", + " print('-'*20)\n", + "\n", + "# -------------------------------------------------------------------------\n", + "# Mini-Lasttest, 5 Threads starten, die jeweils 20 Reservierungen vornehmen\n", + "# -------------------------------------------------------------------------\n", + "\n", + "print(\"Vor dem Lasttest:\")\n", + "print_info()\n", + "\n", + "threads = [ threading.Thread(target=random_seat_reservation) for _ in range(5)]\n", + "\n", + "# Threads starten\n", + "for t in threads:\n", + " t.start()\n", + "\n", + "# Auf das Ende der Threads warten\n", + "for t in threads:\n", + " t.join()\n", + "\n", + "\n", + "print(\"Nach dem Lasttest:\")\n", + "print_info()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "code (3.13.2)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/code/pyproject.toml b/code/pyproject.toml index e8d888a..6c5c86b 100644 --- a/code/pyproject.toml +++ b/code/pyproject.toml @@ -7,5 +7,6 @@ dependencies = [ "matplotlib>=3.10.3", "numpy>=2.2.5", "pandas>=2.2.3", + "psycopg2>=2.9.10", "pymongo>=4.12.1", ] diff --git a/code/uv.lock b/code/uv.lock index f40514e..1537fef 100644 --- a/code/uv.lock +++ b/code/uv.lock @@ -51,6 +51,7 @@ dependencies = [ { name = "matplotlib" }, { name = "numpy" }, { name = "pandas" }, + { name = "psycopg2" }, { name = "pymongo" }, ] @@ -60,6 +61,7 @@ requires-dist = [ { name = "matplotlib", specifier = ">=3.10.3" }, { name = "numpy", specifier = ">=2.2.5" }, { name = "pandas", specifier = ">=2.2.3" }, + { name = "psycopg2", specifier = ">=2.9.10" }, { name = "pymongo", specifier = ">=4.12.1" }, ] @@ -519,6 +521,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, ] +[[package]] +name = "psycopg2" +version = "2.9.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/62/51/2007ea29e605957a17ac6357115d0c1a1b60c8c984951c19419b3474cdfd/psycopg2-2.9.10.tar.gz", hash = "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11", size = 385672 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/49/a6cfc94a9c483b1fa401fbcb23aca7892f60c7269c5ffa2ac408364f80dc/psycopg2-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:91fd603a2155da8d0cfcdbf8ab24a2d54bca72795b90d2a3ed2b6da8d979dee2", size = 2569060 }, +] + [[package]] name = "ptyprocess" version = "0.7.0"