Wróć do katalogu
Season 13 17 Odcinki 1h 8m 2026

High-Performance Python Async

Edycja 2026. Dogłębne spojrzenie na przyspieszanie Python asyncio za pomocą uvloop oraz bezpośrednią komunikację z PostgreSQL przy użyciu protokołu binarnego asyncpg.

Python Core Programowanie asynchroniczne
High-Performance Python Async
Teraz odtwarzane
Click play to start
0:00
0:00
1
Potrzeba szybkości: Architektura uvloop
Odkryj różnice architektoniczne między standardowym asyncio w Pythonie a uvloop. Analizujemy, jak uvloop wykorzystuje Cython i libuv, aby osiągnąć wydajność zbliżoną do języka Go.
3m 52s
2
Wdrażanie uvloop
Dowiedz się, jak zintegrować uvloop ze swoją aplikacją w Pythonie. Ten odcinek omawia podejście oparte na EventLoopPolicy, pozwalające na płynną wymianę domyślnej pętli zdarzeń.
4m 19s
3
Wprowadzenie do asyncpg: Protokół binarny
Poznaj podstawy projektowe asyncpg. Omawiamy, dlaczego pominięcie standardowego DB-API na rzecz protokołu binarnego PostgreSQL przynosi ogromny wzrost wydajności.
3m 52s
4
Łączenie i podstawowe zapytania
Rozpocznij pracę z asyncpg, łącząc się z bazą danych i uruchamiając podstawowe zapytania. Zrozum natywną składnię argumentów Postgres.
3m 54s
5
Natywna konwersja typów
Odkryj, jak asyncpg automatycznie mapuje typy danych PostgreSQL na natywne obiekty Pythona, eliminując potrzebę złożonego parsowania przez ORM.
3m 38s
6
Niestandardowe kodeki typów
Naucz się definiować niestandardowe konwersje danych w asyncpg. Ten odcinek wyjaśnia, jak używać set_type_codec do automatycznego dekodowania JSONB na słowniki Pythona.
4m 09s
7
Zaawansowane kodeki z PostGIS
Zanurz się w niestandardowe kodeki typów, mapując typy geometryczne PostGIS z PostgreSQL na obiekty Shapely w Pythonie przy użyciu formatu binarnego.
3m 53s
8
Zarządzanie transakcjami
Opanuj transakcje bazodanowe w asyncpg. Omawiamy zachowanie auto-commit oraz bezpieczne wykonywanie wielu zapytań przy użyciu asynchronicznych menedżerów kontekstu.
3m 27s
9
Pula połączeń
Skaluj swoją aplikację dzięki wbudowanej puli połączeń w asyncpg. Dowiedz się, jak efektywnie zarządzać połączeniami z bazą danych w usługach sieciowych o dużym natężeniu ruchu.
3m 31s
10
Buforowanie przygotowanych zapytań
Zrozum, jak asyncpg optymalizuje parsowanie zapytań dzięki automatycznym przygotowanym zapytaniom i dlaczego zewnętrzne pule, takie jak PgBouncer, mogą powodować konflikty.
4m 21s
11
Tablice Postgres i klauzule IN
Rozwiąż najczęstszy błąd składniowy podczas migracji do asyncpg. Dowiedz się, jak poprawnie filtrować zapytania względem listy wartości za pomocą ANY().
4m 25s
12
Obiekty Record a Named Tuples
Poznaj unikalny projekt obiektów Record w asyncpg. Zrozum, dlaczego notacja kropkowa jest domyślnie pomijana i jak ją włączyć za pomocą niestandardowych klas.
4m 18s
13
Strumieniowanie wyników za pomocą kursorów
Zapobiegaj wyczerpaniu pamięci podczas zapytań do ogromnych zbiorów danych. Dowiedz się, jak używać kursorów asyncpg do strumieniowania wyników fragment po fragmencie.
4m 30s
14
Błyskawiczne ładowanie danych za pomocą COPY
Przyspiesz swoje potoki ładowania danych. Analizujemy protokół COPY w PostgreSQL, aby masowo ładować dane nieporównywalnie szybciej niż za pomocą instrukcji INSERT.
3m 42s
15
Asynchroniczne Listen i Notify
Odblokuj architektury sterowane zdarzeniami w czasie rzeczywistym bezpośrednio w PostgreSQL. Dowiedz się, jak używać add_listener w asyncpg do natychmiastowego przesyłania wiadomości pub/sub.
3m 40s
16
Telemetria i logowanie zapytań
Zyskaj głęboką obserwowalność wydajności swojej bazy danych. Odkryj, jak używać listenerów logów w asyncpg do śledzenia wolnych zapytań i monitorowania telemetrii wykonania.
4m 42s
17
Zabezpieczanie połączeń za pomocą SSL
Upewnij się, że Twoje połączenia z bazą danych są bezpieczne. Omawiamy konfigurację kontekstu SSL i wymuszanie bezpośredniego TLS podczas łączenia się z chmurowymi bazami danych.
4m 11s

Odcinki

1

Potrzeba szybkości: Architektura uvloop

3m 52s

Odkryj różnice architektoniczne między standardowym asyncio w Pythonie a uvloop. Analizujemy, jak uvloop wykorzystuje Cython i libuv, aby osiągnąć wydajność zbliżoną do języka Go.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny Python Async, odcinek 1 z 17. A co, gdybyś mógł podwoić wydajność swojego kodu async w Pythonie bez przepisywania ani jednej funkcji? To dokładnie to, co obiecuje technologia, której przyjrzymy się dzisiaj: architektura uvloop. Kiedy programiści mówią o standardowym asyncu w Pythonie, często mieszają dwie odrębne warstwy. Pierwsza warstwa to application programming interface. Obejmuje ona słowa kluczowe async i await, które wpisujesz, a także futures i taski. Druga warstwa to sam event loop. Event loop to wewnętrzny scheduler, który monitoruje sockety, zarządza połączeniami sieciowymi i decyduje, który task zostanie uruchomiony jako następny. Standardowy Python dostarcza zarówno interfejs, jak i domyślny event loop napisany w czystym Pythonie. I tu pojawia się kluczowa kwestia. Interfejs i event loop są od siebie oddzielone. Python pozwala ci podmienić bazowy scheduler bez zmiany składni, którą piszesz. Pomyśl o tym jak o samochodzie. Async API to kierownica, pedały i deska rozdzielcza. Korzystasz z nich bezpośrednio. Event loop to silnik pod maską. Zamiana domyślnego event loopa Pythona na uvloop jest jak włożenie silnika V8 do twojego samochodu. Nadal kierujesz i hamujesz dokładnie tak samo, ale samochód jedzie znacznie szybciej. Rdzeń uvloop opiera się na dwóch decyzjach architektonicznych, aby osiągnąć tę prędkość. Po pierwsze, jest to zamiennik typu drop-in napisany w całości w Cythonie. Cython kompiluje kod podobny do Pythona do wysoce zoptymalizowanych rozszerzeń w C. Eliminuje to narzut standardowego interpretera Pythona podczas wykonywania tak zwanych hot paths w schedulerze. Event loopy w czystym Pythonie spędzają dużo czasu na tworzeniu wewnętrznych obiektów i zarządzaniu stanem interpretera, tylko po to, by obsłużyć rutynowe zdarzenia sieciowe. Cython całkowicie to eliminuje. Za każdym razem, gdy event loop sprawdza socket lub budzi task, wykonuje natywny kod w C zamiast przechodzić przez czystego Pythona. Po drugie, uvloop deleguje właściwe interakcje z systemem operacyjnym do biblioteki w C o nazwie libuv. Jeśli ta nazwa brzmi znajomo, to dlatego, że libuv to asynchroniczny silnik I/O, który napędza Node.js. Jest on sprawdzony w boju, mocno zoptymalizowany pod kątem dużych obciążeń sieciowych i obsługuje wszystkie złożone, wieloplatformowe detale asynchronicznego networkingu. Opakowując libuv w ciasną powłokę w Cythonie, uvloop przenosi dokładnie ten sam profil wydajnościowy bezpośrednio do Pythona. Rezultat architektoniczny jest potężny. Omijając scheduler w czystym Pythonie i opierając się na skompilowanym silniku w C, uvloop sprawia, że twoje aplikacje w asyncio są co najmniej dwukrotnie szybsze. W wielu benchmarkach obejmujących wysoką współbieżność połączeń, pozwala to Pythonowi dorównać wydajnością kompilowanym językom, takim jak Go. Zyskujesz developer velocity Pythona z surową szybkością wykonywania natywnego networkingu w C. Przejście nie wymaga absolutnie żadnych zmian w twojej logice biznesowej, zapytaniach do bazy danych ani endpointach API. Podstawowy wniosek jest taki, że wąskie gardła wydajnościowe w standardowym asyncu w Pythonie rzadko dotyczą składni języka, a raczej silnika wykonawczego, a zastąpienie tego silnika daje ci natywne prędkości C przy jednoczesnym zachowaniu twoich istniejących abstrakcji w Pythonie. Jeśli chcesz wesprzeć nasz podcast, możesz wyszukać DevStoriesEU na Patreonie. To wszystko w tym odcinku. Dzięki za słuchanie i buduj dalej!
2

Wdrażanie uvloop

4m 19s

Dowiedz się, jak zintegrować uvloop ze swoją aplikacją w Pythonie. Ten odcinek omawia podejście oparte na EventLoopPolicy, pozwalające na płynną wymianę domyślnej pętli zdarzeń.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny async w Pythonie, odcinek 2 z 17. Najpotężniejsza optymalizacja wydajności w twojej asynchronicznej aplikacji w Pythonie nie wymaga żadnych zmian w architekturze, refaktoryzacji ani skomplikowanej konfiguracji. To dokładnie dwie linijki kodu. Dzisiaj porozmawiamy o wrzuceniu uvloopa. Event loop ze standardowej biblioteki asyncio jest napisany w czystym Pythonie. Uvloop to drop-in replacement zbudowany na bazie libuv, tego samego silnika, który napędza inne runtime'y o wysokiej współbieżności. Sprawia, że twój standardowy kod async w Pythonie wykonuje się znacznie szybciej. Zastąpienie głównego mechanizmu schedulingu w twojej aplikacji wymaga powiedzenia Pythonowi, żeby porzucił swoje domyślne zachowanie, zanim zacznie wykonywać jakąkolwiek właściwą pracę. W skrypcie entry point serwera webowego, na przykład w głównym pliku aplikacji FastAPI lub aiohttp, implementujesz tę podmianę za pomocą event loop policy. Event loop policy to globalny obiekt konfiguracyjny wewnątrz standardowego modułu asyncio. Dyktuje on, jaki rodzaj event loopa zostaje utworzony za każdym razem, gdy aplikacja poprosi o nowy. Aby podmienić event loop, importujesz moduł asyncio i moduł uvloop. Następnie wywołujesz funkcję set event loop policy na module asyncio. Przekazujesz do niej świeżą instancję event loop policy dostarczaną przez moduł uvloop. Oto kluczowa sprawa. Musisz ustawić tę policy wcześnie. To wywołanie musi znaleźć się na samej górze twojego głównego skryptu wykonawczego, zaraz po importach. Event loop policy wpływa tylko na tworzenie nowych event loopów. Jeśli poczekasz z ustawieniem policy do momentu, aż twój framework webowy już wystartuje, albo aż asynchroniczny driver bazy danych się zainicjalizuje, standardowy event loop w czystym Pythonie prawdopodobnie już działa. Zmiana policy w tym momencie nie robi nic z istniejącym event loopem. Twój kod albo całkowicie zignoruje uvloopa, albo skończy z pomieszanymi event loopami, co spowoduje deadlocki i zerwane połączenia. Istnieje alternatywa dla podejścia opartego na policy. Zamiast zmieniać globalne zasady tworzenia event loopów, możesz jawnie utworzyć pojedynczą instancję uvloopa. Robisz to, wywołując funkcję new event loop bezpośrednio z modułu uvloop. Kiedy masz już ten obiekt event loopa w pamięci, przekazujesz go do asyncio, wywołując funkcję set event loop. Dlaczego miałbyś wybrać jedno podejście zamiast drugiego? Ustawienie event loop policy to globalny override. Gwarantuje to, że każda zewnętrzna biblioteka, background task czy komponent frameworka w twoim procesie, który poprosi asyncio o nowy event loop, bezpiecznie otrzyma uvloopa. To standardowy wybór dla serwera webowego, gdzie zależy ci na jednolitej wydajności w całym stacku aplikacji. Podejście z jawnym new event loop jest węższe. Wstrzykuje konkretną instancję, zamiast zmieniać zasady factory. Używasz tej jawnej metody, kiedy zarządzasz złożonymi środowiskami z wieloma wątkami, albo kiedy potrzebujesz ścisłej kontroli nad tym, który dokładnie event loop działa w izolowanym kontekście, bez mutowania globalnego stanu procesu. W standardowych aplikacjach webowych, override policy to wszystko, czego potrzebujesz. Dokładny mechanizm, który wybierzesz, żeby wrzucić uvloopa, ma mniejsze znaczenie niż moment, w którym go zastosujesz. Event loop policy dyktuje fundamenty całej twojej architektury async, więc musi to być absolutnie pierwsza instrukcja, jaką wykonuje twoja aplikacja, zanim jakikolwiek kontekst async zostanie w ogóle ustanowiony. To wszystko w tym odcinku. Dzięki za wysłuchanie i koduj dalej!
3

Wprowadzenie do asyncpg: Protokół binarny

3m 52s

Poznaj podstawy projektowe asyncpg. Omawiamy, dlaczego pominięcie standardowego DB-API na rzecz protokołu binarnego PostgreSQL przynosi ogromny wzrost wydajności.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny async w Pythonie, odcinek 3 z 17. Optymalizujesz indeksy, robisz upgrade sieci i dostrajasz zapytania, ale twoja aplikacja nadal przepala cykle CPU na komunikację z bazą danych. Największym wąskim gardłem w twoich zapytaniach do bazy danych często wcale nie jest opóźnienie sieciowe. To czas, jaki twoja aplikacja spędza na parsowaniu tekstu. Aby wyeliminować ten overhead, wprowadzamy asyncpg i jego implementację binarnego protokołu PostgreSQL. Często błędnie uważa się, że asyncpg to po prostu asynchroniczny wrapper na psycopg2. Wcale tak nie jest. Nie jest to również adaptacja standardowego Python DB-API. Specyfikacja DB-API z natury kieruje drivery bazodanowe w stronę pewnych ustandaryzowanych wzorców obsługi danych. asyncpg całkowicie ignoruje tę specyfikację. To kod napisany od zera, zaprojektowany wyłącznie dla asyncio i PostgreSQL, omijający standardowe interfejsy bazodanowe, aby komunikować się z Postgresem na jego własnych zasadach. Aby zrozumieć, dlaczego ten design ma znaczenie, spójrz, jak tradycyjne drivery obsługują transfer danych. Większość driverów bazodanowych komunikuje się z Postgresem używając formatu tekstowego. Kiedy odpytujesz bazę o liczbę, timestamp czy złożoną tablicę, baza bierze swoją wewnętrzną reprezentację tych danych w pamięci i konwertuje ją na string. Następnie wysyła ten string przez sieć. Kiedy twoja aplikacja w Pythonie go odbierze, driver musi sparsować ten string z powrotem na pythonowy integer, obiekt datetime lub listę. Pomyśl o tym tradycyjnym podejściu jak o zespole, który polega na tłumaczu przy każdej wewnętrznej rozmowie. Baza danych odczytuje swoje natywne struktury danych, zapisuje je jako ustandaryzowane dokumenty tekstowe i wysyła przez sieć. Twoja aplikacja w Pythonie odbiera te dokumenty i mozolnie tłumaczy tekst z powrotem na swoje własne ustrukturyzowane obiekty w pamięci. Całe to kodowanie, zamiana na stringi i parsowanie przepala czas CPU i zużywa pamięć. asyncpg rozwiązuje ten problem, komunikując się bezpośrednio przez binarny protokół frontendu i backendu PostgreSQL. Wymusza na bazie danych wyłączne korzystanie z binarnego inputu i outputu. Zamiast polegać na tłumaczu, baza danych i driver mówią dokładnie w tym samym natywnym języku. Jeśli odpytujesz o 64-bitowy integer, PostgreSQL wysyła surowe bajty reprezentujące ten integer. asyncpg wczytuje te bajty bezpośrednio do pythonowego obiektu integer. Nie ma formatowania stringów. Nie ma parsowania tekstu. To natywne zrozumienie rozciąga się też na złożone dane. Kiedy żądasz bloku JSON, uniwersalnego unikalnego identyfikatora czy geometrycznego typu danych, binarny protokół dba o to, by payload pozostał kompaktowy i ściśle ustrukturyzowany. Driver dokładnie wie, ile bajtów odczytać dla każdej kolumny, bez konieczności skanowania w poszukiwaniu ograniczników tekstu. I tu jest kluczowa sprawa. Szybkość asyncpg nie bierze się przede wszystkim z nieblokującej natury pythonowego asyncio. Ogromny wzrost wydajności wynika z całkowitego usunięcia warstwy translacji tekstu. Wykonujesz znacznie mniej pracy na każdy zwrócony wiersz. Dzięki ścisłemu wymuszaniu binarnego transferu danych, twoja aplikacja przestaje marnować zasoby na czytanie tekstu i poświęca ten czas CPU na wykonywanie twojej właściwej logiki biznesowej. To wszystko w tym odcinku. Dzięki za wysłuchanie i buduj dalej!
4

Łączenie i podstawowe zapytania

3m 54s

Rozpocznij pracę z asyncpg, łącząc się z bazą danych i uruchamiając podstawowe zapytania. Zrozum natywną składnię argumentów Postgres.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. High-Performance Python Async, odcinek 4 z 17. Jeśli jesteś przyzwyczajony do standardowych driverów bazodanowych w Pythonie, sposób przekazywania zmiennych do query mocno cię zaskoczy, a twój kod natychmiast się wysypie. Standardowe drivery w Pythonie polegają na znacznikach formatowania stringów, ale ta biblioteka gada bezpośrednio z silnikiem bazy danych. Dzisiaj omówimy łączenie i podstawowe execution. Aby zacząć gadać z bazą danych, używasz funkcji connect z asyncpg. Robisz await na tej funkcji i przekazujesz jej Data Source Name, czyli standardowe URI połączenia do Postgresa. Wygląda to dokładnie jak adres internetowy. Zaczyna się od postgresql dwukropek ukośnik ukośnik, potem jest twój username, dwukropek, hasło, małpa, adres hosta, i na końcu ukośnik z nazwą bazy danych. Zrobienie await na tej funkcji zestawia połączenie sieciowe i daje ci aktywny obiekt connection. Teraz chcesz zrobić insert nazwy użytkownika i daty urodzenia do tabeli. I tutaj zmienia się składnia query. Nie używaj procent s ani znaków zapytania dla parametrów swojego query. Ponieważ asyncpg celowo omija standardowe bazodanowe API w Pythonie, wymusza na tobie użycie natywnych placeholderów Postgresa. Piszesz znak dolara jeden, znak dolara dwa i tak dalej. Twój query string będzie wyglądał jak standardowy insert statement, ale wartościami będą dolar jeden i dolar dwa. Aby odpalić to query bez żądania zwrotu danych, robisz await na metodzie execute na twoim obiekcie connection. Najpierw przekazujesz query string, a potem właściwe zmienne dla imienia i daty urodzenia. Metoda execute odpala ten statement i odrzuca wszelkie dane tabelaryczne. Zwraca po prostu status string z Postgresa, coś w stylu insert zero jeden. Nie zwraca faktycznych wierszy z bazy danych. Jeśli potrzebujesz, żeby baza danych wygenerowała unikalne ID dla tego nowego użytkownika, i potrzebujesz tego ID z powrotem w Pythonie, execute to złe narzędzie. Zmieniasz swoje SQL query, dodając na końcu klauzulę returning id. Ponieważ teraz oczekujesz zwrotu danych, używasz metody fetchval. Metoda fetchval odpala query i zwraca dokładnie jedną konkretną wartość. Patrzy na pierwszy zwrócony wiersz, bierze pierwszą kolumnę i daje ci tylko ten jeden fragment danych. To idealne rozwiązanie do wyciągnięcia nowo wygenerowanego user ID. Jeśli potrzebujesz czegoś więcej niż tylko ID, na przykład chcesz pobrać domyślne wartości bazy danych dla kilku kolumn, używasz zamiast tego fetchrow. Zrobienie await na fetchrow zwraca pojedynczy obiekt rekordu, zawierający wszystkie kolumny z tego pierwszego wiersza. Możesz dostać się do danych w tym rekordzie dokładnie tak, jak w Pythonowym dictionary, używając nazw kolumn jako kluczy. Kiedy skończysz robić insert swoich danych, musisz zrobić await na metodzie close na obiekcie connection, aby posprzątać network socket i zwolnić zasoby bazy danych. Oto kluczowa sprawa. Wymuszanie użycia natywnych placeholderów ze znakiem dolara to nie tylko stylistyczne dziwactwo. Pozwala to asyncpg całkowicie ominąć client-side string interpolation, mapując typy Pythona bezpośrednio na formaty binarne Postgresa, co zapewnia maksymalną szybkość i pełną ochronę przed SQL injection. Dzięki za wysłuchanie, miłego kodowania wszystkim!
5

Natywna konwersja typów

3m 38s

Odkryj, jak asyncpg automatycznie mapuje typy danych PostgreSQL na natywne obiekty Pythona, eliminując potrzebę złożonego parsowania przez ORM.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. High-Performance Python Async, odcinek 5 z 17. Piszesz raw SQL query, wykonujesz je i przygotowujesz się na ręczne parsowanie stringów z datami, splitowanie oddzielonych przecinkami arrayów i castowanie wartości walut. Ale wcale nie musisz. Nie potrzebujesz ORM-a, żeby wyciągnąć z bazy danych w pełni otypowane obiekty Pythona. Asyncpg ogarnia to automatycznie dzięki natywnej konwersji typów. Kiedy asyncpg komunikuje się z Postgresem, używa binarnego protokołu bazy danych. Dokładnie wie, jaki typ danych reprezentuje każda kolumna. Zamiast zwracać ci surowe stringi, które wymagają dodatkowego parsowania w Pythonie, asyncpg tłumaczy typy Postgresa bezpośrednio na standardowe obiekty z biblioteki Pythona. Ta translacja działa automatycznie w obie strony. Kiedy przekazujesz obiekt Pythona jako parametr zapytania, asyncpg koduje go do odpowiedniego binarnego formatu Postgresa. A kiedy baza odpowiada, asyncpg dekoduje dane binarne z powrotem na odpowiedni typ Pythona. Wyobraź sobie scenariusz, w którym pobierasz profil użytkownika z bazy danych. Twoje zapytanie SQL prosi o username, array tagów użytkownika, czas utworzenia konta i ostatni znany adres IP. W wielu driverach bazodanowych dostałbyś z powrotem stringi, które musiałbyś ręcznie parsować. Z asyncpg rekord z wynikiem jest już otypowany. Username to standardowy pythonowy string. Kolumna z tagami, która w Postgresie jest arrayem, przychodzi jako natywna pythonowa lista stringów. Czas utworzenia to standardowy obiekt datetime. Adres IP, przechowywany w bazie jako typ inet, mapuje się bezpośrednio na wbudowane w Pythona obiekty ipaddress. Piszesz zero logiki parsowania, żeby to osiągnąć. Istnieje też ścisłe mapowanie liczb, które potrafi zaskoczyć niektórych developerów. Jeśli twoja kolumna w Postgresie jest zdefiniowana jako numeric, nie konwertuje się ona na pythonowego floata. Asyncpg mapuje postgresowy typ numeric bezpośrednio na pythonową klasę decimal dot Decimal. To zachowuje dokładną precyzję. Jeśli robisz zapytania o dane finansowe albo precyzyjne pomiary, nie stracisz danych przez błędy zaokrągleń floatów. Standardowe typy zmiennoprzecinkowe w Postgresie, takie jak real czy double precision, mapują się już na pythonowe floaty. Oto kluczowa sprawa dla innych specyficznych typów. Jeśli zrobisz selecta na natywnej kolumnie UUID, otrzymasz pythonowy obiekt uuid dot UUID, a nie zwykłą reprezentację w postaci stringa. Daty stają się obiektami datetime dot date. Interwały Postgresa idealnie mapują się na pythonowe timedelty. Dane binarne przechowywane w kolumnie bytea są konwertowane bezpośrednio na pythonowe bytes. Kolumny JSON i JSONB zachowują się nieco inaczej. Asyncpg domyślnie konwertuje dane JSON i JSONB na standardowe pythonowe stringi. Nie parsuje ich automatycznie do pythonowych dictów. Otrzymujesz surowy string, który możesz następnie przekazać do standardowego pythonowego modułu json, jeśli potrzebujesz manipulować zagnieżdżonymi danymi. Poleganie na tej binarnej translacji typów utrzymuje logikę twojej aplikacji w czystości i przenosi ciężar type safety na driver bazy danych, czyli tam, gdzie jego miejsce. To wszystko w tym odcinku. Dzięki za wysłuchanie i koduj dalej!
6

Niestandardowe kodeki typów

4m 09s

Naucz się definiować niestandardowe konwersje danych w asyncpg. Ten odcinek wyjaśnia, jak używać set_type_codec do automatycznego dekodowania JSONB na słowniki Pythona.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny Python Async, odcinek 6 z 17. Robisz query do bazy danych o rekord użytkownika i dostajesz z powrotem surowy string. Za każdym razem piszesz ten sam boilerplate, żeby sparsować ten string do dictionary, zanim będziesz mógł faktycznie użyć tych danych. Możesz nauczyć swój driver bazy danych natywnej obsługi formatu danych twojej aplikacji, co oszczędzi ci ręcznego parsowania przy każdym query. Robi się to za pomocą Custom Type Codecs. Kiedy robisz query do PostgreSQL używając drivera, podstawowe typy, takie jak integer i text, są tłumaczone automatycznie. Ale kiedy używasz bogatszych typów bazodanowych, takich jak JSON, driver musi wiedzieć, jak chcesz, żeby te dane były reprezentowane w Pythonie. Custom type codec działa jak warstwa translacji. Znajduje się pomiędzy połączeniem z bazą danych a logiką twojej aplikacji. Żeby to skonfigurować, używasz metody o nazwie set type codec. Wywołujesz tę metodę bezpośrednio na aktywnym obiekcie połączenia. Wymaga ona czterech głównych informacji. Po pierwsze, podajesz nazwę typu bazy danych, na przykład string jsonb. Po drugie, określasz schema, w którym znajduje się ten typ. Dla wbudowanych typów PostgreSQL, jest to schema pg catalog. Następnie dostarczasz logikę translacji, przekazując funkcję encodera i funkcję decodera. Encoder definiuje, jak Python wysyła dane do bazy danych. Przyjmuje twój obiekt Pythona i zwraca format zrozumiały dla PostgreSQL, zazwyczaj string. Jeśli pracujesz z JSON, możesz po prostu przekazać funkcję json dumps ze standardowej biblioteki. Decoder definiuje, jak dane wracają z bazy danych. Odbiera surowy string z PostgreSQL i zwraca twój docelowy obiekt Pythona. Dla JSON, po prostu przekazujesz funkcję json loads. Wyobraź sobie system przechowujący nieustrukturyzowane preferencje użytkownika. W twojej bazie danych, kolumna preferencji jest zdefiniowana jako jsonb. W twojej aplikacji w Pythonie, obsługujesz preferencje jako standardowy dictionary. Kiedy twój codec jest już skonfigurowany, wykonujesz podstawowe query select dla użytkownika. Kolumna preferencji trafia do twojej aplikacji już ustrukturyzowana jako dictionary w Pythonie. Kiedy odpalasz query insert, przekazujesz dictionary bezpośrednio jako argument query. Driver automatycznie odpala twój encoder, konwertuje dictionary na JSON i wysyła go do bazy danych. Nigdy nie wywołujesz encodera ani decodera ręcznie w kodzie wykonującym query. Oto kluczowa sprawa. Custom type codecs nie mają zastosowania globalnie do całego drivera bazy danych. Metoda set type codec modyfikuje tylko to konkretne połączenie, na którym została wywołana. Jeśli skonfigurujesz codec na jednym połączeniu, drugie połączenie nie będzie o nim nic wiedzieć i znowu zwróci surowe stringi. To zachowanie często powoduje problemy, kiedy developerzy wprowadzają connection pool. Nie możesz skonfigurować poola za pomocą pojedynczego wywołania metody dla codeca. Zamiast tego, musisz zarejestrować swój customowy codec za każdym razem, gdy nawiązywane jest nowe połączenie. Osiągasz to definiując funkcję inicjalizującą. Wewnątrz tej funkcji przyjmujesz nowy obiekt połączenia i wywołujesz na nim set type codec. Następnie przekazujesz tę funkcję inicjalizującą do logiki tworzącej twój pool. Pool uruchamia twoją funkcję automatycznie za każdym razem, gdy otwiera nowe połączenie, gwarantując, że twoje codeki są zawsze obecne i aktywne. Przeniesienie serializacji danych do warstwy drivera za pomocą custom type codecs usuwa powtarzalną logikę parsowania i zapewnia, że formaty twoich danych pozostają idealnie zsynchronizowane w całej twojej aplikacji. Dzięki za wysłuchanie, udanego kodowania wszystkim!
7

Zaawansowane kodeki z PostGIS

3m 53s

Zanurz się w niestandardowe kodeki typów, mapując typy geometryczne PostGIS z PostgreSQL na obiekty Shapely w Pythonie przy użyciu formatu binarnego.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny Python Async, odcinek 7 z 17. Obsługa współrzędnych geograficznych zazwyczaj oznacza uciążliwe parsowanie stringów. Piszesz query, dostajesz z powrotem gigantyczny string z liczbami, a potem palisz cykle procesora na rozkładanie go na części, tylko po to, żeby znaleźć szerokość i długość geograficzną. Zaawansowane kodeki z PostGIS w asyncpg całkowicie rozwiązują ten problem. Kiedy wyciągasz customowe typy z PostgreSQL, masz do czynienia z kodekami. Kodek mówi asyncpg, jak przetłumaczyć typ danych PostgreSQL na obiekt Pythona. Często pojawia się tu zamieszanie między formatem tekstowym a binarnym. Format tekstowy jest domyślny dla wielu narzędzi bazodanowych. W PostGIS tekstowe query zwraca format Well-Known Text. Wygląda to jak słowo POINT, po którym idą współrzędne w nawiasach. Jest to czytelne dla człowieka, ale odczytanie tego w kodzie wymaga alokowania stringów, szukania nawiasów i rzutowania znaków na floaty. Parsowanie tekstu jest wolne i słabo się skaluje, kiedy przetwarzasz tysiące wierszy. Chcesz formatu binarnego. PostGIS używa standardu o nazwie Well-Known Binary. Konfigurując swój kodek w asyncpg, jawnie ustawiasz argument format na binary. Baza danych pomija generowanie tekstu i przekazuje surowe bajty. Teraz potrzebujesz sposobu na przetłumaczenie tych bajtów na coś, co twoja aplikacja w Pythonie będzie mogła faktycznie wykorzystać. I tu wkracza biblioteka Pythona, taka jak Shapely. Shapely obsługuje złożoną geometrię i doskonale wie, jak czytać Well-Known Binary. Mówisz asyncpg, żeby użył customowego kodeka, wywołując metodę set type codec bezpośrednio na swoim połączeniu z bazą danych. Podajesz nazwę typu w PostgreSQL, czyli geometry. Następnie przekazujesz funkcję encodera i funkcję decodera. Decoder bierze surowy byte string z PostgreSQL i przekazuje go bezpośrednio do binarnego readera w Shapely. Wyobraź sobie, że robisz query o lokalizację Empire State Building. Bez customowego binarnego kodeka, twoja baza danych zwraca string, twoja aplikacja go parsuje, buduje słownik i ostatecznie tworzy obiekt geometryczny. Z binarnym kodekiem na miejscu, wykonujesz standardowy select. Asyncpg przechwytuje dane binarne, odpala twoją funkcję decodera i natychmiast przekazuje ci w pełni uformowany obiekt Shapely Point. Możesz od razu uzyskać dostęp do współrzędnych x i y na zwróconym obiekcie. Ten proces działa w drugą stronę dla danych wracających do bazy danych. Twoja funkcja encodera przygotowuje dane z Pythona do wysłania do PostgreSQL. Obiekty Shapely implementują standard o nazwie geo interface. To popularna struktura słownika w Pythonie używana do geometrii. Twój encoder bierze dowolny obiekt w Pythonie, który wspiera ten interfejs, używa Shapely, żeby zserializować go do Well-Known Binary, i wysyła te surowe bajty z powrotem do bazy danych. Nigdy nie dotykasz reprezentacji tekstowej. Jeśli uważasz te deep dive'y za przydatne, możesz wesprzeć podcast, wyszukując DevStoriesEU na Patreonie. Oto kluczowy wniosek. Używając wyłącznie formatu binarnego dla customowych kodeków typów, eliminujesz wąskie gardło serializacji, pozwalając twojej bazie danych i aplikacji w Pythonie na komunikację z prędkością pamięci. Dzięki za wysłuchanie, udanego kodowania wszystkim!
8

Zarządzanie transakcjami

3m 27s

Opanuj transakcje bazodanowe w asyncpg. Omawiamy zachowanie auto-commit oraz bezpieczne wykonywanie wielu zapytań przy użyciu asynchronicznych menedżerów kontekstu.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny async w Pythonie, odcinek 8 z 17. Wykonujesz update query, a potem twój skrypt crashuje przed uruchomieniem kolejnego query. Sprawdzasz bazę danych, spodziewając się, że nic się nie zmieniło, ale pierwszy update już tam jest, trwale zapisany. W asyncpg, jeśli jawnie nie otworzysz transakcji, każde pojedyncze query jest commitowane w milisekundę po swoim zakończeniu. Zarządzanie transakcjami to sposób, w jaki naprawiasz to zachowanie. Domyślnie asyncpg działa w trybie auto-commit. Oznacza to, że jeśli napiszesz kod, który wykonuje trzy query jedno po drugim, nie uruchamiasz jednego bloku logiki. Uruchamiasz trzy całkowicie odizolowane operacje. Jeśli drugie query się wywali, pierwsze query jest już sfinalizowane w bazie danych. Brak jawnego bloku transakcji to częsta przyczyna uszkodzonego stanu aplikacji. Weźmy scenariusz, w którym przelewasz pieniądze między dwoma kontami bankowymi. Musisz odjąć środki z konta A, a następnie dodać dokładnie te same środki do konta B. Oba update'y muszą się powieść razem, albo oba muszą razem zakończyć się błędem. Jeśli odjęcie środków się powiedzie, ale dodanie zakończy się błędem, pieniądze po prostu znikają. Aby powiązać te operacje ze sobą, używasz metody transaction na swoim obiekcie connection. Ta metoda zwraca asynchroniczny context manager. W swoim kodzie piszesz async with connection dot transaction, po czym stawiasz dwukropek, a następnie robisz wcięcie dla powiązanych query. Kiedy Python wchodzi do tego bloku, asyncpg mówi bazie PostgreSQL, żeby wystartowała nową transakcję. Wewnątrz bloku wykonujesz query odejmujące, a po nim query dodające. Jeśli oba query wykonają się bez problemu i Python dotrze do końca bloku, asyncpg automatycznie wyda komendę commit. Zmiany na obu kontach stają się widoczne dla reszty bazy danych dokładnie w tym samym momencie. Oto kluczowa sprawa. Jeśli wewnątrz tego bloku wystąpi jakikolwiek problem, baza danych pozostaje bezpieczna. Problemem może być naruszenie constraintu bazy danych, network timeout, a nawet czysty błąd Pythona, taki jak brakująca zmienna czy dzielenie przez zero. Jeśli zostanie rzucony exception, context manager go przechwytuje. Automatycznie wysyła komendę rollback do PostgreSQL, cofając odjęcie środków z konta A, a następnie pozwala, by exception Pythona propagował się w górę twojego call stacka. Możesz również bezpiecznie zagnieżdżać te bloki. Jeśli otworzysz nowy context manager transakcji, będąc już wewnątrz aktywnego bloku transakcji, asyncpg się nie pogubi. Zamiast tego automatycznie tworzy savepoint w bazie danych. Savepoint działa jak zakładka w trwającej transakcji. Jeśli wewnętrzny blok napotka błąd, robi rollback stanu bazy danych tylko do tej zakładki. Zewnętrzny blok pozostaje całkowicie nienaruszony i nadal może zcommitować swoją własną pracę, albo zdecydować się na błąd w zależności od twojej logiki. Nie musisz pisać ręcznych komend savepoint, po prostu zagnieżdżasz swoje bloki async with. Ostatecznie, context manager transakcji trwale wiąże twój stan bazy danych ze stanem wykonania Pythona, zapewniając, że nieobsłużony exception Pythona jest absolutną gwarancją braku częściowych update'ów bazy danych. To wszystko w tym odcinku. Dzięki za wysłuchanie i koduj dalej!
9

Pula połączeń

3m 31s

Skaluj swoją aplikację dzięki wbudowanej puli połączeń w asyncpg. Dowiedz się, jak efektywnie zarządzać połączeniami z bazą danych w usługach sieciowych o dużym natężeniu ruchu.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny async w Pythonie, odcinek 9 z 17. Otwieranie nowego połączenia z bazą danych dla każdego przychodzącego requestu to bardzo skuteczny sposób na całkowite zcrashowanie serwera. Sam network overhead zatrzyma twoją aplikację, a ty szybko wyczerpiesz limit połączeń w bazie danych. Rozwiązaniem tego problemu jest Connection Pooling. Kiedy używasz biblioteki takiej jak asyncpg do komunikacji z PostgreSQL, nawiązanie surowego połączenia to kosztowna operacja. Wymaga to TCP handshake'u, bezpiecznej negocjacji i uwierzytelnienia w bazie danych. Jeśli prowadzisz web serwis o dużym natężeniu ruchu, po prostu nie możesz sobie pozwolić na płacenie tego podatku od opóźnień przy każdym pojedynczym requeście HTTP. Zamiast tego musisz utrzymywać stałą pulę gotowych do użycia połączeń. W asyncpg osiągasz to za pomocą funkcji create pool. Zazwyczaj wywołujesz tę funkcję raz podczas fazy uruchamiania aplikacji. Podajesz credentials do bazy danych, host i port, a asyncpg odpala w tle zestaw bezczynnych połączeń. Od tego momentu twoje route handlery i background taski nigdy nie tworzą nowego połączenia od zera. One jedynie pożyczają te już istniejące. Jest tu pewna częsta pułapka, w którą wpada wielu programistów. Nie myl pożyczania połączenia z otwieraniem transakcji w bazie danych. To całkowicie oddzielne operacje. Kiedy pożyczasz połączenie z puli, rezerwujesz socket sieciowy tylko do swojego wyłącznego, tymczasowego użytku. Jeśli twoja operacja wymaga atomowości w wielu zapytaniach, nadal musisz jawnie wystartować transakcję na tym konkretnym, pożyczonym połączeniu. Pomyśl o web serwisie w FastAPI lub aiohttp o dużym natężeniu ruchu. Załóżmy, że masz endpoint, który przyjmuje integera, odpytuje bazę danych, aby obliczyć potęgę dwójki dla tej liczby i zwraca wynik. Gdy request uderza w twój endpoint, wywołujesz metodę acquire na obiekcie puli. Robisz to za pomocą asynchronicznego context managera. To tymczasowo pobiera jedno połączenie z puli. Następnie używasz tej konkretnej instancji połączenia do wykonania zapytania do bazy danych, aby obliczyć potęgę dwójki. Po zakończeniu bloku kodu, context manager automatycznie zwalnia połączenie. Czyści jego stan i oddaje je z powrotem do puli, dzięki czemu jest natychmiast dostępne dla następnego przychodzącego requestu HTTP. Jeśli nagły skok ruchu uderzy w twój serwer, a wszystkie połączenia w puli są aktualnie zajęte, kolejny request nie zcrashuje. Będzie czekać. Wywołanie acquire po prostu wstrzyma wykonywanie kodu, dopóki inny request się nie zakończy i nie zwróci swojego połączenia. Oto kluczowy wniosek. Connection pool nie tylko oszczędza czas na sieciowych handshake'ach. Działa jak ścisły i niezawodny ogranicznik współbieżności. Chroni twoją bazę PostgreSQL przed przeciążeniem przez nieoczekiwany zalew ruchu. Jeśli skonfigurujesz swoją pulę tak, aby trzymała dokładnie dwadzieścia połączeń, baza danych nigdy nie zobaczy więcej niż dwadzieścia równoczesnych, aktywnych zapytań z tej instancji aplikacji, niezależnie od tego, ile tysięcy jednoczesnych requestów uderzy w twój serwer. Dzięki za wysłuchanie. Do następnego razu!
10

Buforowanie przygotowanych zapytań

4m 21s

Zrozum, jak asyncpg optymalizuje parsowanie zapytań dzięki automatycznym przygotowanym zapytaniom i dlaczego zewnętrzne pule, takie jak PgBouncer, mogą powodować konflikty.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. High-Performance Python Async, odcinek 10 z 17. Twoja aplikacja działa idealnie na twojej lokalnej maszynie. Ale w momencie, gdy robisz deploy na produkcję za routerem połączeń z bazą danych, zaczynasz widzieć losowe crashe. Logi narzekają na statementy, które już istnieją, albo na statementy, których nie można znaleźć. Nic w twoim kodzie się nie zmieniło. Dzieje się tak z powodu wbudowanej optymalizacji o nazwie Prepared Statement Caching. Żeby zrozumieć ten błąd, musimy najpierw przyjrzeć się, co asyncpg robi pod spodem. Za każdym razem, gdy wysyłasz query przez asyncpg, biblioteka automatycznie tłumaczy je na prepared statement na serwerze PostgreSQL. Normalnie, gdy baza danych otrzymuje query, musi sparsować tekst, przeanalizować składnię i zbudować execution plan. To zajmuje czas. Przygotowując taki statement, PostgreSQL wykonuje tę ciężką robotę dokładnie raz i przypisuje mu wewnętrzną nazwę. Przy wszystkich kolejnych wykonaniach dokładnie tego samego query, asyncpg wysyła tylko nowe parametry i nazwę statementu. To całkowicie pomija fazę parsowania i daje ogromny boost do wydajności. asyncpg trzyma cache tych prepared statementów w pamięci, ściśle powiązany z aktywnym połączeniem do bazy danych. Oto kluczowa kwestia. Problem wynika z konfliktu między tym, jak asyncpg zarządza swoimi wewnętrznymi połączeniami, a tym, jak działają zewnętrzne poolery, takie jak PgBouncer. asyncpg zakłada, że ma dedykowane, stałe fizyczne połączenie z serwerem Postgres. Kiedy tworzy prepared statement, ufa, że ten statement pozostanie dostępny na dokładnie tym połączeniu, dopóki połączenie nie zostanie zamknięte. Teraz dodaj do architektury PgBouncera, a konkretnie działającego w transaction mode. PgBouncer siedzi między twoją aplikacją a bazą danych. Utrzymuje małą pulę rzeczywistych połączeń do Postgresa i współdzieli je między tysiącami przychodzących requestów od klientów. W transaction mode, PgBouncer daje twojej aplikacji fizyczne połączenie z bazą danych tylko na czas trwania pojedynczej transakcji. W momencie, gdy ta transakcja robi commit, PgBouncer zabiera fizyczne połączenie z powrotem i przekazuje je zupełnie innemu klientowi. To psuje cache prepared statementów. Twoja aplikacja wysyła query. asyncpg przygotowuje je i wrzuca do cache'a na fizycznym połączeniu A. Transakcja się kończy. Kilka sekund później twoja aplikacja wysyła dokładnie to samo query. asyncpg pamięta, że już przygotował to query, więc mówi Postgresowi, żeby wykonał zapisany statement. Ale tym razem PgBouncer przekierował twoją aplikację do fizycznego połączenia B. Połączenie B nie ma żadnego śladu po tym prepared statemencie. Baza danych rzuca błędem, mówiąc, że statement nie istnieje. Działa to też w drugą stronę. Inny klient może zostać przekierowany do połączenia A, spróbować przygotować statement o tej samej wewnętrznej nazwie i wywołać błąd mówiący, że statement już istnieje. Rozwiązanie jest proste, ale wymaga pewnego trade-offu. Musisz powiedzieć asyncpg, żeby wyłączył tę optymalizację. Kiedy inicjalizujesz swoje połączenie asyncpg albo pulę połączeń, przekazujesz konkretny argument ustawiający rozmiar cache'a statementów na zero. To całkowicie wyłącza automatyczny caching prepared statementów. Twoje queries będą teraz parsowane przez PostgreSQL za każdym razem, gdy się wykonają. Poświęcasz trochę wydajności na parsowaniu, ale twoja aplikacja natychmiast stanie się stabilna na rozproszonych połączeniach. Jeśli twoje połączenia z bazą danych są rutowane dynamicznie per transakcja, twoja aplikacja nie może już zakładać, że serwer bazy danych pamięta cokolwiek pomiędzy queries. To wszystko w tym odcinku. Dzięki za wysłuchanie i koduj dalej!
11

Tablice Postgres i klauzule IN

4m 25s

Rozwiąż najczęstszy błąd składniowy podczas migracji do asyncpg. Dowiedz się, jak poprawnie filtrować zapytania względem listy wartości za pomocą ANY().

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny async w Pythonie, odcinek 11 z 17. Piszesz standardowe query SQL, żeby filtrować rekordy. Przekazujesz listę wartości do parametrów query, tak jak robiłeś to w dziesiątkach innych bibliotek baz danych. Postgres natychmiast rzuca syntax error. Problem nie leży w twoich danych. Problemem jest to, jak obsługujesz arraye w Postgresie i klauzule IN. To syntax error numer jeden u developerów, którzy migrują do asyncpg z innych interfejsów baz danych. W wielu starszych bibliotekach driver przechwytuje twój query string. Jeśli przekażesz pythonową listę do klauzuli IN, biblioteka przepisuje string SQL w locie. Rozwija twoją listę w oddzielony przecinkami string pojedynczych parametrów, zanim wyśle go do bazy danych. Asyncpg tego nie robi. Opiera się wyłącznie na natywnych, serwerowych prepared statements w Postgresie. Oto kluczowa sprawa. W standardowym SQL-u Postgresa, operator IN wymaga oddzielonej przecinkami listy wartości skalarnych, ujętej w nawiasy. Nie akceptuje pojedynczego obiektu typu array. Kiedy przekazujesz pythonową listę do asyncpg jako parametr, asyncpg mapuje tę listę bezpośrednio na natywny array w Postgresie. Twoje query ostatecznie ewaluuje się jako wyrażenie IN z obiektem array, co jest nieprawidłowym syntaxem. Postgres oczekuje wyrażenia IN wartość jeden, wartość dwa. Żeby to naprawić, musisz przestać używać operatora IN dla sparametryzowanych list. Zamiast tego, użyj funkcji any z Postgresa. Logika zmienia się z pytania, czy wartość jest w liście, na pytanie, czy wartość równa się jakiemukolwiek elementowi wewnątrz arraya. Operator any został zaprojektowany specjalnie do pracy z typami array w Postgresie. Ewaluuje wartość po lewej stronie, sprawdza array po prawej i zwraca true, jeśli znajdzie dopasowanie. Musisz też powiedzieć Postgresowi, jaki rodzaj arraya otrzymuje, rzutując parametr. Jeśli oczekujesz arraya text stringów, rzutujesz swój parametr jawnie na text array. Takie jawne rzutowanie typów gwarantuje, że Postgres wie dokładnie, jak zaplanować i wykonać query, bez zgadywania bazowego typu danych przychodzącego strumienia binarnego. Wyobraź sobie scenariusz, w którym filtrujesz listę produktów. Chcesz dopasować kategorię produktu do dynamicznie dostarczanej listy kategorii wybranych przez użytkownika. Piszesz query, żeby wybrać produkty, gdzie kategoria równa się any parametr jeden, i rzutujesz parametr jeden na text array. W swoim kodzie w Pythonie wywołujesz metodę fetch na bazie danych. Przekazujesz query string jako pierwszy argument, a twoją pythonową listę stringów — takich jak elektronika i książki — jako drugi argument. Asyncpg pakuje twoją pythonową listę w binarny text array Postgresa i wysyła przez sieć jako pojedynczy parametr. Postgres odbiera query, widzi text array i wydajnie dopasowuje kategorie używając funkcji any. To podejście jest zdecydowanie lepsze niż manipulacja stringami. Ponieważ struktura query nigdy się nie zmienia, niezależnie od tego, ile kategorii jest na liście, Postgres parsuje i planuje statement dokładnie raz. Baza danych cache'uje query plan, oszczędzając czas wykonania przy kolejnych wywołaniach. Eliminujesz też ryzyko SQL injection, ponieważ dane są przesyłane w formie binarnej, całkowicie oddzielone od tekstu query. Jeśli przekazujesz pythonową listę do parametru bazy danych, traktuj ją jak natywny array, rzutuj na odpowiedni typ i ewaluuj za pomocą funkcji any. To wszystko w tym odcinku. Dzięki za wysłuchanie i budujcie dalej!
12

Obiekty Record a Named Tuples

4m 18s

Poznaj unikalny projekt obiektów Record w asyncpg. Zrozum, dlaczego notacja kropkowa jest domyślnie pomijana i jak ją włączyć za pomocą niestandardowych klas.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny async w Pythonie, odcinek 12 z 17. Robisz zapytanie do bazy danych, dostajesz z powrotem wiersz i instynktownie wpisujesz nazwę zmiennej kropka id. Python natychmiast rzuca AttributeError. Twoje dane tam są, ale nie możesz się do nich dostać w oczekiwany sposób. Dzieje się tak przez celowy design obiektów Record w odróżnieniu od named tuples. Kiedy pobierasz dane używając asyncpg, nie zwraca on standardowych słowników Pythona ani named tuples. Zamiast tego zwraca wysoce zoptymalizowany customowy obiekt o nazwie Record. Jeśli jesteś przyzwyczajony do innych sterowników baz danych lub ORM-ów, możesz oczekiwać, że dot-notation zadziała out of the box. Przy obiekcie Record z asyncpg, to celowo nie działa. Możesz się zastanawiać, dlaczego sterownik nie używa po prostu standardowego named tuple z Pythona, skoro named tuples natywnie wspierają dot-notation. Powodem jest czysta wydajność. Named tuple wymaga od Pythona wygenerowania zupełnie nowej struktury klasy dla każdej unikalnej kombinacji kolumn zwróconej przez query. Jeśli twoja aplikacja wykonuje setki różnie zbudowanych queries, generowanie tych dynamicznych klas tworzy ogromny overhead. Biblioteka jest zbudowana dla absolutnej szybkości, więc całkowicie omija ten bottleneck, zwracając zamiast tego swój własny, skompilowany typ Record. Ten customowy obiekt Record działa jak szybka hybryda. Wspiera indeksowanie integerami dokładnie tak jak standardowy tuple, co oznacza, że możesz dostać się do pierwszej kolumny używając indeksu zero. Ale zapewnia też mapowanie podobne do słownika. Do kolumn dostajesz się używając bracket notation, przekazując nazwę kolumny jako string. I tu jest kluczowa sprawa. Twórcy celowo wyłączyli dot-notation na tych obiektach, aby chronić twoją aplikację przed konfliktami namespace'ów. Pomyśl o standardowych metodach mapowania w Pythonie, takich jak keys, items, values czy get. Jeśli twoja tabela w bazie danych akurat ma kolumnę o nazwie keys, a sterownik wspierałby dot-notation, wpisanie record kropka keys stworzyłoby konflikt strukturalny. Python nie wiedziałby, czy chcesz wartość z bazy danych, czy wbudowaną metodę. Wymuszając bracket notation opartą na stringach, sterownik gwarantuje, że nazwy twoich kolumn nigdy nie wejdą w kolizję ze standardowymi atrybutami Pythona. Jeśli jednak masz pełną kontrolę nad swoim schematem bazy danych, wiesz na pewno, że nie używasz zarezerwowanych słów Pythona jako nazw kolumn i wymagasz dot-notation w swoim codebase, masz wyjście z tej sytuacji. Możesz nadpisać domyślne zachowanie. Kiedy nawiązujesz połączenie z bazą danych, możesz podać konkretny parametr o nazwie record_class. Aby to zaimplementować, piszesz customową klasę, która dziedziczy bezpośrednio z bazowego Record w asyncpg. Wewnątrz tej nowej klasy implementujesz wbudowaną metodę Pythona o nazwie podwójne podkreślenie getattr. Instruujesz tę metodę, aby wzięła żądaną nazwę atrybutu i po prostu wyszukała ją używając bezpiecznego fallbacku w postaci bracket notation. Kiedy przekażesz tę customową klasę do konfiguracji połączenia, każdy pojedynczy wiersz zwrócony przez twoje queries będzie instancją twojego customowego obiektu. Python pozwoli ci wtedy na użycie dot-notation, płynnie kierując żądanie atrybutu przez twoją customową metodę, aby pobrać dane z kolumny pod spodem. Ostatecznie, ścisła bracket notation w domyślnym obiekcie Record nie jest przeoczeniem, ale strukturalną granicą bezpieczeństwa, która zapewnia, że twój dostęp do danych pozostanie przewidywalny, niezależnie od tego, jak zmieni się twój schemat bazy danych. To wszystko w tym odcinku. Dzięki za wysłuchanie i koduj dalej!
13

Strumieniowanie wyników za pomocą kursorów

4m 30s

Zapobiegaj wyczerpaniu pamięci podczas zapytań do ogromnych zbiorów danych. Dowiedz się, jak używać kursorów asyncpg do strumieniowania wyników fragment po fragmencie.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny async w Pythonie, odcinek 13 z 17. Musisz wyeksportować tabelę użytkowników z milionem wierszy do pliku, więc odpalasz standardowego fetcha z bazy danych. Nagle twoja aplikacja w Pythonie zżera cały dostępny RAM i serwer crashuje. Nie możesz zaciągnąć ogromnych datasetów do pamięci za jednym zamachem, i właśnie dlatego używamy streamowania wyników za pomocą kursorów. Zazwyczaj, kiedy wykonujesz query w asyncpg, cały result set jest pobierany przez sieć z PostgreSQL i trzymany w pamięci Pythona. Baza danych buduje kompletną odpowiedź, wysyła ją, a asyncpg konstruuje obiekty Pythona dla każdego pojedynczego wiersza, zanim twój kod zdąży przetworzyć choćby pierwszy z nich. To jest całkowicie okej przy pięćdziesięciu wierszach. Ale staje się natychmiastowym problemem przy pięciu milionach. Aby uniknąć crasha serwera, musisz pobierać dane małymi porcjami, zamiast łykać je w całości. Kursor bazy danych daje ci wskaźnik do wyników query na serwerze PostgreSQL. Zamiast zaciągać wszystko, kursor pozwala twojej aplikacji w Pythonie na fetchowanie danych przyrostowo. W asyncpg robisz to, wywołując metodę cursor na swoim connection. Przekazujesz swoje query SQL i wszystkie wymagane argumenty. Ta metoda zwraca asynchroniczny iterator. Piszesz pętlę async-for, aby iterować po tym kursorze. Pod spodem, asyncpg automatycznie fetchuje wiersze z PostgreSQL w małych, łatwych do zarządzania batchach. Twój kod przetwarza kilka wierszy, zapisuje je do pliku eksportu i leci dalej. Python czyści stare wiersze z pamięci, co sprawia, że całkowity memory footprint twojej aplikacji pozostaje zupełnie płaski, niezależnie od tego, jak duża jest tabela. Jest tu jednak pewna ścisła zasada, na której wykłada się wielu developerów. Jeśli spróbujesz iterować po kursorze bezpośrednio na standardowym connection, asyncpg natychmiast rzuci błędem InterfaceError. Komunikat błędu powie ci, że kursorów nie można używać poza transakcją. Oto kluczowa sprawa. Kursory w PostgreSQL są strukturalnie powiązane z transakcjami w bazie danych. Kiedy transakcja robi commit lub rollback, PostgreSQL niszczy wszystkie aktywne kursory, które są z nią powiązane. Domyślnie asyncpg działa w trybie auto-commit. To oznacza, że każde pojedyncze query, które odpalasz, jest opakowane we własną, malutką, niewidoczną transakcję, która zamyka się w momencie zakończenia tego query. Gdyby asyncpg pozwalał ci na otwarcie kursora w trybie auto-commit, ta niejawna transakcja zakończyłaby się natychmiast, a PostgreSQL ubiłby twój kursor, zanim zdążyłbyś sfetchować choćby jeden wiersz. Aby kursory zadziałały, musisz jawnie zarządzać granicami transakcji. Robisz to, otwierając asynchroniczny context manager za pomocą metody transaction na swoim connection. Kiedy jesteś już bezpiecznie wewnątrz tego bloku transakcji, wywołujesz metodę cursor i odpalasz swoją pętlę async-for. Ponieważ transakcja pozostaje otwarta przez cały czas trwania bloku context managera, twój kursor pozostaje żywy na serwerze PostgreSQL, pozwalając ci na bezpieczne streamowanie całego miliona wierszy. Istnieje jeden rzadki wyjątek od tej reguły. PostgreSQL wspiera feature, w którym możesz zadeklarować surowy kursor SQL za pomocą frazy WITH HOLD. To mówi silnikowi bazy danych, aby zmaterializował wynik i utrzymał kursor przy życiu nawet po zakończeniu transakcji. Zrobienie tego zżera zasoby bazy danych i omija wydajność standardowego streamowania. Dla prawie wszystkich zadań streamowania w asyncpg, jawny blok transakcji jest wymaganym podejściem. Jeśli uważasz te odcinki za przydatne i chcesz wesprzeć podcast, wyszukaj DevStoriesEU na Patreonie. Pamiętaj, że kursor przekształca twoją interakcję z bazą danych z ogromnej, ryzykownej alokacji pamięci w kontrolowany, trwały pipeline, który może bezpiecznie przetworzyć dowolną ilość danych. To wszystko w tym odcinku. Dzięki za wysłuchanie i koduj dalej!
14

Błyskawiczne ładowanie danych za pomocą COPY

3m 42s

Przyspiesz swoje potoki ładowania danych. Analizujemy protokół COPY w PostgreSQL, aby masowo ładować dane nieporównywalnie szybciej niż za pomocą instrukcji INSERT.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. High-Performance Python Async, odcinek 14 z 17. Musisz przenieść milion wierszy do lub z bazy danych. Jeśli twoim pierwszym odruchem jest zbudowanie ogromnej listy statementów insert albo pobranie wszystkich wierszy do wielkiej listy w Pythonie, wybierasz najwolniejszą dostępną ścieżkę. Istnieje mechanizm stworzony specjalnie po to, żeby ominąć ten overhead. Dzisiaj porozmawiamy o błyskawicznym ładowaniu danych za pomocą COPY. COPY to specyficzny dla Postgresa protokół do masowego transferu danych. Kiedy odpalasz standardowe statementy insert lub select, Postgres musi sparsować zapytanie, zaplanować je i wykonać. Robienie tego wielokrotnie generuje ogromny overhead. Protokół COPY całkowicie pomija standardowy pipeline zapytań. Otwiera bezpośredni stream do warstwy storage'u, przenosząc dane w mocno zoptymalizowanym formacie. Dzięki temu jest o rzędy wielkości szybszy niż bulk inserty. W asyncpg wrzucasz dane do bazy za pomocą metody copy to table. Podajesz docelową nazwę tabeli i źródło danych. Tym źródłem może być lokalna ścieżka do pliku, obiekt file-like lub asynchroniczny iterator zwracający rekordy. Jeśli wskażesz mu lokalny plik CSV, Postgres natywnie ogarnie parsowanie. Nie musisz otwierać pliku w Pythonie, parsować wierszy i mapować ich na zmienne. Driver bazy danych streamuje surowe bajty pliku bezpośrednio do serwera. Możesz też przekazać zwykłą pythonową listę krotek, jeśli twoje dane są już w pamięci, a asyncpg zestreamuje je za pomocą protokołu COPY pod spodem. Wyciąganie danych jest równie szybkie. Jeśli potrzebujesz pełnego eksportu, używasz copy from table. Bierze to całą zawartość tabeli i wyrzuca ją do pliku lub streamu. Jednak dumpowanie całej tabeli to rzadko to, czego faktycznie potrzebujesz. Zazwyczaj potrzebujesz przefiltrowanych lub zjoinowanych danych. I tu do gry wchodzi copy from query. Częstym błędem jest myślenie, że ta metoda dumpuje wyniki zapytania tylko do statycznego pliku. To po prostu nieprawda. Choć może ona zapisywać bezpośrednio do ścieżki pliku, możesz też przekazać funkcję callback. Asyncpg wykona zapytanie i zestreamuje wyniki w chunkach do twojego callbacka, pozwalając ci przetwarzać ogromny dataset w locie, bez trzymania całego result setu w pamięci systemowej. Wyobraź sobie scenariusz, w którym musisz wygenerować raport CSV wszystkich aktywnych użytkowników. Standardowe podejście to wykonanie zapytania select, pobranie stu tysięcy wierszy do Pythona, sformatowanie ich przy użyciu modułu CSV i zapisanie na dysk. To zżera mnóstwo pamięci i CPU. I to jest kluczowe. Możesz całkowicie pominąć przetwarzanie w Pythonie. Wywołujesz copy from query, przekazujesz mu swój statement select, ustawiasz parametr format na CSV i podajesz ścieżkę do pliku wyjściowego. Postgres wykonuje zapytanie, formatuje wyniki do CSV natywnie na serwerze bazy danych, a asyncpg streamuje gotowy tekst prosto na twój dysk. Twoja aplikacja w Pythonie działa jak zwykły pipe, praktycznie nie manipulując danymi. Powinieneś dalej używać standardowych statementów insert i select do codziennej logiki aplikacji, ale w momencie, gdy wolumen surowych danych staje się twoim bottleneckiem, przełącz się na protokół COPY, żeby całkowicie ominąć parser SQL. To wszystko w tym odcinku. Dzięki za wysłuchanie i twórz dalej!
15

Asynchroniczne Listen i Notify

3m 40s

Odblokuj architektury sterowane zdarzeniami w czasie rzeczywistym bezpośrednio w PostgreSQL. Dowiedz się, jak używać add_listener w asyncpg do natychmiastowego przesyłania wiadomości pub/sub.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny async w Pythonie, odcinek 15 z 17. Sięgasz po osobnego message brokera, takiego jak Redis, w momencie, gdy potrzebujesz eventów w czasie rzeczywistym. Ale jeśli twoja aplikacja korzysta już z bazy danych, być może bez powodu zwiększasz złożoność infrastruktury. PostgreSQL ma wbudowanego w siebie message busa działającego w czasie rzeczywistym. Dzisiaj porozmawiamy o asynchronicznym listen i notify. Postgres natywnie obsługuje wzorzec publish-subscribe, używając dwóch komend: listen i notify. Biblioteka asyncpg udostępnia tę funkcję w Pythonie za pomocą metody o nazwie add listener. Zamiast pisać pętlę, która co kilka sekund odpytuje tabelę w bazie danych, żeby sprawdzić nowe dane, rejestrujesz asynchroniczny callback w Pythonie na konkretnym, nazwanym kanale. Gdy w Postgresie wystąpi event, rozgłasza on wiadomość na ten kanał, a twój pythonowy callback wykonuje się natychmiast. Oto kluczowa sprawa. Listener nie jest podpięty do twojej aplikacji globalnie i nie jest podpięty do connection poola. Jest on przypisany do jednego, konkretnego, indywidualnego połączenia z bazą danych. To częsty punkt awarii. Jeśli pobierzesz połączenie z poola, zarejestrujesz swój listener, a następnie zwrócisz połączenie z powrotem do poola, listener przepada. Aby niezawodnie korzystać z tej funkcji, musisz pobrać połączenie z asyncpg, wywołać na nim metodę add listener i trzymać to połączenie otwarte w nieskończoność. Staje się ono dedykowanym pipelinem do nasłuchiwania. Spójrzmy na praktyczny scenariusz. Masz background workera, który musi się obudzić i przetworzyć rekordy za każdym razem, gdy nowy wiersz zostanie wstawiony do tabeli jobs. Zamiast pollingu, ustawiasz trigger w bazie danych. Przy każdym insercie, trigger wykonuje komendę notify na kanale o nazwie new jobs. Wysyła również krótki text payload, na przykład unikalny identyfikator nowego wiersza. W swoim kodzie w Pythonie piszesz asynchroniczny callback. Funkcja ta oczekuje czterech argumentów od asyncpg: obiektu połączenia, identyfikatora procesu Postgresa, nazwy kanału i samego text payloadu. Następnie pobierasz swoje dedykowane połączenie. Wywołujesz add listener na tym połączeniu, przekazując string kanału new jobs wraz ze swoim callbackiem. Na koniec podtrzymujesz działanie skryptu, zazwyczaj używając await na asynchronicznym evencie, który nigdy nie zostaje ustawiony. Twój proces w Pythonie jest teraz całkowicie bezczynny. Zużywa prawie zero zasobów procesora i przestaje bombardować bazę danych pustymi zapytaniami. W momencie, gdy transakcja commituje nowy wiersz w tabeli jobs, Postgres wypycha powiadomienie przez otwarty socket sieciowy. Asyncpg odczytuje ten socket i natychmiast planuje twój callback w pythonowym event loopie, przekazując mu identyfikator nowego joba. Prawdziwą siłą tego wzorca jest spójność transakcyjna. Jeśli transakcja w bazie danych zrobi rollback, wszystkie komendy notify wykonane podczas tej transakcji są automatycznie odrzucane przez Postgresa. To gwarantuje, że twoje pythonowe workery będą się budzić i reagować tylko na dane, które zostały pomyślnie zapisane na dysku. To wszystko w tym odcinku. Dzięki za słuchanie i buduj dalej!
16

Telemetria i logowanie zapytań

4m 42s

Zyskaj głęboką obserwowalność wydajności swojej bazy danych. Odkryj, jak używać listenerów logów w asyncpg do śledzenia wolnych zapytań i monitorowania telemetrii wykonania.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Wysokowydajny async w Pythonie, odcinek 16 z 17. Aby dowiedzieć się, dlaczego twoja baza danych działa wolno, nie musisz zgadywać. Możesz polegać na logach po stronie serwera, ale nie uwzględniają one opóźnień sieciowych i overheadu po stronie Pythona. Ręczne mierzenie wydajności przy każdym wywołaniu bazy danych szybko zaśmieca twoją logikę biznesową. Rozwiązaniem jest telemetria i query logging. Ludzie często mylą log listenery z query loggerami. Log listenery przechwytują powiadomienia z serwera Postgres. Query loggery przechwytują telemetrię z wykonania po stronie klienta. Obsługują dwa zupełnie różne strumienie informacji. Query loggery obsługują telemetrię. Podpinasz callback do swojego połączenia, używając metody add query logger. Po podpięciu, za każdym razem, gdy zapytanie skończy się wykonywać, asyncpg automatycznie przekazuje obiekt LoggedQuery do tego callbacku. Dzieje się to globalnie dla tego połączenia, w pełni w oderwaniu od konkretnej funkcji, która robi request do bazy danych. Obiekt LoggedQuery zawiera trzy kluczowe informacje. Przechowuje dokładny tekst zapytania SQL, argumenty przekazane do tego zapytania oraz czas wykonania. Argumenty są przechwytywane dokładnie w takiej postaci, w jakiej przekazała je twoja aplikacja. Dzięki temu nie musisz ręcznie pisać logiki formatowania stringów tylko po to, żeby dowiedzieć się, jakie parametry spowodowały wolną odpowiedź. Wyobraź sobie środowisko produkcyjne, w którym musisz wyłapać każde zapytanie trwające dłużej niż 500 milisekund. Definiujesz standardową funkcję w Pythonie, która przyjmuje dwa parametry: połączenie z bazą danych i obiekt LoggedQuery. Wewnątrz tej funkcji sprawdzasz atrybut czasu wykonania. Jeśli czas przekroczy pół sekundy, zapisujesz tekst zapytania, argumenty i dokładny czas trwania do systemu monitorowania twojej aplikacji. Następnie przekazujesz ten callback do metody add query logger. Teraz twoja aplikacja automatycznie i po cichu śledzi wolne zapytania w tle. Jeśli kiedykolwiek będziesz musiał zatrzymać to śledzenie, po prostu przekazujesz ten sam callback do metody remove query logger. Teraz druga część, czyli log listener. Podczas gdy query logger zajmuje się mierzeniem czasu po stronie klienta, log listener obsługuje komunikaty po stronie serwera. Czasami Postgres wysyła komunikaty, które nie są błędami i nie zwracają wierszy z danymi. Są to asynchroniczne powiadomienia, ostrzeżenia lub customowe logi generowane bezpośrednio przez silnik bazy danych. Aby przechwycić te komunikaty, podpinasz callback za pomocą metody add log listener. Gdy Postgres wyemituje powiadomienie lub ostrzeżenie, asyncpg uruchamia ten callback. Przekazuje połączenie i konkretny obiekt komunikatu do twojej funkcji. Daje to twojej aplikacji natychmiastowy wgląd w ostrzeżenia na poziomie bazy danych, całkowicie niezależnie od standardowych wyników zapytań. Podobnie jak w przypadku query loggera, możesz później odpiąć ten callback, używając metody remove log listener. Oto kluczowy wniosek. Query logging po stronie klienta daje ci prawdziwy czas wykonania odczuwany przez twoją aplikację w Pythonie, całkowicie eliminując zgadywanie, co jest opóźnieniem sieciowym, a co przetwarzaniem w bazie danych. Dzięki za wysłuchanie. Trzymajcie się!
17

Zabezpieczanie połączeń za pomocą SSL

4m 11s

Upewnij się, że Twoje połączenia z bazą danych są bezpieczne. Omawiamy konfigurację kontekstu SSL i wymuszanie bezpośredniego TLS podczas łączenia się z chmurowymi bazami danych.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. High-Performance Python Async, odcinek 17 z 17. Łączenie się z zarządzaną bazą danych w chmurze bez SSL jest jak wykrzykiwanie swoich credentials do bazy na cały zatłoczony pokój. Potrzebujesz szyfrowania, ale jego poprawny setup często prowadzi do mylących błędów połączenia albo niebezpiecznych ustawień domyślnych. Dzisiaj zabezpieczymy połączenia za pomocą SSL w asyncpg. Ludzie często mają problem z tym, jak skonfigurować SSL w swojej logice połączenia. Jeśli używasz connection URIs, asyncpg natywnie parsuje standardowe query parameters PostgreSQL dla sslmode, takie jak ustawienie sslmode na require. To sprawdza się w podstawowych setupach. Ale kiedy potrzebujesz precyzyjnej kontroli — na przykład przy bezpiecznym łączeniu się z zarządzaną bazą w chmurze przy użyciu customowego bundle'a Certificate Authority — standardowe stringi URI to za mało. Do tego używasz programowego parametru ssl. Parametr ssl w funkcjach połączenia asyncpg dyktuje, jak negocjowany jest TLS. Akceptuje on dwa typy wartości. Pierwszy typ to string preset. Możesz przekazać string prefer, który próbuje nawiązać połączenie SSL, ale robi fallback do nieszyfrowanego, jeśli serwer go nie obsługuje. Możesz przekazać require, które wymusza szyfrowanie, ale pomija weryfikację tożsamości serwera. Albo możesz przekazać verify-full, które wymusza szyfrowanie i rygorystycznie waliduje certyfikat serwera względem trusted roots. Oto kluczowa kwestia. Kiedy twój scenariusz wymaga customowego Certificate Authority, nie polegaj na string presetach. Zamiast tego, tworzysz standardowy obiekt SSLContext w Pythonie. Konfigurujesz ten obiekt swoimi customowymi plikami certyfikatów, wymuszasz ścisłą weryfikację, a następnie przekazujesz ten obiekt Pythona bezpośrednio do parametru ssl w asyncpg. Daje ci to dokładną kontrolę nad kryptograficznym handshake'iem, omijając wszelkie domyślne certyfikaty systemowe. To tyle, jeśli chodzi o reguły szyfrowania, ale jak właściwie startuje to połączenie? To prowadzi nas do parametru direct TLS. Domyślnie PostgreSQL używa protokołu o nazwie STARTTLS. Klient nawiązuje połączenie plain-text, pyta serwer, czy ten obsługuje szyfrowanie, i jeśli serwer odpowie twierdząco, robią upgrade połączenia do TLS. Jednak nowoczesne setupy proxy — takie jak niektóre connection poolery czy cloudowe load balancery — często oczekują połączenia direct TLS już od pierwszego bajtu. Nie chcą negocjacji w plain-text. Jeśli twoja infrastruktura jest tak zbudowana, przekazujesz true do parametru połączenia direct TLS. Kiedy to zrobisz, asyncpg pomija negocjację STARTTLS i natychmiast inicjuje surowy TLS handshake. Naturalnie, to zadziała tylko wtedy, gdy dostarczysz też prawidłową konfigurację ssl. Jeśli włączysz direct TLS, ale zostawisz parametr ssl pusty, połączenie zakończy się błędem. Zabezpieczając swoje połączenia z bazą danych, pamiętaj, że chociaż string presety są wygodne, przekazanie jawnego obiektu SSLContext to jedyny sposób, aby absolutnie zagwarantować, że twoja aplikacja ufa właściwej tożsamości serwera. Ponieważ to już ostatni odcinek tej serii, zachęcam cię do przejrzenia oficjalnej dokumentacji asyncpg i wypróbowania tych parametrów połączenia w praktyce. Możesz odwiedzić devstories dot eu, żeby zaproponować tematy do naszych przyszłych serii. To wszystko w tym odcinku. Dzięki za wysłuchanie i buduj dalej!