Update Umsetzung

Marcel Schwarz 2021-01-17 21:46:44 +00:00
parent b379115c99
commit f5c29789e8

@ -64,7 +64,7 @@ graph LR
TYPING --> INSERT[in Datenbank schreiben] 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. Der wichtigste Teil, der Importer der "usage-stats", ist hier Ausschnittsweise abgebildet.
```python ```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). 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 ## Performance
* 60 Mio Datensätze * 60 Mio Datensätze
* Indexe für schnellere querries * Indexe für schnellere querries
* Indexe auf "computed values" * Indexe auf "computed values"