3.1 KiB
3.1 KiB
Task 17 — PostgresStorage
Rückblick Task 16: Lock und @timer-Decorator
Ihr habt CONCURRENT_LOCKED und den @timer-Decorator eingeführt.
Die wichtigsten Punkte:
- Warum Lock, obwohl der GIL schützt? Der GIL verhindert echte
Parallelität auf CPU-Ebene, aber er gibt keine Garantien über die
Reihenfolge von Operationen oder die Konsistenz komplexerer
Datenstrukturen. Ein expliziter
Lockmacht die Absicht klar, ist portabel (auch ohne GIL, z.B. in PyPy oder zukünftigen Python-Versionen) und schützt bei read-modify-write-Operationen zuverlässig. time.perf_counter()ist präziser alstime.time()für Laufzeitmessungen, weil er eine hochauflösende Systemuhr verwendet und nicht durch Systemzeit-Anpassungen beeinflusst wird.
Aufgabe
Bisher speichern wir POIs in JSON-Dateien. Für grössere Datenmengen
und spätere Abfragen ist eine Datenbank besser geeignet. Ziel ist es,
PostgresStorage als zweites konkretes Backend zu implementieren —
ohne main.py, pipeline.py oder fetcher.py anzufassen.
Vorbereitung:
- Stelle sicher, dass eine PostgreSQL-Datenbank läuft und erreichbar ist.
- Erstelle in der PostgreSQL-Datenbank eine neue Datenbank. Das kannst Du z.B. in PGAdmin oder aus dem Terminal erreichen. Gebt der neuen Datenbank einen Namen (z.B. overpass) und ordnet ihr einen Nutzer (z.B. postgres) inkl. Passwort zu.
- Installiere
psycopg2:pip install psycopg2-binary - Ergänze den Connection-String in
config.yaml("postgresql://user:password@localhost:5432/dbname").
Konkret:
- Ergänze in
models.pyzwei Methoden in derPOI-Dataclass:
def to_row(self) -> tuple:
"""POI-Objekt → DB-Zeile (Schreiben)"""
...
@classmethod
def from_row(cls, row: dict) -> POI:
"""DB-Zeile → POI-Objekt (Lesen)"""
...
Die tags in POI sollen als JSONB gespeichert werden —
verwende dafür psycopg2.extras.Json.
-
Implementiere
PostgresStorage(Storage)instorage.py:- Nimmt
connection_string: strundtable: str = "pois"entgegen. _ensure_table(): Erstellt die Tabelle, falls sie nicht existiert (Primary Key:(id, poi_type)).store(): Schreibt alle POIs per UPSERT in die Tabelle (ON CONFLICT DO UPDATE). Verwendeexecute_valuesauspsycopg2.extrasfür effizientes Bulk-Insert.- Verwende
with conn:als Context-Manager für Transaktionen (automatisches commit/rollback). - Für die Bildung des SQL-Statements könnt ihr gerne in den Unterrichtsunterlagen (Folien) 'spicken'.
- Nimmt
-
Ergänze
build_storage()— derPOSTGRES-Case soll nunPostgresStorage(**params)zurückgeben stattNotImplementedError. -
Passe
config.yamlan:
storage:
type: postgres
params:
connection_string: "postgresql://user:password@localhost:5432/dbname"
Fragen zum Nachdenken:
- Warum
ON CONFLICT DO UPDATE(UPSERT) statt einem einfachenINSERT— was passiert ohne es beim zweiten Ausführen? - Was macht
with conn:als Context-Manager — was passiert bei einem Fehler innerhalb des Blocks?