From f5c29789e87c7087f835a62eaa268fc82a83c14b Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Sun, 17 Jan 2021 21:46:44 +0000 Subject: [PATCH] Update Umsetzung --- Projektarbeit-3/Umsetzung.md | 52 +++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/Projektarbeit-3/Umsetzung.md b/Projektarbeit-3/Umsetzung.md index 49b6faf..712b79f 100644 --- a/Projektarbeit-3/Umsetzung.md +++ b/Projektarbeit-3/Umsetzung.md @@ -64,7 +64,7 @@ graph LR TYPING --> INSERT[in Datenbank schreiben] ``` -Der Vorgang geschieht komplett im Arbeitsspeicher, was die maximale Lesegeschwindigkeit ermöglicht. Der Import dauert von 0 bis 100 % etwa 60 Minuten und verbraucht 6 Gigabyte Festplattenspeicher, sowie Netzwerktraffic. +Der Vorgang geschieht komplett im Arbeitsspeicher, was die maximale Lesegeschwindigkeit ermöglicht. Der Import dauert von 0 bis 100 % etwa 60 Minuten und verbraucht 6 Gigabyte Festplattenspeicher, sowie Netzwerktraffic. **Insgesamt werden etwa 60 Millionen Datensätze für über 800 Bikestations geladen!** Der wichtigste Teil, der Importer der "usage-stats", ist hier Ausschnittsweise abgebildet. ```python @@ -101,7 +101,57 @@ for entry in entries: Obiges zeigt die Transformation, sowie die Datentypanpassung eines Eintrags. Es fällt sofort die konvertierung der Datumsangaben auf, sowie die "-1" Defaultwerte der Zahlenspalten. Ebenso kann ein Value Error auftreten, wenn das Datum nicht geparsed werden kann. Sollte dies passieren, wird der komplette Datensatz ignoriert. Gleiches gilt für eine Datei mit weniger Spalten wie erwartet (KeyError). +## Endpoint Implementation +Zunächst ist es sinnvoll, einen einfachen Endpoint zu Betrachten. Hierzu eignet sich der Accidents-Endpoint. +Damit FastAPI gute Dokumentation generieren kann, muss das Framework wissen, wie die Daten aussehen, die zurückgegeben werden. Dafür werden in Python Klassen benutzt, die von Typ "BaseModel" erben. + +```python +class Accident(BaseModel): + lat: float + lon: float + severity: Severity +``` + +Weiter ist es nötig, alle "severity" Werte als ENUM anzugeben, dies geschieht durch eine weitere Klasse + +```python +class Severity(str, Enum): + slight = "Slight" + serious = "Serious" + fatal = "Fatal +``` + +Um nun den Request zu bedienen, implementieren wir folgende Funktion: +```python +@router.get( + "/", + name="Get all accidents", + description="Get all bike accidents in London.", + response_model=List[Accident] +) +def get_accidents(): + return api_database.get_all_accidents() +``` +Hier ist zu sehen, wie auf einem Subrouter über eine Annotation (ähnlich wie bei Spring Boot) ein neuer Endpoint registriert wird. Der Subrouter ist hier "/api/latest/accidents". Des Weiteren wird das Response-Model als eine Liste von Accidents angegeben. Hieraus kann dann FastAPI ganz leicht die Dokumentation des Returnwertes der API ableiten. + +![image](uploads/22a23d5c70df7f0ef7fff8b2260a4b87/image.png) + +Der Aufruf von `api_database.get_all_accidents()` ist nun nur noch eine reine delegation an eine Funtkion mit den SQL Befehlen. + +### Probleme +Zunächst sah alles gut aus und die API war auch entsprechend schnell. Sobald allerdings größere Zeitspannen abgerufen werden, wuchs die Zeit enorm. Folgende Werte wurden im zusammenhang mit einer Bikestation gemessen: +* Abruf von **einem Tag:** ~300ms +* Abruf von **einer Woche:** ~1.25s +* Abruf von **zwei Wochen:** ~12s +* Abruf von **einem Monat:** ~90s +* Abruf von **drei Monaten:** ~7min +* Abruf von **einem Jahr:** Nicht möglich! + +Das würde bedeuten, dass wir nur etwa 10% unseres Datensatzes nutzen könnten. + ## Performance + + * 60 Mio Datensätze * Indexe für schnellere querries * Indexe auf "computed values"