Zurück zum Katalog
Season 13 17 Episoden 1h 9m 2026

High-Performance Python Async

Ausgabe 2026. Ein tiefer Einblick in die Beschleunigung von Python asyncio mit uvloop und die direkte Anbindung an PostgreSQL über das binäre Protokoll von asyncpg.

Python Core Asynchrone Programmierung
High-Performance Python Async
Aktuelle Wiedergabe
Click play to start
0:00
0:00
1
Das Bedürfnis nach Geschwindigkeit: Die uvloop-Architektur
Entdecke die architektonischen Unterschiede zwischen Pythons Standard-asyncio und uvloop. Wir untersuchen, wie uvloop Cython und libuv nutzt, um eine Performance ähnlich wie Go zu erreichen.
4m 04s
2
uvloop nahtlos integrieren
Lerne, wie du uvloop in deine Python-Anwendung integrierst. Diese Episode behandelt den EventLoopPolicy-Ansatz, um die Standard-Event-Loop nahtlos zu ersetzen.
4m 24s
3
Einführung in asyncpg: Das binäre Protokoll
Erkunde das grundlegende Design von asyncpg. Wir diskutieren, warum die Umgehung der Standard-DB-API zugunsten des binären Protokolls von PostgreSQL massive Leistungssteigerungen bringt.
3m 57s
4
Verbindungsaufbau und einfache Ausführung
Mache deine ersten Schritte mit asyncpg, indem du dich mit einer Datenbank verbindest und grundlegende Abfragen ausführst. Verstehe die native Postgres-Argument-Syntax.
3m 54s
5
Native Typkonvertierung
Entdecke, wie asyncpg PostgreSQL-Datentypen automatisch auf native Python-Objekte abbildet und so komplexes ORM-Parsing überflüssig macht.
3m 48s
6
Benutzerdefinierte Type Codecs
Lerne, benutzerdefinierte Datenkonvertierungen in asyncpg zu definieren. Diese Episode erklärt, wie du set_type_codec verwendest, um JSONB automatisch in Python-Dictionaries zu dekodieren.
4m 11s
7
Fortgeschrittene Codecs mit PostGIS
Tauche tief in benutzerdefinierte Type Codecs ein, indem du die PostGIS-Geometrietypen von PostgreSQL über das binäre Format auf Python Shapely-Objekte abbildest.
4m 05s
8
Transaktionen verwalten
Meistere Datenbanktransaktionen in asyncpg. Wir behandeln das Auto-Commit-Verhalten und wie man mehrere Abfragen mithilfe asynchroner Context Manager sicher ausführt.
3m 42s
9
Connection Pooling
Skaliere deine Anwendung mit dem integrierten Connection Pooling von asyncpg. Lerne, wie du Datenbankverbindungen in Webdiensten mit hohem Traffic effizient verwaltest.
3m 45s
10
Prepared Statement Caching
Verstehe, wie asyncpg das Query-Parsing mit automatischen Prepared Statements optimiert und warum externe Pooler wie PgBouncer Konflikte verursachen können.
4m 18s
11
Postgres-Arrays und IN-Klauseln
Löse den häufigsten Syntaxfehler bei der Migration zu asyncpg. Lerne, wie du Abfragen mithilfe von ANY() korrekt nach einer Liste von Werten filterst.
4m 03s
12
Record-Objekte vs. Named Tuples
Erkunde das einzigartige Design von asyncpg Record-Objekten. Verstehe, warum die Dot-Notation standardmäßig weggelassen wird und wie du sie mit benutzerdefinierten Klassen aktivieren kannst.
4m 33s
13
Ergebnisse streamen mit Cursors
Verhindere Speichererschöpfung bei der Abfrage riesiger Datensätze. Lerne, wie du asyncpg Cursors verwendest, um Ergebnisse Stück für Stück zu streamen.
4m 59s
14
Blitzschnelle Datenaufnahme mit COPY
Gib deinen Datenaufnahme-Pipelines einen Boost. Wir untersuchen das PostgreSQL COPY-Protokoll, um Daten exponentiell schneller massenhaft zu laden als mit INSERT-Statements.
3m 57s
15
Asynchrones Listen und Notify
Erschließe ereignisgesteuerte Echtzeit-Architekturen direkt in PostgreSQL. Lerne, wie du add_listener von asyncpg für sofortiges Pub/Sub-Messaging verwendest.
3m 54s
16
Telemetrie und Query Logging
Erhalte tiefe Einblicke in die Leistung deiner Datenbank. Entdecke, wie du asyncpg Log Listeners verwendest, um langsame Abfragen zu verfolgen und die Ausführungstelemetrie zu überwachen.
3m 43s
17
Verbindungen mit SSL absichern
Stelle sicher, dass deine Datenbankverbindungen sicher sind. Wir behandeln die Konfiguration des SSL-Kontexts und wie man direktes TLS bei der Verbindung zu Cloud-Datenbanken erzwingt.
3m 57s

Episoden

1

Das Bedürfnis nach Geschwindigkeit: Die uvloop-Architektur

4m 04s

Entdecke die architektonischen Unterschiede zwischen Pythons Standard-asyncio und uvloop. Wir untersuchen, wie uvloop Cython und libuv nutzt, um eine Performance ähnlich wie Go zu erreichen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 1 von 17. Was wäre, wenn du die Performance deines Python-Async-Codes verdoppeln könntest, ohne eine einzige Funktion neu zu schreiben? Genau das verspricht die Technologie, die wir uns heute ansehen: die uvloop-Architektur. Wenn Entwickler über Standard-Python-Async sprechen, vermischen sie oft zwei unterschiedliche Layer. Der erste Layer ist das Application Programming Interface. Dazu gehören die async und await Keywords, die du eintippst, die Futures und die Tasks. Der zweite Layer ist der Event Loop selbst. Der Event Loop ist der interne Scheduler, der Sockets überwacht, Network Connections verwaltet und entscheidet, welcher Task als Nächstes läuft. Standard-Python liefert sowohl das Interface als auch einen Default Event Loop, der in purem Python geschrieben ist. Hier ist die wichtigste Erkenntnis. Das Interface und der Event Loop sind entkoppelt. Python erlaubt es dir, den zugrunde liegenden Scheduler auszutauschen, ohne die Syntax zu ändern, die du schreibst. Stell es dir wie ein Auto vor. Die Async-API ist das Lenkrad, die Pedale und das Armaturenbrett. Mit denen interagierst du direkt. Der Event Loop ist der Motor unter der Haube. Den Default Python Event Loop durch uvloop zu ersetzen, ist so, als würdest du einen V8-Motor in dein Auto einbauen. Du lenkst und bremst immer noch genau gleich, aber das Auto fährt deutlich schneller. Der Kern von uvloop basiert auf zwei architektonischen Entscheidungen, um diese Geschwindigkeit zu erreichen. Erstens ist es ein Drop-in Replacement, das komplett in Cython geschrieben ist. Cython kompiliert Python-ähnlichen Code runter zu hochoptimierten C Extensions. Das eliminiert den Overhead des Standard-Python-Interpreters, wenn die Hot Paths des Schedulers ausgeführt werden. Pure Python Event Loops verbringen viel Zeit damit, interne Objekte zu erstellen und den Interpreter State zu managen, nur um ganz normale Network Events zu verarbeiten. Cython räumt das aus dem Weg. Jedes Mal, wenn der Loop einen Socket checkt oder einen Task aufweckt, führt er nativen C-Code aus, anstatt den Umweg über pures Python zu gehen. Zweitens delegiert uvloop die eigentlichen Interaktionen mit dem Betriebssystem an eine C-Library namens libuv. Wenn dir der Name bekannt vorkommt, liegt das daran, dass libuv die asynchrone I/O-Engine ist, die Node.js antreibt. Sie ist battle-tested, extrem auf netzwerkintensive Workloads optimiert und kümmert sich um all die komplexen Cross-Platform-Details von asynchronem Networking. Indem libuv in eine kompakte Cython-Shell gepackt wird, bringt uvloop genau dieses Performance-Profil direkt zu Python. Das architektonische Ergebnis ist massiv. Indem der pure Python-Scheduler umgangen wird und man sich auf eine kompilierte C-Engine verlässt, macht uvloop deine asyncio-Applications mindestens doppelt so schnell. In vielen Benchmark-Szenarien mit hoher Connection Concurrency erlaubt es Python, mit der Performance von kompilierten Sprachen wie Go mitzuhalten. Du bekommst die Developer Velocity von Python mit der rohen Execution Speed von nativem C-Networking. Der Umstieg erfordert null Änderungen an deiner Business Logic, deinen Database Queries oder deinen API Endpoints. Das grundlegende Takeaway hier ist, dass Performance Bottlenecks in Standard-Python-Async selten an der Sprachsyntax liegen, sondern vielmehr an der Execution Engine. Und wenn du diese Engine austauschst, bekommst du native C-Geschwindigkeiten, während deine bestehenden Python-Abstraktionen erhalten bleiben. Wenn du die Show unterstützen möchtest, kannst du auf Patreon nach DevStoriesEU suchen. Das war es für diese Folge. Danke fürs Zuhören und keep building!
2

uvloop nahtlos integrieren

4m 24s

Lerne, wie du uvloop in deine Python-Anwendung integrierst. Diese Episode behandelt den EventLoopPolicy-Ansatz, um die Standard-Event-Loop nahtlos zu ersetzen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 2 von 17. Die mächtigste Performance-Optimierung in deiner asynchronen Python-Anwendung erfordert keine Architekturänderungen, kein Refactoring und keine komplexe Konfiguration. Sie ist genau zwei Zeilen Code lang. Heute sprechen wir darüber, uvloop als Drop-in zu verwenden. Der Event Loop der asyncio Standard Library ist in purem Python geschrieben. Uvloop ist ein Drop-in Replacement, das auf libuv aufbaut, derselben Engine, die auch andere High-Concurrency Runtimes antreibt. Dadurch wird dein Standard-Python-Async-Code deutlich schneller ausgeführt. Um den Core Scheduling Mechanismus deiner Anwendung zu ersetzen, musst du Python anweisen, sein Default-Verhalten aufzugeben, bevor es mit der eigentlichen Arbeit beginnt. In einem Webserver Entry Point Script, wie der Main-Datei für eine FastAPI- oder aiohttp-Anwendung, implementierst du dieses Replacement mit einer Event Loop Policy. Eine Event Loop Policy ist ein globales Configuration Object innerhalb des Standard-asyncio-Moduls. Sie legt fest, welche Art von Event Loop instanziiert wird, wann immer die Anwendung einen neuen anfordert. Um den Loop auszutauschen, importierst du das asyncio-Modul und das uvloop-Modul. Dann rufst du die Funktion set event loop policy auf dem asyncio-Modul auf. Du übergibst ihr eine frische Instanz der Event Loop Policy, die vom uvloop-Modul bereitgestellt wird. Hier ist der entscheidende Punkt. Du musst diese Policy frühzeitig setzen. Der Aufruf muss ganz oben in deinem Main Execution Script stehen, direkt nach deinen Imports. Die Event Loop Policy beeinflusst nur die Erstellung neuer Loops. Wenn du mit dem Setzen der Policy wartest, bis dein Web-Framework bereits gestartet ist oder ein asynchroner Database Driver initialisiert wurde, läuft wahrscheinlich bereits der Standard Pure-Python-Loop. Die Policy zu diesem Zeitpunkt zu ändern, hat keine Auswirkungen auf den bestehenden Loop. Dein Code wird uvloop entweder komplett ignorieren, oder du endest mit gemischten Event Loops, die Deadlocks und Broken Connections verursachen. Es gibt eine Alternative zum Policy-Ansatz. Anstatt die globalen Regeln für die Loop-Erstellung zu ändern, kannst du explizit eine einzelne uvloop-Instanz erstellen. Das machst du, indem du die Funktion new event loop direkt aus dem uvloop-Modul aufrufst. Sobald du dieses Loop-Objekt im Memory hast, übergibst du es an asyncio, indem du die Funktion set event loop aufrufst. Warum solltest du dich für den einen oder den anderen Ansatz entscheiden? Das Setzen der Event Loop Policy ist ein globaler Override. Es garantiert, dass jede Third-Party Library, jeder Background Task oder jede Framework-Komponente in deinem Prozess, die asyncio nach einem neuen Loop fragt, sicher einen uvloop erhält. Es ist die Standardwahl für einen Webserver, bei dem du eine einheitliche Performance über den gesamten Application Stack hinweg haben möchtest. Der explizite new event loop Ansatz ist eng gefasst. Er injiziert eine spezifische Instanz, anstatt die Factory-Regeln zu ändern. Du verwendest diese explizite Methode, wenn du komplexe Environments mit mehreren Threads verwaltest, oder wenn du strikte Kontrolle darüber brauchst, welcher Loop genau in einem isolierten Context läuft, ohne den globalen Process State zu mutieren. Für Standard-Web-Applications ist der Policy Override alles, was du brauchst. Der genaue Mechanismus, den du wählst, um uvloop als Drop-in zu nutzen, ist weniger wichtig als das Timing, wann du ihn anwendest. Die Event Loop Policy diktiert das Fundament deiner gesamten Async-Architektur, daher muss sie die allererste Instruction sein, die deine Anwendung ausführt, bevor jemals ein asynchroner Context etabliert wird. Das war's für diese Folge. Danke fürs Zuhören, und keep building!
3

Einführung in asyncpg: Das binäre Protokoll

3m 57s

Erkunde das grundlegende Design von asyncpg. Wir diskutieren, warum die Umgehung der Standard-DB-API zugunsten des binären Protokolls von PostgreSQL massive Leistungssteigerungen bringt.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 3 von 17. Du optimierst deine Indizes, upgradest dein Netzwerk und tunest deine Queries, aber deine Application verbrennt immer noch CPU-Zyklen bei der Datenbankkommunikation. Der größte Bottleneck bei deinen Datenbank-Queries ist oft nicht die Network Latency. Es ist die Zeit, die deine Application mit dem Parsen von Text verbringt. Um diesen Overhead zu eliminieren, stellen wir asyncpg und seine Implementierung des PostgreSQL Binary Protocols vor. Es ist ein weit verbreiteter Irrtum, dass asyncpg nur ein asynchroner Wrapper um psycopg2 ist. Ist es aber nicht. Es ist auch keine Adaption der Standard-Python-DB-API. Die DB-API-Specification lenkt Datenbanktreiber von Natur aus in Richtung bestimmter standardisierter Data-Handling-Patterns. asyncpg ignoriert diese Specification komplett. Es ist ein Ground-up-Rewrite, der exklusiv für asyncio und PostgreSQL designt wurde. Er umgeht Standard-Datenbank-Interfaces, um mit Postgres zu dessen eigenen Bedingungen zu kommunizieren. Um zu verstehen, warum dieses Design so wichtig ist, schau dir an, wie traditionelle Treiber den Data Transfer handhaben. Die meisten Datenbanktreiber kommunizieren mit PostgreSQL über ein textbasiertes Format. Wenn du die Datenbank nach einer Zahl, einem Timestamp oder einem komplexen Array abfragst, nimmt die Datenbank ihre interne Memory-Representation dieser Daten und konvertiert sie in einen String. Dann sendet sie diesen String über das Netzwerk. Wenn deine Python-Application ihn empfängt, muss der Treiber diesen Text-String wieder in einen Python-Integer, ein Datetime-Object oder eine List parsen. Stell dir diesen traditionellen Ansatz wie ein Team vor, das sich bei jeder internen Unterhaltung auf einen Übersetzer verlässt. Die Datenbank liest ihre nativen Datenstrukturen, schreibt sie als standardisierte Textdokumente raus und sendet sie über das Netzwerk. Deine Python-Application empfängt diese Dokumente und übersetzt den Text mühsam wieder zurück in ihre eigenen strukturierten Memory-Objects. All dieses Encoding, Stringifying und Parsing verbrennt CPU-Zeit und verbraucht Memory. asyncpg löst dieses Problem, indem es das PostgreSQL Frontend und Backend Binary Protocol direkt spricht. Es zwingt die Datenbank, exklusiv binären Input und Output zu verwenden. Anstatt sich auf einen Übersetzer zu verlassen, sprechen die Datenbank und der Treiber exakt dieselbe native Sprache. Wenn du einen 64-Bit-Integer abfragst, sendet PostgreSQL die Raw-Bytes, die diesen Integer repräsentieren. asyncpg liest diese Bytes direkt in ein Python-Integer-Object. Es gibt kein String-Formatting. Es gibt kein Text-Parsing. Dieses native Verständnis erstreckt sich auch auf komplexe Daten. Wenn du einen JSON-Block, einen Universally Unique Identifier oder einen geometrischen Datentyp anfragst, stellt das Binary Protocol sicher, dass die Payload kompakt und strikt strukturiert bleibt. Der Treiber weiß genau, wie viele Bytes er für jede Column lesen muss, ohne jemals nach Text-Delimitern scannen zu müssen. Hier ist die entscheidende Erkenntnis. Die Geschwindigkeit von asyncpg kommt nicht primär von der Non-Blocking-Natur von Python asyncio. Die massiven Performance-Gains kommen daher, dass der Text-Translation-Layer komplett entfernt wurde. Du machst signifikant weniger Arbeit pro zurückgegebener Row. Indem der binäre Data Transfer strikt erzwungen wird, hört deine Application auf, Ressourcen beim Lesen von Text zu verschwenden, und verbringt diese CPU-Zeit damit, deine eigentliche Business Logic auszuführen. Das war's für diese Folge. Danke fürs Zuhören und keep building!
4

Verbindungsaufbau und einfache Ausführung

3m 54s

Mache deine ersten Schritte mit asyncpg, indem du dich mit einer Datenbank verbindest und grundlegende Abfragen ausführst. Verstehe die native Postgres-Argument-Syntax.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 4 von 17. Wenn du die Standard-Datenbank-Driver von Python gewohnt bist, wird dich die Art und Weise, wie du Variablen an Queries übergibst, ziemlich überraschen, und dein Code wird sofort crashen. Standard-Python-Driver verlassen sich auf String-Formatting-Marker, aber diese Library kommuniziert direkt mit der Database-Engine. Heute behandeln wir Connecting und Basic Execution. Um mit deiner Datenbank zu kommunizieren, nutzt du die connect-Funktion von asyncpg. Du awaitest diese Funktion und übergibst ihr einen Data Source Name, was eine Standard-Postgres-Connection-URI ist. Das sieht genau wie eine Webadresse aus. Es beginnt mit postgresql Doppelpunkt Slash Slash, gefolgt von deinem Username, einem Doppelpunkt, deinem Passwort, einem At-Zeichen, der Host-Adresse und schließlich einem Slash mit dem Datenbanknamen. Das Awaiten dieser Funktion stellt den Network-Link her und gibt dir ein aktives Connection-Objekt. Jetzt willst du einen Username und ein Geburtsdatum in eine Tabelle einfügen. Hier ändert sich die Query-Syntax. Benutze kein Prozent S oder Fragezeichen für deine Query-Parameter. Weil asyncpg die Standard-Python-Datenbank-API bewusst umgeht, zwingt es dich, native Postgres-Placeholder zu verwenden. Du schreibst Dollarzeichen eins, Dollarzeichen zwei und so weiter. Dein Query-String sieht dann aus wie ein Standard-Insert-Statement, aber die Values sind Dollar eins und Dollar zwei. Um diese Query auszuführen, ohne Daten zurückzuverlangen, awaitest du die execute-Methode auf deinem Connection-Objekt. Du übergibst zuerst den Query-String, gefolgt von den eigentlichen Variablen für den Namen und das Geburtsdatum. Die execute-Methode führt das Statement aus und verwirft alle tabellarischen Daten. Sie gibt einfach einen Status-String von Postgres zurück, so etwas wie insert null eins. Sie gibt nicht die eigentlichen Datenbank-Rows zurück. Wenn die Datenbank eine eindeutige ID für diesen neuen User generieren soll und du diese ID in Python zurückbrauchst, ist execute das falsche Tool. Du änderst deine SQL-Query und fügst am Ende eine returning id Clause hinzu. Weil du jetzt Daten zurückerwartest, nutzt du die fetchval-Methode. Die fetchval-Methode führt die Query aus und gibt genau einen spezifischen Value zurück. Sie schaut sich die erste zurückgegebene Row an, schnappt sich die erste Column und gibt dir genau diesen Wert. Das ist perfekt, um eine neu generierte User-ID abzugreifen. Wenn du mehr als nur die ID brauchst, vielleicht willst du die Database-Defaults für mehrere Columns, nutzt du stattdessen fetchrow. Das Awaiten von fetchrow gibt ein einzelnes Record-Objekt zurück, das alle Columns aus dieser ersten Row enthält. Du kannst auf die Daten in diesem Record genau wie bei einem Python-Dictionary zugreifen, indem du die Column-Namen als Keys verwendest. Wenn du mit dem Einfügen deiner Daten fertig bist, musst du die close-Methode auf dem Connection-Objekt awaiten, um den Network-Socket aufzuräumen und die Datenbank-Ressourcen freizugeben. Hier ist die wichtigste Erkenntnis. Dass du gezwungen wirst, native Dollarzeichen-Placeholder zu verwenden, ist nicht nur eine stilistische Eigenart. Es erlaubt asyncpg, die clientseitige String-Interpolation komplett zu umgehen und Python-Typen direkt auf binäre Postgres-Formate zu mappen – für maximale Geschwindigkeit und kompletten Schutz vor SQL-Injection. Danke fürs Zuhören, Happy Coding zusammen!
5

Native Typkonvertierung

3m 48s

Entdecke, wie asyncpg PostgreSQL-Datentypen automatisch auf native Python-Objekte abbildet und so komplexes ORM-Parsing überflüssig macht.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 5 von 17. Du schreibst eine raw SQL Query, führst sie aus und machst dich darauf gefasst, Date Strings manuell zu parsen, comma-separated Arrays zu splitten und Währungswerte zu casten. Aber das musst du nicht. Du brauchst kein ORM, um fully typed Python-Objekte aus deiner Datenbank zu bekommen. Asyncpg erledigt das automatisch durch native Type Conversion. Wenn asyncpg mit PostgreSQL kommuniziert, nutzt es das Database Binary Protocol. Es weiß genau, welchen Datentyp jede Spalte repräsentiert. Anstatt dir raw Text Strings zu liefern, die in Python noch geparst werden müssen, übersetzt asyncpg PostgreSQL-Typen direkt in Standard Python Library Objekte. Diese Übersetzung passiert automatisch in beide Richtungen. Wenn du ein Python-Objekt als Query Parameter übergibst, encodiert asyncpg es in das richtige PostgreSQL Binary Format. Wenn die Datenbank antwortet, decodiert asyncpg die binären Daten zurück in den entsprechenden Python-Typ. Stell dir ein Szenario vor, in dem du ein User Profile aus deiner Datenbank abrufst. Deine SQL Query fragt nach einem Username, einem Array von User Tags, der Account Creation Time und der letzten bekannten IP-Adresse. Bei vielen Database Drivern würdest du Strings zurückbekommen, die du manuell parsen musst. Mit asyncpg ist der Result Record bereits typisiert. Der Username ist ein Standard Python String. Die Tags-Spalte, die in PostgreSQL ein Array ist, kommt als native Python List von Strings an. Die Creation Time ist ein Standard datetime Objekt. Die IP-Adresse, die in der Datenbank als inet Typ gespeichert ist, mappt direkt auf built-in Python ipaddress Objekte. Du schreibst null Parsing-Logik, um das zu erreichen. Es gibt ein striktes Mapping für Zahlen, das manche Developer überrascht. Wenn deine PostgreSQL-Spalte als numeric definiert ist, wird sie nicht in einen Python Float konvertiert. Asyncpg mappt den PostgreSQL numeric Typ direkt auf die Python decimal dot Decimal Klasse. Das bewahrt die exakte Precision. Wenn du Finanzdaten oder präzise Messwerte abfragst, verlierst du keine Daten durch Floating-Point Rounding Errors. Standard Floating-Point-Typen in Postgres, wie real oder double precision, mappen hingegen auf Python Floats. Hier ist der entscheidende Punkt für andere spezifische Typen. Wenn du eine native UUID-Spalte abfragst, erhältst du ein Python uuid dot UUID Objekt, keine generische String Representation. Dates werden zu datetime dot date Objekten. Postgres Intervals mappen perfekt auf Python timedeltas. Binärdaten, die in einer bytea-Spalte gespeichert sind, werden direkt in Python bytes konvertiert. JSON- und JSONB-Spalten verhalten sich etwas anders. Asyncpg konvertiert JSON- und JSONB-Daten by default in Standard Python Strings. Es parst sie nicht automatisch in Python Dictionaries. Du erhältst den raw String, den du dann an das Standard Python json Modul übergeben kannst, wenn du die nested Data manipulieren musst. Sich auf diese Binary Type Translation zu verlassen, hält deine Application Logic clean und verlagert die Last der Type Safety auf den Database Driver, wo sie hingehört. Das war's für diese Folge. Danke fürs Zuhören und keep building!
6

Benutzerdefinierte Type Codecs

4m 11s

Lerne, benutzerdefinierte Datenkonvertierungen in asyncpg zu definieren. Diese Episode erklärt, wie du set_type_codec verwendest, um JSONB automatisch in Python-Dictionaries zu dekodieren.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 6 von 17. Du fragst einen User Record aus einer Datenbank ab und bekommst einen Raw String zurück. Jedes einzelne Mal schreibst du denselben Boilerplate-Code, um diesen String in ein Dictionary zu parsen, bevor du die Daten wirklich nutzen kannst. Du kannst deinem Database Driver beibringen, das Datenformat deiner Applikation nativ zu sprechen, wodurch du dir das manuelle Parsen bei jeder Query sparst. Das machst du mithilfe von Custom Type Codecs. Wenn du PostgreSQL mit dem Driver abfragst, werden grundlegende Typen wie Integer und Text automatisch übersetzt. Aber wenn du komplexe Datenbanktypen wie JSON verwendest, muss der Driver wissen, wie du diese Daten in Python repräsentiert haben möchtest. Ein Custom Type Codec fungiert als Translation Layer. Er sitzt zwischen der Database Connection und deiner Application Logic. Um das zu konfigurieren, nutzt du eine Methode namens set type codec. Diese Methode rufst du direkt auf einem aktiven Connection-Objekt auf. Sie braucht vier wichtige Informationen. Erstens gibst du den Namen des Datenbanktyps an, zum Beispiel den String jsonb. Zweitens gibst du das Schema an, in dem dieser Typ liegt. Für built-in PostgreSQL-Typen ist das das pg catalog Schema. Als Nächstes stellst du die Translation Logic bereit, indem du eine Encoder-Funktion und eine Decoder-Funktion übergibst. Der Encoder definiert, wie Python Daten an die Datenbank sendet. Er nimmt dein Python-Objekt und gibt ein Format zurück, das PostgreSQL versteht, typischerweise einen String. Wenn du mit JSON arbeitest, kannst du einfach die json dumps Funktion der Standard Library übergeben. Der Decoder definiert, wie Daten aus der Datenbank zurückkommen. Er empfängt den Raw String von PostgreSQL und gibt dein gewünschtes Python-Objekt zurück. Für JSON übergibst du einfach die json loads Funktion. Stell dir ein System vor, das unstrukturierte User Preferences speichert. In deiner Datenbank ist die preferences Spalte als jsonb definiert. In deiner Python-Applikation behandelst du die Preferences als Standard-Dictionary. Sobald dein Codec konfiguriert ist, führst du eine einfache Select Query für einen User aus. Die preferences Spalte kommt bereits als Python-Dictionary strukturiert in deiner Applikation an. Wenn du eine Insert Query ausführst, übergibst du ein Dictionary direkt als Query-Argument. Der Driver triggert automatisch deinen Encoder, konvertiert das Dictionary zu JSON und sendet es an die Datenbank. Du rufst einen Encoder oder Decoder niemals manuell in deinem Query Execution Code auf. Hier ist der entscheidende Punkt. Custom Type Codecs gelten nicht global für den gesamten Database Driver. Die set type codec Methode ändert nur die spezifische Connection, auf der sie aufgerufen wird. Wenn du einen Codec auf einer Connection konfigurierst, weiß eine zweite Connection nichts davon und gibt wieder Raw Strings zurück. Dieses Verhalten führt häufig zu Problemen, wenn Entwickler einen Connection Pool einführen. Du kannst einen Pool nicht mit einem einzigen Aufruf der Codec-Methode konfigurieren. Stattdessen musst du deinen Custom Codec jedes Mal registrieren, wenn eine neue Connection aufgebaut wird. Das erreichst du durch die Definition einer Initialisierungsfunktion. Innerhalb dieser Funktion nimmst du das neue Connection-Objekt entgegen und rufst set type codec darauf auf. Anschließend übergibst du diese Initialisierungsfunktion an deine Pool Creation Logic. Der Pool führt deine Funktion automatisch aus, sobald er eine neue Connection öffnet, wodurch garantiert wird, dass deine Codecs immer vorhanden und aktiv sind. Durch die Verlagerung der Data Serialization auf den Driver Layer mithilfe von Custom Type Codecs entfällt die wiederholte Parsing Logic, und es wird sichergestellt, dass deine Datenformate in der gesamten Applikation perfekt synchronisiert bleiben. Danke fürs Zuhören, Happy Coding zusammen!
7

Fortgeschrittene Codecs mit PostGIS

4m 05s

Tauche tief in benutzerdefinierte Type Codecs ein, indem du die PostGIS-Geometrietypen von PostgreSQL über das binäre Format auf Python Shapely-Objekte abbildest.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 7 von 17. Die Verarbeitung geografischer Koordinaten bedeutet meistens umständliches String-Parsing. Du schreibst eine Query, bekommst einen riesigen Text-String aus Zahlen zurück und verbrennst dann CPU-Zyklen, um ihn zu zerlegen, nur um Latitude und Longitude zu finden. Advanced Codecs mit PostGIS in asyncpg lösen dieses Problem komplett. Wenn du Custom Types aus PostgreSQL ziehst, hast du es mit Codecs zu tun. Ein Codec sagt asyncpg, wie ein PostgreSQL-Datentyp in ein Python-Objekt übersetzt werden soll. Oft gibt es hier Verwirrung zwischen dem Textformat und dem Binärformat. Das Textformat ist bei vielen Datenbank-Tools der Default. Mit PostGIS liefert eine Text-Query Well-Known Text zurück. Das sieht aus wie das Wort POINT, gefolgt von den Koordinaten in Klammern. Das ist zwar menschenlesbar, aber um es im Code zu lesen, musst du Strings allozieren, nach Klammern suchen und Characters in Floats casten. Text zu parsen ist langsam und skaliert schlecht, wenn du tausende von Rows verarbeitest. Du willst das Binärformat. PostGIS nutzt einen Standard namens Well-Known Binary. Wenn du deinen Codec in asyncpg konfigurierst, setzt du das format-Argument explizit auf binary. Die Datenbank überspringt die Textgenerierung und übergibt Raw Bytes. Jetzt brauchst du einen Weg, diese Bytes in etwas zu übersetzen, das deine Python-Applikation auch wirklich nutzen kann. Hier kommt eine Python-Library wie Shapely ins Spiel. Shapely verarbeitet komplexe Geometrien und weiß bereits ganz genau, wie man Well-Known Binary liest. Du sagst asyncpg, dass es einen Custom Type Codec nutzen soll, indem du die Methode set type codec direkt auf deiner Datenbankverbindung aufrufst. Du gibst den PostgreSQL-Typnamen an, und zwar geometry. Dann übergibst du eine Encoder-Funktion und eine Decoder-Funktion. Der Decoder nimmt den Raw Byte String von PostgreSQL und reicht ihn direkt an den Binary Reader von Shapely weiter. Stell dir vor, du fragst die Location des Empire State Buildings ab. Ohne einen Custom Binary Codec gibt deine Datenbank einen String zurück, deine Applikation parst ihn, baut ein Dictionary und erstellt am Ende ein Geometry-Objekt. Mit dem Binary Codec führst du ein Standard-Select-Statement aus. Asyncpg fängt die Binärdaten ab, führt deine Decoder-Funktion aus und übergibt dir sofort ein fertig geformtes Shapely Point Objekt. Du kannst sofort auf die x- und y-Koordinaten des zurückgegebenen Objekts zugreifen. Der Prozess funktioniert auch umgekehrt für Daten, die zurück in die Datenbank gehen. Deine Encoder-Funktion bereitet Python-Daten darauf vor, an PostgreSQL gesendet zu werden. Shapely-Objekte implementieren einen Standard namens geo interface. Das ist eine gängige Python-Dictionary-Struktur, die für Geometrien genutzt wird. Dein Encoder nimmt jedes Python-Objekt, das dieses Interface unterstützt, nutzt Shapely, um es als Well-Known Binary zu serialisieren, und sendet diese Raw Bytes zurück an die Datenbank. Du kommst nie mit einer Text-Repräsentation in Berührung. Wenn du diese Deep Dives nützlich findest, kannst du die Show unterstützen, indem du auf Patreon nach DevStoriesEU suchst. Hier ist die wichtigste Erkenntnis. Indem du strikt das Binärformat für Custom Type Codecs nutzt, eliminierst du den Serialisierungs-Bottleneck und ermöglichst es deiner Datenbank und deiner Python-Applikation, in Memory-Geschwindigkeit zu kommunizieren. Danke fürs Zuhören, Happy Coding zusammen!
8

Transaktionen verwalten

3m 42s

Meistere Datenbanktransaktionen in asyncpg. Wir behandeln das Auto-Commit-Verhalten und wie man mehrere Abfragen mithilfe asynchroner Context Manager sicher ausführt.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 8 von 17. Du führst eine Update-Query aus, und dann stürzt dein Skript ab, bevor die nächste Query läuft. Du checkst die Datenbank und erwartest, dass sich nichts geändert hat, aber das erste Update ist genau da, dauerhaft gespeichert. In asyncpg wird jede einzelne Query in der Millisekunde committed, in der sie fertig ist, wenn du nicht explizit eine Transaktion anforderst. Mit Transaction Management fixt du dieses Verhalten. Standardmäßig arbeitet asyncpg im Auto-Commit-Modus. Das bedeutet: Wenn du Code schreibst, der drei Queries nacheinander ausführt, führst du nicht einen einzigen Logikblock aus. Du führst drei komplett isolierte Operationen aus. Wenn die zweite Query fehlschlägt, ist die erste Query in der Datenbank bereits finalisiert. Ein fehlender expliziter Transaction-Block ist eine häufige Ursache für einen korrupten Application State. Stell dir ein Szenario vor, in dem du Geld zwischen zwei Bankkonten überweist. Du musst Geld von Konto A abbuchen und dann genau diesen Betrag Konto B hinzufügen. Beide Updates müssen zusammen erfolgreich sein, oder beide müssen zusammen fehlschlagen. Wenn die Abbuchung erfolgreich ist, aber das Hinzufügen fehlschlägt, verschwindet das Geld einfach. Um diese Operationen miteinander zu verbinden, nutzt du die transaction-Methode auf deinem Connection-Objekt. Diese Methode gibt einen asynchronen Context Manager zurück. In deinem Code schreibst du async with connection dot transaction, gefolgt von einem Doppelpunkt, und dann rückst du deine zusammengehörigen Queries ein. Wenn Python diesen Block betritt, sagt asyncpg PostgreSQL, dass es eine neue Transaktion starten soll. Innerhalb des Blocks führst du die Abbuchungs-Query aus, gefolgt von der Additions-Query. Wenn beide Queries ohne Probleme durchlaufen und Python das Ende des Blocks erreicht, setzt asyncpg automatisch einen Commit-Befehl ab. Die Änderungen an beiden Konten werden für den Rest der Datenbank im exakt selben Moment sichtbar. Hier ist die entscheidende Erkenntnis. Wenn innerhalb dieses Blocks irgendein Problem auftritt, bleibt die Datenbank sicher. Das Problem könnte eine Database Constraint Violation, ein Network Timeout oder sogar ein reiner Python-Fehler wie eine fehlende Variable oder eine Division durch Null sein. Wenn eine Exception geworfen wird, fängt der Context Manager sie ab. Er sendet automatisch einen Rollback-Befehl an PostgreSQL, macht die Abbuchung von Konto A rückgängig und lässt die Python-Exception dann weiter deinen Call Stack aufsteigen. Du kannst diese Blöcke auch sicher verschachteln. Wenn du einen neuen Transaction Context Manager öffnest, während du bereits in einem aktiven Transaction-Block bist, kommt asyncpg nicht durcheinander. Stattdessen erstellt es automatisch einen Database Savepoint. Ein Savepoint funktioniert wie ein Lesezeichen innerhalb einer laufenden Transaktion. Wenn der innere Block auf einen Fehler stößt, rollt er den Database State nur bis zu diesem Lesezeichen zurück. Der äußere Block bleibt komplett intakt und kann seine eigene Arbeit immer noch committen, oder sich basierend auf deiner Logik entscheiden, fehlzuschlagen. Du musst keine manuellen Savepoint-Befehle schreiben, du verschachtelst einfach deine async with Blöcke. Letztendlich bindet der Transaction Context Manager deinen Database State dauerhaft an deinen Python Execution State und stellt so sicher, dass eine unbehandelte Python-Exception eine absolute Garantie gegen partielle Database Updates ist. Das war es für diese Folge. Danke fürs Zuhören und viel Spaß beim Entwickeln!
9

Connection Pooling

3m 45s

Skaliere deine Anwendung mit dem integrierten Connection Pooling von asyncpg. Lerne, wie du Datenbankverbindungen in Webdiensten mit hohem Traffic effizient verwaltest.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 9 von 17. Für jeden eingehenden Web Request eine neue Database Connection zu öffnen, ist ein extrem effektiver Weg, deinen Server komplett zum Absturz zu bringen. Allein der Network Overhead wird deine Application lahmlegen, und du wirst schnell das Connection Limit deiner Datenbank ausschöpfen. Die Lösung dafür ist Connection Pooling. Wenn du eine Library wie asyncpg benutzt, um mit PostgreSQL zu kommunizieren, ist der Aufbau einer reinen Connection eine teure Operation. Das erfordert einen TCP Handshake, Secure Negotiation und Database Authentication. Wenn du einen High-Traffic Web Service betreibst, kannst du es dir schlichtweg nicht leisten, diese Latency Tax bei jedem einzelnen HTTP Request zu zahlen. Stattdessen brauchst du einen ständigen Vorrat an sofort einsatzbereiten Connections. In asyncpg erreichst du das mit der create pool Funktion. Normalerweise rufst du diese Funktion einmal während der Startup-Phase deiner Application auf. Du übergibst deine Database Credentials, Host und Port, und asyncpg fährt im Hintergrund ein Set von Idle Connections hoch. Ab diesem Zeitpunkt erstellen deine Route Handler und Background Tasks nie wieder eine neue Connection von Grund auf. Sie leihen sich nur noch bestehende aus. Hier gibt es eine häufige Falle, in die viele Developer tappen. Verwechsle das Ausleihen einer Connection nicht mit dem Öffnen einer Database Transaction. Das sind völlig separate Operationen. Wenn du dir eine Connection aus dem Pool leihst, reservierst du den Network Socket lediglich für deine exklusive, temporäre Nutzung. Wenn deine Operation Atomarität über mehrere Queries hinweg erfordert, musst du auf dieser spezifischen, ausgeliehenen Connection immer noch explizit eine Transaction starten. Denk mal an einen High-Traffic FastAPI oder aiohttp Web Service. Sagen wir, du hast einen Endpoint, der einen Integer akzeptiert, die Datenbank abfragt, um die Zweierpotenz für diese Zahl zu berechnen, und das Result zurückgibt. Wenn ein Request deinen Endpoint trifft, rufst du die acquire Methode auf deinem Pool Object auf. Das machst du mit einem asynchronen Context Manager. Dadurch wird temporär eine Connection aus dem Pool ausgecheckt. Du nutzt dann genau diese Connection Instance, um deine Database Query zur Berechnung der Zweierpotenz auszuführen. Sobald der Code Block durchgelaufen ist, gibt der Context Manager die Connection automatisch wieder frei. Er bereinigt ihren State und gibt sie an den Pool zurück, sodass sie sofort für den nächsten eingehenden HTTP Request zur Verfügung steht. Wenn ein plötzlicher Traffic Spike deinen Webserver trifft und alle Connections im Pool gerade ausgecheckt sind, stürzt der nächste Request nicht ab. Er wartet. Der acquire Call pausiert die Execution einfach, bis ein anderer Request fertig ist und seine Connection zurückgibt. Hier ist die entscheidende Erkenntnis. Der Connection Pool spart nicht nur Zeit bei Network Handshakes. Er fungiert als strikte, zuverlässige Concurrency Throttle. Er schützt deine PostgreSQL Datenbank davor, von einer unerwarteten Traffic-Flut überlastet zu werden. Wenn du deinen Pool so konfigurierst, dass er genau zwanzig Connections hält, wird die Datenbank niemals mehr als zwanzig gleichzeitig aktive Queries von dieser Application Instance sehen, egal wie viele Tausende von gleichzeitigen Requests deinen Webserver treffen. Danke fürs Einschalten. Bis zum nächsten Mal!
10

Prepared Statement Caching

4m 18s

Verstehe, wie asyncpg das Query-Parsing mit automatischen Prepared Statements optimiert und warum externe Pooler wie PgBouncer Konflikte verursachen können.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 10 von 17. Deine Application läuft auf deiner lokalen Maschine perfekt. Aber in dem Moment, in dem du sie in Production hinter einem Database Connection Router deployst, siehst du plötzlich random Abstürze. Die Logs beschweren sich über Statements, die bereits existieren, oder über Statements, die nicht gefunden werden können. An deinem Code hat sich nichts geändert. Das passiert wegen einer eingebauten Optimierung namens Prepared Statement Caching. Um den Error zu verstehen, müssen wir uns zuerst ansehen, was asyncpg behind the scenes macht. Jedes Mal, wenn du eine Query über asyncpg sendest, übersetzt die Library sie automatisch in ein Prepared Statement auf dem PostgreSQL-Server. Normalerweise muss eine Datenbank, wenn sie eine Query empfängt, den Text parsen, die Syntax analysieren und einen Execution Plan bauen. Das kostet Zeit. Indem das Statement prepared wird, übernimmt PostgreSQL dieses Heavy Lifting genau einmal und weist ihm einen internen Namen zu. Für alle zukünftigen Executions genau dieser Query sendet asyncpg nur noch die neuen Parameter und den Statement-Namen. Das überspringt die Parsing-Phase komplett und sorgt für einen massiven Performance-Boost. asyncpg hält einen Cache dieser Prepared Statements im Memory, eng gebunden an die aktive Database Connection. Hier ist die entscheidende Erkenntnis. Das Problem entsteht durch einen Konflikt zwischen der Art, wie asyncpg seine internen Connections managt, und wie externe Pooler wie PgBouncer arbeiten. asyncpg geht davon aus, dass es eine dedizierte, persistente physische Connection zum Postgres-Server hat. Wenn es ein Prepared Statement erstellt, vertraut es darauf, dass das Statement auf genau dieser Connection verfügbar bleibt, bis die Connection geschlossen wird. Jetzt bringen wir PgBouncer in die Architektur, speziell im Transaction Mode. PgBouncer sitzt zwischen deiner Application und der Datenbank. Er hält einen kleinen Pool von echten Postgres Connections und teilt sie zwischen Tausenden von eingehenden Client Requests auf. Im Transaction Mode gibt PgBouncer deiner Application eine physische Database Connection nur für die Dauer einer einzigen Transaction. In dem Moment, in dem diese Transaction committet, nimmt PgBouncer die physische Connection zurück und gibt sie einem völlig anderen Client. Das macht den Prepared Statement Cache kaputt. Deine Application sendet eine Query. asyncpg prepared sie und cached sie auf der physischen Connection A. Die Transaction endet. Ein paar Sekunden später sendet deine Application exakt dieselbe Query. asyncpg erinnert sich, dass es diese Query bereits prepared hat, also sagt es Postgres, dass es das gespeicherte Statement ausführen soll. Aber dieses Mal hat PgBouncer deine Application an die physische Connection B geroutet. Connection B weiß nichts von diesem Prepared Statement. Die Datenbank wirft einen Error, der besagt, dass das Statement nicht existiert. Das Umgekehrte gilt genauso. Ein anderer Client könnte an Connection A geroutet werden, versuchen, ein Statement mit demselben internen Namen zu preparen, und einen Error triggern, der besagt, dass das Statement bereits existiert. Der Fix ist einfach, erfordert aber einen Trade-off. Du musst asyncpg sagen, dass es diese Optimierung deaktivieren soll. Wenn du deine asyncpg Connection oder deinen Connection Pool initialisierst, übergibst du ein bestimmtes Argument, das die Statement Cache Size auf null setzt. Das schaltet das automatische Prepared Statement Caching komplett ab. Deine Queries werden jetzt jedes einzelne Mal, wenn sie laufen, von PostgreSQL geparsed. Du opferst zwar ein bisschen Parsing-Performance, aber deine Application wird über verteilte Connections hinweg sofort stabil. Wenn deine Database Connections dynamisch pro Transaction geroutet werden, kann deine Application nicht mehr davon ausgehen, dass der Database Server sich zwischen den Queries überhaupt noch etwas merkt. Das war’s für diese Folge. Danke fürs Zuhören und keep building!
11

Postgres-Arrays und IN-Klauseln

4m 03s

Löse den häufigsten Syntaxfehler bei der Migration zu asyncpg. Lerne, wie du Abfragen mithilfe von ANY() korrekt nach einer Liste von Werten filterst.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 11 von 17. Du schreibst eine Standard-SQL-Query, um Datensätze zu filtern. Du übergibst eine Liste von Werten an deine Query-Parameter, genau wie du es schon bei dutzenden anderen Datenbank-Libraries gemacht hast. Postgres wirft sofort einen Syntax Error. Das Problem sind nicht deine Daten. Das Problem ist, wie du Postgres Arrays und IN Clauses handlest. Das ist der Syntax Error Nummer eins für Entwickler, die von anderen Datenbank-Interfaces zu asyncpg migrieren. In vielen älteren Libraries fängt der Driver deinen Query String ab. Wenn du eine Python-Liste an eine IN Clause übergibst, schreibt die Library den SQL String on the fly um. Sie expandiert deine Liste in einen kommagetrennten String aus einzelnen Parametern, bevor sie ihn an die Datenbank sendet. Asyncpg macht das nicht. Es verlässt sich komplett auf native, serverseitige Prepared Statements von Postgres. Hier ist die entscheidende Erkenntnis. Im Standard Postgres SQL erfordert der IN Operator eine kommagetrennte Liste von skalaren Werten in Klammern. Er akzeptiert kein einzelnes Array-Objekt. Wenn du eine Python-Liste als Parameter an asyncpg übergibst, mappt asyncpg diese Liste direkt auf ein natives Postgres Array. Deine Query wird am Ende als Expression IN einem Array-Objekt evaluiert, was eine ungültige Syntax ist. Postgres erwartet eine Expression IN Wert eins, Wert zwei. Um das zu fixen, musst du aufhören, den IN Operator für parametrisierte Listen zu verwenden. Nutze stattdessen die Postgres any Funktion. Die Logik ändert sich: Anstatt zu fragen, ob ein Wert IN einer Liste ist, fragst du, ob ein Wert irgendeinem Item in einem Array entspricht. Der any Operator ist speziell dafür designt, mit Postgres Array Types zu funktionieren. Er evaluiert den Wert auf der linken Seite, checkt das Array auf der rechten Seite und gibt true zurück, wenn er einen Match findet. Du musst Postgres außerdem mitteilen, was für ein Array es empfängt, indem du den Parameter castest. Wenn du ein Array aus Text Strings erwartest, castest du deinen Parameter explizit zu einem Text Array. Dieses explizite Type Casting garantiert, dass Postgres genau weiß, wie es die Query planen und ausführen soll, ohne den zugrundeliegenden Data Type des eingehenden Binary Streams erraten zu müssen. Stell dir ein Szenario vor, in dem du eine Liste von Produkten filterst. Du möchtest die Produktkategorie mit einer dynamisch bereitgestellten Liste von Kategorien matchen, die der User ausgewählt hat. Du schreibst eine Query, um Produkte zu selecten, bei denen die Kategorie gleich any Parameter eins ist, und du castest Parameter eins zu einem Text Array. In deinem Python Code rufst du deine Datenbank Fetch-Methode auf. Du übergibst den Query String als erstes Argument und deine Python-Liste von Strings – wie Elektronik und Bücher – als zweites Argument. Asyncpg verpackt deine Python-Liste in ein binäres Postgres Text Array und sendet es übers Netzwerk als einen einzelnen Parameter. Postgres empfängt die Query, sieht das Text Array und matcht die Kategorien effizient mit der any Funktion. Dieser Ansatz ist strikt besser als String Manipulation. Weil sich die Query-Struktur nie ändert, egal wie viele Kategorien in der Liste sind, parst und plant Postgres das Statement genau einmal. Die Datenbank cached den Query Plan, was Execution Time bei nachfolgenden Calls spart. Du eliminierst auch das Risiko von SQL Injection, da die Daten in binärer Form übertragen werden, komplett getrennt vom Query Text. Wenn du eine Python-Liste an einen Datenbank-Parameter übergibst, behandle sie als natives Array, caste sie zum korrekten Type und evaluiere sie mit der any Funktion. Das war's für diese Folge. Danke fürs Zuhören und keep building!
12

Record-Objekte vs. Named Tuples

4m 33s

Erkunde das einzigartige Design von asyncpg Record-Objekten. Verstehe, warum die Dot-Notation standardmäßig weggelassen wird und wie du sie mit benutzerdefinierten Klassen aktivieren kannst.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 12 von 17. Du fragst deine Datenbank ab, bekommst eine Row zurück und tippst instinktiv den Variablennamen dot id. Sofort wirft Python einen AttributeError. Deine Daten sind zwar da, aber du kannst nicht wie erwartet darauf zugreifen. Das liegt an der bewussten Designentscheidung für Record Objects im Vergleich zu Named Tuples. Wenn du Daten mit asyncpg abrufst, werden keine Standard Python Dictionaries oder Named Tuples zurückgegeben. Stattdessen bekommst du ein hochoptimiertes Custom Object namens Record. Wenn du andere Datenbanktreiber oder Object-Relational Mapper gewohnt bist, erwartest du vielleicht, dass die Dot-Notation out of the box funktioniert. Bei einem asyncpg Record schlägt das jedoch absichtlich fehl. Du fragst dich vielleicht, warum der Treiber nicht einfach ein Standard Python Named Tuple verwendet, da Named Tuples die Dot-Notation nativ unterstützen. Der Grund ist reine Performance. Ein Named Tuple erfordert von Python die Generierung einer komplett neuen Class Structure für jede eindeutige Kombination von Columns, die von einer Query zurückgegeben wird. Wenn deine Application Hunderte von unterschiedlich strukturierten Queries ausführt, verursacht die Generierung dieser dynamischen Klassen einen massiven Execution Overhead. Die Library ist auf absolute Geschwindigkeit ausgelegt und umgeht diesen Flaschenhals vollständig, indem sie stattdessen ihren eigenen kompilierten Record Type zurückgibt. Dieses Custom Record Object funktioniert wie ein schneller Hybrid. Es unterstützt Integer Indexing genau wie ein Standard Tuple, das heißt, du kannst über Index null auf die erste Column zugreifen. Aber es bietet auch ein Dictionary-like Mapping. Du greifst über die Bracket-Notation auf deine Columns zu und übergibst den Column-Namen als String. Hier ist die entscheidende Erkenntnis. Die Entwickler haben die Dot-Notation für diese Objekte bewusst deaktiviert, um deine Application vor Namespace Clashes zu schützen. Denk an Standard Mapping Methods in Python wie keys, items, values oder get. Wenn deine Datenbanktabelle zufällig eine Column namens keys hat und der Treiber die Dot-Notation unterstützen würde, würde die Eingabe record dot keys einen strukturellen Konflikt verursachen. Python wüsste nicht, ob du den Datenbankwert oder die Built-in Method haben willst. Durch die Erzwingung der String-basierten Bracket-Notation garantiert der Treiber, dass deine Column-Namen niemals mit Standard Python Attributes kollidieren. Wenn du jedoch dein Datenbankschema vollständig kontrollierst, sicher weißt, dass du keine reservierten Python-Wörter als Column-Namen verwendest, und du die Dot-Notation für deine Codebase brauchst, gibt es einen Ausweg. Du kannst das Default Behavior überschreiben. Wenn du deine Database Connection herstellst, kannst du einen spezifischen Parameter namens record class übergeben. Um das zu implementieren, schreibst du eine Custom Class, die direkt vom Base asyncpg Record erbt. In dieser neuen Klasse implementierst du die Built-in Python Method namens Double Underscore getattr. Du weist diese Methode an, den angeforderten Attribute Name zu nehmen und ihn einfach über den sicheren Bracket-Notation Fallback nachzuschlagen. Sobald du diese Custom Class an dein Connection Setup übergibst, wird jede einzelne Row, die von deinen Queries zurückgegeben wird, eine Instanz deines Custom Objects sein. Python erlaubt dir dann die Verwendung der Dot-Notation und leitet den Attribute Request nahtlos über deine Custom Method weiter, um die zugrunde liegenden Column Data abzurufen. Letztendlich ist die strikte Bracket-Notation in einem Default Record kein Versehen, sondern eine Structural Safety Boundary, die sicherstellt, dass dein Data Access vorhersehbar bleibt, egal wie sich dein Datenbankschema ändert. Das war es für diese Folge. Vielen Dank fürs Zuhören und keep building!
13

Ergebnisse streamen mit Cursors

4m 59s

Verhindere Speichererschöpfung bei der Abfrage riesiger Datensätze. Lerne, wie du asyncpg Cursors verwendest, um Ergebnisse Stück für Stück zu streamen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 13 von 17. Du musst eine User-Tabelle mit einer Million Rows in eine Datei exportieren, also machst du einen Standard-Database-Fetch. Plötzlich frisst deine Python-Application den gesamten verfügbaren RAM auf und der Server stürzt ab. Du kannst nicht einfach riesige Datensätze auf einmal in den Memory ziehen, und genau deshalb verwenden wir Streaming Results mit Cursors. Normalerweise wird, wenn du eine Query in asyncpg ausführst, das gesamte Result-Set über das Netzwerk von PostgreSQL abgerufen und im Python-Memory gehalten. Die Datenbank baut die komplette Response, sendet sie, und asyncpg erzeugt Python-Objekte für jede einzelne Row, bevor dein Code die erste verarbeiten kann. Bei fünfzig Rows ist das völlig in Ordnung. Bei fünf Millionen Rows wird es jedoch sofort zum Problem. Um einen Serverabsturz zu vermeiden, musst du die Daten häppchenweise abrufen, anstatt alles auf einmal zu laden. Ein Database-Cursor gibt dir einen Pointer auf die Query-Results auf dem PostgreSQL-Server. Anstatt alles auf einmal abzurufen, ermöglicht der Cursor deiner Python-Application, die Daten inkrementell zu fetchen. In asyncpg machst du das, indem du die cursor-Methode auf deiner Connection aufrufst. Du übergibst deine SQL-Query und alle erforderlichen Argumente. Diese Methode gibt einen asynchronen Iterator zurück. Du schreibst eine async-for-Loop, um über diesen Cursor zu iterieren. Unter der Haube fetcht asyncpg automatisch Rows in kleinen, überschaubaren Batches aus PostgreSQL. Dein Code verarbeitet einige Rows, schreibt sie in deine Export-File und macht weiter. Python bereinigt die alten Rows aus dem Memory, wodurch der gesamte Memory-Footprint deiner Application komplett flach bleibt, egal wie groß die Tabelle ist. Hier gibt es eine strikte Regel, über die viele Entwickler stolpern. Wenn du versuchst, einen Cursor direkt auf einer Standard-Connection zu iterieren, wirft asyncpg sofort einen InterfaceError. Die Error-Message wird besagen, dass Cursors nicht außerhalb einer Transaction verwendet werden können. Hier ist die entscheidende Erkenntnis. PostgreSQL-Cursors sind strukturell an Database-Transactions gebunden. Wenn eine Transaction ein Commit oder Rollback ausführt, zerstört PostgreSQL alle aktiven Cursors, die damit verknüpft sind. Standardmäßig arbeitet asyncpg im Auto-Commit-Mode. Das bedeutet, dass jede einzelne Query, die du ausführst, in eine eigene, winzige, unsichtbare Transaction gewrappt ist, die sich in dem Moment schließt, in dem die Query beendet ist. Wenn asyncpg dir erlauben würde, einen Cursor im Auto-Commit-Mode zu öffnen, würde diese implizite Transaction sofort enden, und PostgreSQL würde deinen Cursor killen, bevor du auch nur eine einzige Row fetchen könntest. Damit Cursors funktionieren, musst du die Transaction-Boundary explizit managen. Das machst du, indem du einen asynchronen Context-Manager über die transaction-Methode auf deiner Connection öffnest. Sobald du sicher innerhalb dieses Transaction-Blocks bist, rufst du die cursor-Methode auf und startest deine async-for-Loop. Weil die Transaction für die gesamte Dauer des Context-Manager-Blocks offen bleibt, bleibt dein Cursor auf dem PostgreSQL-Server am Leben, sodass du alle eine Million Rows sicher streamen kannst. Es gibt eine seltene Ausnahme von dieser Regel. PostgreSQL unterstützt ein Feature, bei dem du einen Raw-SQL-Cursor mit dem Zusatz WITH HOLD deklarieren kannst. Das sagt der Database-Engine, dass sie das Result materialisieren und den Cursor auch nach Abschluss der Transaction am Leben halten soll. Das frisst Database-Resources auf und umgeht die Effizienz von Standard-Streaming. Für fast alle Streaming-Tasks in asyncpg ist der explizite Transaction-Block der erforderliche Ansatz. Wenn du diese Episoden nützlich findest und die Show unterstützen möchtest, such auf Patreon nach DevStoriesEU. Denk daran, dass ein Cursor deine Database-Interaction von einer massiven, riskanten Memory-Allocation in eine kontrollierte, persistente Pipeline verwandelt, die jedes Datenvolumen sicher verarbeiten kann. Das war's für diese Folge. Danke fürs Zuhören und keep building!
14

Blitzschnelle Datenaufnahme mit COPY

3m 57s

Gib deinen Datenaufnahme-Pipelines einen Boost. Wir untersuchen das PostgreSQL COPY-Protokoll, um Daten exponentiell schneller massenhaft zu laden als mit INSERT-Statements.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 14 von 17. Du musst eine Million Rows in deine Datenbank schieben oder aus ihr herausholen. Wenn dein erster Instinkt ist, eine riesige Liste von Insert-Statements zu batchen oder alle Rows in eine gigantische Python-Liste zu fetchen, wählst du den langsamsten Weg, den es gibt. Es gibt einen Mechanismus, der speziell dafür gebaut wurde, diesen Overhead zu umgehen. Heute sprechen wir über Blazing Fast Ingestion mit COPY. COPY ist ein Postgres-spezifisches Protokoll für Bulk Data Transfer. Wenn du Standard-Insert- oder Select-Statements ausführst, muss Postgres die Query parsen, planen und ausführen. Das immer wieder zu tun, erzeugt massiven Overhead. Das COPY-Protokoll überspringt die Standard-Query-Pipeline komplett. Es öffnet einen direkten Stream zum Storage Layer und bewegt die Daten in einem hochoptimierten Format. Deshalb ist es um Größenordnungen schneller als Bulk Inserts. In asyncpg schiebst du Daten mit einer Methode namens copy to table in die Datenbank. Du gibst den Namen der Target Table und eine Data Source an. Diese Source kann ein lokaler File Path, ein file-like Object oder ein asynchroner Iterator sein, der Records yieldet. Wenn du auf ein lokales CSV-File zeigst, übernimmt Postgres das Parsen nativ. Du musst das File nicht in Python öffnen, die Rows parsen und auf Variablen mappen. Der Database Driver streamt die rohen File-Bytes direkt an den Server. Du kannst auch eine einfache Python-Liste von Tuples übergeben, wenn deine Daten schon im Memory sind, und asyncpg streamt sie under the hood mit dem COPY-Protokoll. Daten herauszuholen ist genauso schnell. Wenn du einen vollen Export brauchst, nutzt du copy from table. Das nimmt den kompletten Inhalt einer Tabelle und feuert ihn in ein File oder einen Stream. Allerdings ist das Dumpen einer kompletten Tabelle selten das, was du wirklich brauchst. Normalerweise willst du gefilterte oder gejointe Daten. Hier kommt copy from query ins Spiel. Ein häufiges Missverständnis ist, dass diese Methode Query-Results nur in ein statisches File dumpt. Das ist einfach nicht wahr. Sie kann zwar direkt in einen File Path schreiben, aber du kannst auch eine Callback-Funktion übergeben. Asyncpg führt die Query aus und streamt die Results in Chunks an deinen Callback, sodass du ein massives Dataset on the fly verarbeiten kannst, ohne jemals das komplette Result Set im System Memory zu halten. Stell dir ein Szenario vor, in dem du einen CSV-Report aller aktiven User generieren musst. Ein Standardansatz ist, eine Select-Query auszuführen, hunderttausend Rows nach Python zu fetchen, sie mit dem CSV-Modul zu formatieren und auf die Disk zu schreiben. Das verbraucht massiv Memory und CPU. Hier ist die entscheidende Erkenntnis. Du kannst das Python-Processing komplett überspringen. Du rufst copy from query auf, übergibst dein spezifisches Select-Statement, setzt den Format-Parameter auf CSV und gibst einen Output File Path an. Postgres führt die Query aus, formatiert die Results nativ auf dem Database Server in CSV, und asyncpg streamt den fertigen Text direkt auf deine Festplatte. Deine Python-Applikation fungiert als einfache Pipe und macht fast null Datenmanipulation. Du solltest für alltägliche Application Logic weiterhin Standard-Insert- und Select-Statements nutzen, aber in dem Moment, in dem das rohe Datenvolumen zu deinem Bottleneck wird, wechsle zum COPY-Protokoll, um den SQL-Parser komplett zu umgehen. Das war's für diese Folge. Danke fürs Zuhören und keep building!
15

Asynchrones Listen und Notify

3m 54s

Erschließe ereignisgesteuerte Echtzeit-Architekturen direkt in PostgreSQL. Lerne, wie du add_listener von asyncpg für sofortiges Pub/Sub-Messaging verwendest.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 15 von 17. Sobald du Real-Time Events brauchst, greifst du wahrscheinlich zu einem separaten Message Broker wie Redis. Aber wenn deine Anwendung bereits eine Datenbank nutzt, fügst du vielleicht völlig ohne Grund Infrastruktur-Komplexität hinzu. PostgreSQL hat einen Real-Time Message Bus direkt integriert. Heute sprechen wir über Asynchronous Listen and Notify. Postgres unterstützt von Haus aus ein Publish-Subscribe-Pattern mit zwei Commands, Listen und Notify. Die asyncpg Library stellt diese Funktionalität in Python über eine Methode namens add listener bereit. Anstatt eine Loop zu schreiben, die alle paar Sekunden eine Datenbanktabelle pollt, um nach neuen Daten zu suchen, registrierst du eine asynchrone Python Callback-Funktion auf einem spezifischen, benannten Channel. Wenn ein Event in Postgres auftritt, sendet es eine Message an diesen Channel, und dein Python Callback wird sofort ausgeführt. Hier ist der entscheidende Punkt. Ein Listener ist nicht global an deine Anwendung gebunden, und er ist auch nicht an einen Connection Pool gebunden. Er ist an eine spezifische, individuelle Datenbank-Connection gebunden. Das ist ein häufiger Point of Failure. Wenn du eine Connection aus einem Pool ziehst, deinen Listener registrierst und die Connection dann wieder an den Pool zurückgibst, geht der Listener verloren. Um dieses Feature zuverlässig zu nutzen, musst du eine Connection von asyncpg anfordern, die add listener Methode darauf aufrufen und diese Connection dauerhaft offen halten. Sie wird zu einer dedizierten Listening-Pipeline. Schauen wir uns ein praktisches Szenario an. Du hast einen Background Worker, der aufwachen und Records verarbeiten muss, sobald eine neue Row in eine Jobs-Tabelle eingefügt wird. Anstatt zu pollen, richtest du einen Datenbank-Trigger ein. Bei jedem Insert führt der Trigger einen Notify Command auf einem Channel namens new jobs aus. Er sendet auch einen kurzen Text Payload, wie zum Beispiel den Unique Identifier der neuen Row. In deinem Python-Code schreibst du eine asynchrone Callback-Funktion. Diese Funktion erwartet vier Argumente von asyncpg: das Connection-Objekt, den Postgres Process Identifier, den Channel-Namen und den Text Payload selbst. Als Nächstes holst du dir deine dedizierte Connection. Du rufst add listener auf dieser Connection auf und übergibst den Channel String new jobs zusammen mit deiner Callback-Funktion. Schließlich hältst du das Script am Laufen, typischerweise indem du auf ein asynchrones Event wartest, das niemals gesetzt wird. Dein Python-Prozess ist jetzt komplett idle. Er verbraucht fast null CPU-Ressourcen und hört auf, die Datenbank mit leeren Queries zu bombardieren. In dem Moment, in dem eine Transaction eine neue Job-Row committet, pusht Postgres die Notification über den offenen Network Socket. Asyncpg liest diesen Socket und plant deinen Callback sofort auf dem Python Event Loop ein, wobei der neue Job Identifier übergeben wird. Die wahre Stärke dieses Patterns ist die transaktionale Konsistenz. Wenn eine Datenbank-Transaction einen Rollback macht, werden alle Notify Commands, die während dieser Transaction ausgeführt wurden, von Postgres automatisch verworfen. Das garantiert, dass deine Python Worker immer nur aufwachen und auf Daten reagieren, die erfolgreich auf der Disk gespeichert wurden. Das war's für diese Folge. Danke fürs Zuhören und keep building!
16

Telemetrie und Query Logging

3m 43s

Erhalte tiefe Einblicke in die Leistung deiner Datenbank. Entdecke, wie du asyncpg Log Listeners verwendest, um langsame Abfragen zu verfolgen und die Ausführungstelemetrie zu überwachen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 16 von 17. Um herauszufinden, warum deine Datenbank langsam ist, musst du nicht raten. Du könntest dich auf serverseitige Logs verlassen, aber die erfassen weder die Network Latency noch den Python-seitigen Overhead. Die Performance manuell bei jedem Datenbankaufruf zu messen, macht deine Business Logic schnell unübersichtlich. Die Lösung ist Telemetry und Query Logging. Leute verwechseln oft Log Listener und Query Logger. Log Listener fangen Postgres Server Notices ab. Query Logger fangen die clientseitige Execution Telemetry ab. Sie verarbeiten zwei völlig unterschiedliche Informationsströme. Query Logger kümmern sich um die Telemetry. Du hängst eine Callback-Funktion über die add query logger Methode an deine Connection an. Sobald das erledigt ist, übergibt asyncpg jedes Mal, wenn eine Query fertig ausgeführt ist, automatisch ein LoggedQuery Objekt an diesen Callback. Das passiert global für diese Connection, komplett entkoppelt von der spezifischen Funktion, die den Database Request ausführt. Das LoggedQuery Objekt enthält drei wichtige Datenpunkte. Es enthält den exakten SQL Query Text, die Argumente, die an diese Query übergeben wurden, und die Execution Time. Die Argumente werden genau so erfasst, wie deine Application sie bereitgestellt hat. Das erspart dir das manuelle Schreiben von String Formatting Logic, nur um herauszufinden, welche Parameter eine langsame Response verursacht haben. Stell dir eine Production Environment vor, in der du jede Query abfangen musst, die länger als 500 Millisekunden dauert. Du definierst eine Standard-Python-Funktion, die zwei Parameter akzeptiert: die Database Connection und das LoggedQuery Objekt. Innerhalb dieser Funktion checkst du das Execution Time Attribut. Wenn die Zeit null Komma fünf Sekunden überschreitet, schreibst du den Query Text, die Argumente und die genaue Duration in dein Application Monitoring System. Dann übergibst du diese Callback-Funktion an die add query logger Methode. Jetzt trackt deine Application langsame Queries automatisch und still im Hintergrund. Wenn du dieses Tracking jemals stoppen musst, übergibst du einfach denselben Callback an die remove query logger Methode. Der zweite Bestandteil ist der Log Listener. Während der Query Logger das clientseitige Timing übernimmt, kümmert sich der Log Listener um das serverseitige Messaging. Manchmal sendet Postgres Messages, die keine Errors sind und keine Data Rows zurückgeben. Das sind asynchrone Notices, Warnings oder Custom Log Messages, die direkt von der Database Engine generiert werden. Um diese Messages abzufangen, hängst du einen Callback über die add log listener Methode an. Wenn Postgres eine Notice oder eine Warning ausgibt, triggert asyncpg diesen Callback. Es übergibt die Connection und ein spezifisches Message Objekt an deine Funktion. Das gibt deiner Application sofortigen Einblick in Database-Level Warnings, komplett unabhängig von deinen Standard Query Results. Genau wie beim Query Logger kannst du diesen Callback später mit remove log listener wieder entfernen. Hier ist der entscheidende Punkt. Clientseitiges Query Logging liefert dir die tatsächliche Execution Duration, die deine Python Application erfährt, und umgeht so komplett das Rätselraten zwischen Network Delay und Database Processing. Danke fürs Zuhören. Macht's gut, Leute.
17

Verbindungen mit SSL absichern

3m 57s

Stelle sicher, dass deine Datenbankverbindungen sicher sind. Wir behandeln die Konfiguration des SSL-Kontexts und wie man direktes TLS bei der Verbindung zu Cloud-Datenbanken erzwingt.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. High-Performance Python Async, Folge 17 von 17. Die Verbindung zu einer Managed Cloud Database ohne SSL ist, als würdest du deine Database Credentials in einem vollen Raum lautstark durchgeben. Du brauchst Verschlüsselung, doch die korrekte Einrichtung führt oft zu verwirrenden Connection-Fehlern oder unsicheren Defaults. Heute sichern wir Connections mit SSL in asyncpg. Viele Leute stolpern oft darüber, wie sie SSL in ihrer Connection Logic konfigurieren. Wenn du Connection URIs nutzt, parst asyncpg nativ die Standard PostgreSQL sslmode Query-Parameter, wie etwa das Setzen von sslmode auf require. Das funktioniert für einfache Setups. Wenn du jedoch präzise Kontrolle brauchst – etwa für die sichere Verbindung zu einer Managed Cloud Database mit einem Custom Certificate Authority Bundle – reichen Standard URI-Strings nicht aus. Dafür nutzt du den programmatischen ssl Parameter. Der ssl Parameter in den asyncpg Connection-Funktionen legt fest, wie TLS ausgehandelt wird. Er akzeptiert zwei Arten von Werten. Der erste Typ ist ein String Preset. Du kannst den String prefer übergeben, der eine SSL Connection versucht, aber auf unverschlüsselt zurückfällt, falls der Server es nicht unterstützt. Du kannst require übergeben, was die Verschlüsselung erzwingt, aber die Überprüfung der Serveridentität überspringt. Oder du übergibst verify-full, was die Verschlüsselung erzwingt und das Serverzertifikat streng anhand von Trusted Roots validiert. Hier ist die wichtigste Erkenntnis: Wenn dein Szenario eine Custom Certificate Authority erfordert, verlass dich nicht auf String Presets. Erstelle stattdessen ein Standard Python SSLContext Objekt. Du konfigurierst dieses Objekt mit deinen Custom Certificate Files, erzwingst eine strenge Überprüfung und übergibst dieses Python Objekt dann direkt an den asyncpg ssl Parameter. Das gibt dir die exakte Kontrolle über den kryptografischen Handshake und umgeht alle Default System Certificates. Damit sind die Verschlüsselungsregeln abgedeckt, aber wie startet die Connection eigentlich? Das bringt uns zum direct TLS Parameter. Standardmäßig verwendet PostgreSQL ein Protokoll namens STARTTLS. Der Client stellt eine Plain-Text Connection her, fragt den Server, ob er Verschlüsselung unterstützt, und wenn der Server ja sagt, upgraden sie die Connection auf TLS. Moderne Proxy Setups – wie bestimmte Connection Pooler oder Cloud Load Balancer – erwarten jedoch oft eine Direct TLS Connection vom allerersten Byte an. Sie wollen keine Plain-Text Negotiation. Wenn deine Infrastruktur so aufgebaut ist, übergibst du true an den direct TLS Connection Parameter. Wenn du das tust, überspringt asyncpg die STARTTLS Negotiation und initiiert sofort einen Raw TLS Handshake. Das funktioniert natürlich nur, wenn du auch eine gültige ssl Configuration angibst. Wenn du direct TLS aktivierst, den ssl Parameter aber leer lässt, schlägt die Connection fehl. Wenn du deine Database Connections absicherst, denk daran, dass String Presets zwar praktisch sind, die explizite Übergabe eines SSLContext Objekts jedoch der einzige Weg ist, um absolut zu garantieren, dass deine Application der korrekten Serveridentität vertraut. Da dies die letzte Folge der Serie ist, ermutige ich dich, die offizielle asyncpg Documentation zu erkunden und diese Connection Parameter hands-on auszuprobieren. Du kannst devstories dot eu besuchen, um Themen für unsere zukünftigen Serien vorzuschlagen. Das war's für diese Folge. Danke fürs Zuhören und keep building!