Înapoi la catalog
Season 13 17 Episoade 1h 4m 2026

High-Performance Python Async

Ediția 2026. O analiză detaliată a accelerării Python asyncio cu uvloop și a interfeței directe cu PostgreSQL folosind protocolul binar al asyncpg.

Python Core Programare asincronă
High-Performance Python Async
Se redă acum
Click play to start
0:00
0:00
1
Nevoia de viteză: Arhitectura uvloop
Descoperă diferențele arhitecturale dintre asyncio standard din Python și uvloop. Explorăm modul în care uvloop folosește Cython și libuv pentru a atinge o performanță similară cu Go.
3m 49s
2
Integrarea uvloop
Învață cum să integrezi uvloop în aplicația ta Python. Acest episod acoperă abordarea EventLoopPolicy pentru a înlocui fără probleme event loop-ul implicit.
3m 50s
3
Introducere în asyncpg: Protocolul binar
Explorează designul fundamental al asyncpg. Discutăm de ce ocolirea DB-API-ului standard în favoarea protocolului binar PostgreSQL aduce creșteri masive de performanță.
3m 35s
4
Conectarea și execuția de bază
Începe să folosești asyncpg conectându-te la o bază de date și rulând interogări de bază. Înțelege sintaxa nativă a argumentelor Postgres.
3m 57s
5
Conversia nativă a tipurilor
Descoperă cum asyncpg mapează automat tipurile de date PostgreSQL la obiecte native Python, eliminând nevoia unei parsări complexe prin ORM.
3m 16s
6
Codecuri de tip personalizate
Învață să definești conversii de date personalizate în asyncpg. Acest episod explică modul de utilizare a set_type_codec pentru a decoda automat JSONB în dicționare Python.
4m 51s
7
Codecuri avansate cu PostGIS
Aprofundează codecurile de tip personalizate mapând tipurile de geometrie PostGIS din PostgreSQL la obiecte Python Shapely folosind formatul binar.
3m 18s
8
Gestionarea tranzacțiilor
Stăpânește tranzacțiile bazei de date în asyncpg. Acoperim comportamentul auto-commit și modul de a executa în siguranță interogări multiple folosind context managers asincroni.
3m 32s
9
Connection Pooling
Scalează-ți aplicația cu funcționalitatea integrată de connection pooling din asyncpg. Învață cum să gestionezi eficient conexiunile la baza de date în serviciile web cu trafic intens.
3m 38s
10
Caching pentru Prepared Statements
Înțelege cum asyncpg optimizează parsarea interogărilor cu prepared statements automate și de ce poolerele externe precum PgBouncer pot cauza conflicte.
4m 08s
11
Array-uri Postgres și clauze IN
Rezolvă cea mai comună eroare de sintaxă la migrarea către asyncpg. Învață cum să filtrezi corect interogările pe baza unei liste de valori folosind ANY().
3m 46s
12
Obiecte Record vs Named Tuples
Explorează designul unic al obiectelor Record din asyncpg. Înțelege de ce dot-notation este omisă implicit și cum să o activezi folosind clase personalizate.
4m 18s
13
Streaming de rezultate cu cursoare
Previne epuizarea memoriei atunci când interoghezi seturi masive de date. Învață cum să folosești cursoarele asyncpg pentru a face streaming de rezultate chunk-by-chunk.
4m 09s
14
Ingerare extrem de rapidă cu COPY
Accelerează-ți pipeline-urile de ingerare a datelor. Explorăm protocolul COPY din PostgreSQL pentru a încărca date în masă exponențial mai rapid decât cu instrucțiuni INSERT.
3m 59s
15
Listen și Notify asincron
Deblochează arhitecturi event-driven în timp real direct în PostgreSQL. Învață cum să folosești add_listener din asyncpg pentru mesagerie pub/sub instantanee.
3m 13s
16
Telemetrie și logarea interogărilor
Obține o observabilitate profundă asupra performanței bazei tale de date. Descoperă cum să folosești log listeners din asyncpg pentru a urmări interogările lente și a monitoriza telemetria de execuție.
3m 42s
17
Securizarea conexiunilor cu SSL
Asigură-te că legăturile la baza ta de date sunt sigure. Acoperim configurarea contextului SSL și modul de a impune TLS direct la conectarea la baze de date în cloud.
3m 42s

Episoade

1

Nevoia de viteză: Arhitectura uvloop

3m 49s

Descoperă diferențele arhitecturale dintre asyncio standard din Python și uvloop. Explorăm modul în care uvloop folosește Cython și libuv pentru a atinge o performanță similară cu Go.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. High-Performance Python Async, episodul 1 din 17. Ce-ar fi dacă ai putea dubla performanța codului tău Python async fără să rescrii nicio funcție? Asta e exact promisiunea tehnologiei la care ne uităm astăzi: arhitectura uvloop. Când developerii vorbesc despre Python async standard, tind să confunde două layere distincte. Primul layer este application programming interface-ul. Aici intră keyword-urile async și await pe care le scrii, future-urile și task-urile. Al doilea layer este event loop-ul în sine. Event loop-ul este scheduler-ul intern care monitorizează socket-urile, gestionează conexiunile de rețea și decide ce task rulează mai departe. Python-ul standard oferă atât interfața, cât și un event loop default scris în pure Python. Aici e ideea de bază. Interfața și event loop-ul sunt decuplate. Python îți permite să schimbi scheduler-ul din spate fără să modifici sintaxa pe care o scrii. Gândește-te la asta ca la o mașină. API-ul async reprezintă volanul, pedalele și bordul. Interacționezi direct cu ele. Event loop-ul este motorul de sub capotă. Să schimbi event loop-ul default din Python cu uvloop e ca și cum ai pune un motor V8 pe mașina ta. Tu continui să virezi și să frânezi exact la fel, dar mașina se mișcă mult mai repede. Core-ul uvloop se bazează pe două alegeri de arhitectură pentru a atinge viteza asta. În primul rând, este un drop-in replacement scris în întregime în Cython. Cython compilează cod Python-like în extensii C extrem de optimizate. Asta elimină overhead-ul interpreterului Python standard atunci când execută hot path-urile din scheduler. Event loop-urile pure Python pierd mult timp creând obiecte interne și gestionând state-ul interpreterului doar ca să rezolve evenimente de rețea de rutină. Cython scapă de toată partea asta. De fiecare dată când loop-ul verifică un socket sau trezește un task, execută cod C nativ în loc să ruteze totul prin pure Python. În al doilea rând, uvloop deleagă interacțiunile efective cu sistemul de operare către o librărie C numită libuv. Dacă numele ăsta îți sună familiar, e pentru că libuv este engine-ul de I/O asincron din spatele Node.js. Este battle-tested, extrem de optimizat pentru workload-uri network-heavy și gestionează toate detaliile complexe cross-platform ale networking-ului asincron. Împachetând libuv într-un shell Cython compact, uvloop aduce exact același profil de performanță direct în Python. Rezultatul arhitectural este masiv. Făcând bypass la scheduler-ul pure Python și bazându-se pe un engine C compilat, uvloop îți face aplicațiile asyncio de cel puțin două ori mai rapide. În multe scenarii de benchmark care implică un concurrency mare al conexiunilor, permite Python-ului să rivalizeze cu performanța limbajelor compilate precum Go. Obții developer velocity-ul din Python cu viteza brută de execuție a networking-ului C nativ. Tranziția necesită zero modificări la business logic-ul tău, la database queries sau la API endpoint-urile tale. Ideea de bază aici este că bottleneck-urile de performanță în Python async standard sunt rareori legate de sintaxa limbajului, ci mai degrabă de engine-ul de execuție, iar înlocuirea acestui engine îți oferă viteze de C nativ, păstrând în același timp abstracțiile Python existente. Dacă vrei să susții podcastul, poți căuta DevStoriesEU pe Patreon. Asta e tot pentru episodul ăsta. Mersi că m-ai ascultat și continuă să construiești!
2

Integrarea uvloop

3m 50s

Învață cum să integrezi uvloop în aplicația ta Python. Acest episod acoperă abordarea EventLoopPolicy pentru a înlocui fără probleme event loop-ul implicit.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. High-Performance Python Async, episodul 2 din 17. Cea mai puternică optimizare de performanță din aplicația ta Python async nu necesită modificări de arhitectură, refactoring și nici configurări complexe. Are exact două linii de cod. Astăzi vorbim despre cum să faci drop-in la uvloop. Event loop-ul asyncio din standard library este scris în Python pur. Uvloop este un drop-in replacement construit peste libuv, același motor care rulează și alte runtime-uri cu concurență ridicată. Face ca tot codul tău standard Python async să se execute semnificativ mai rapid. Înlocuirea mecanismului de scheduling de bază al aplicației tale necesită să-i spui lui Python să abandoneze comportamentul default înainte să înceapă să execute munca propriu-zisă. Într-un script entry point de web server, cum ar fi fișierul main pentru o aplicație FastAPI sau aiohttp, implementezi această înlocuire folosind un event loop policy. Un event loop policy este un obiect de configurare globală în interiorul modulului standard asyncio. El dictează ce fel de event loop este instanțiat de fiecare dată când aplicația cere unul nou. Ca să schimbi loop-ul, imporți modulul asyncio și modulul uvloop. Apoi, apelezi funcția set event loop policy pe modulul asyncio. Îi pasezi o instanță nouă de event loop policy oferită de modulul uvloop. Aici e ideea cheie. Trebuie să setezi acest policy devreme. Apelul trebuie să fie chiar la începutul scriptului tău main de execuție, imediat după importuri. Event loop policy-ul afectează doar crearea de noi loop-uri. Dacă aștepți să setezi policy-ul abia după ce web framework-ul tău a pornit deja, sau după ce un driver asincron de bază de date s-a inițializat, loop-ul standard de Python pur probabil că rulează deja. Schimbarea policy-ului în acel moment nu are niciun efect asupra loop-ului existent. Codul tău fie va ignora uvloop complet, fie va ajunge să aibă event loop-uri mixte care provoacă deadlock-uri și conexiuni întrerupte. Există o alternativă la abordarea cu policy. În loc să schimbi regulile globale pentru crearea de loop-uri, poți crea explicit o singură instanță de uvloop. Faci asta apelând funcția new event loop direct din modulul uvloop. Odată ce ai acel obiect loop în memorie, i-l pasezi lui asyncio apelând funcția set event loop. De ce ai alege o abordare în locul celeilalte? Setarea unui event loop policy este un global override. Garantează că orice third-party library, background task sau componentă de framework din procesul tău care îi cere lui asyncio un nou loop, va primi în siguranță un uvloop. Este alegerea standard pentru un web server unde vrei performanță uniformă pe întregul application stack. Abordarea explicită cu new event loop este mai restrânsă. Injectează o instanță specifică în loc să schimbe regulile de factory. Folosești această metodă explicită atunci când gestionezi medii complexe cu mai multe thread-uri, sau când ai nevoie de un control strict asupra loop-ului exact care rulează într-un context izolat, fără să modifici starea globală a procesului. Pentru aplicațiile web standard, policy override-ul este tot ce ai nevoie. Mecanismul exact pe care îl alegi ca să faci drop-in la uvloop contează mai puțin decât momentul în care îl aplici. Event loop policy-ul dictează fundația întregii tale arhitecturi async, așa că trebuie să fie chiar prima instrucțiune pe care aplicația ta o execută înainte să fie stabilit vreun context asincron. Asta e tot pentru acum. Mulțumesc că m-ai ascultat și continuă să construiești!
3

Introducere în asyncpg: Protocolul binar

3m 35s

Explorează designul fundamental al asyncpg. Discutăm de ce ocolirea DB-API-ului standard în favoarea protocolului binar PostgreSQL aduce creșteri masive de performanță.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Python Async de înaltă performanță, episodul 3 din 17. Îți optimizezi indecșii, faci upgrade la rețea și îți ajustezi query-urile, dar aplicația ta încă arde cicluri de CPU pe comunicarea cu baza de date. Cel mai mare bottleneck în query-urile tale către baza de date nu este, de cele mai multe ori, latența de rețea. Este timpul pe care aplicația ta îl petrece făcând parsing de text. Pentru a elimina acest overhead, îți prezentăm asyncpg și implementarea sa pentru protocolul binar PostgreSQL. E o concepție greșită comună că asyncpg este doar un wrapper asincron peste psycopg2. Nu este. Și nu este nici o adaptare a DB-API-ului standard din Python. Specificația DB-API ghidează în mod inerent driverele de baze de date către anumite pattern-uri standardizate de data handling. asyncpg ignoră complet această specificație. Este rescris de la zero, conceput exclusiv pentru asyncio și PostgreSQL, făcând bypass la interfețele standard de baze de date pentru a comunica cu Postgres în propriii săi termeni. Pentru a înțelege de ce contează acest design, uită-te la modul în care driverele tradiționale gestionează transferul de date. Majoritatea driverelor de baze de date comunică cu PostgreSQL folosind un format text-based. Când faci un query în baza de date pentru un număr, un timestamp sau un array complex, baza de date ia reprezentarea sa din memoria internă pentru acele date și o convertește într-un string. Apoi trimite acel string prin rețea. Când aplicația ta Python îl primește, driverul trebuie să facă parsing acelui string de text înapoi într-un integer Python, un obiect datetime sau o listă. Gândește-te la această abordare tradițională ca la o echipă care se bazează pe un traducător pentru fiecare conversație internă. Baza de date citește structurile sale native de date, le scrie ca documente text standardizate și le trimite prin rețea. Aplicația ta Python primește aceste documente și traduce cu greu textul înapoi în propriile sale obiecte structurate din memorie. Toată această encodare, stringifying și parsing arde timp de CPU și consumă memorie. asyncpg rezolvă această problemă vorbind direct protocolul binar de frontend și backend al PostgreSQL. Forțează baza de date să folosească exclusiv input și output binar. În loc să se bazeze pe un traducător, baza de date și driverul vorbesc exact aceeași limbă nativă. Dacă faci un query pentru un integer pe șaizeci și patru de biți, PostgreSQL trimite acei raw bytes care reprezintă acel integer. asyncpg citește acei bytes direct într-un obiect integer din Python. Nu există string formatting. Nu există text parsing. Această înțelegere nativă se extinde și la date complexe. Când ceri un bloc JSON, un universally unique identifier sau un tip de date geometric, protocolul binar se asigură că payload-ul rămâne compact și strict structurat. Driverul știe exact câți bytes să citească pentru fiecare coloană fără să scaneze vreodată după delimitatori de text. Iată ideea cheie. Viteza asyncpg nu vine în primul rând din natura non-blocking a Python asyncio. Câștigurile masive de performanță vin din eliminarea completă a layer-ului de traducere a textului. Faci semnificativ mai puțină muncă per rând returnat. Prin forțarea strictă a transferului binar de date, aplicația ta nu mai irosește resurse citind text și își petrece acel timp de CPU executând business logic-ul tău real. Asta e tot pentru acest episod. Mulțumesc pentru audiție și continuă să construiești!
4

Conectarea și execuția de bază

3m 57s

Începe să folosești asyncpg conectându-te la o bază de date și rulând interogări de bază. Înțelege sintaxa nativă a argumentelor Postgres.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Python Async de înaltă performanță, episodul 4 din 17. Dacă ești obișnuit cu driverele standard de baze de date din Python, modul în care transmiți variabile în query-uri te va lua prin surprindere, iar codul tău va crăpa imediat. Driverele standard din Python se bazează pe markere de string formatting, dar această librărie comunică direct cu motorul bazei de date. Astăzi vorbim despre conectare și execuția de bază. Pentru a începe să comunici cu baza de date, folosești funcția connect din asyncpg. Faci await pe această funcție și îi transmiți un Data Source Name, care este un connection URI standard pentru Postgres. Acesta arată exact ca o adresă web. Începe cu postgresql două puncte slash slash, urmat de username-ul tău, două puncte, parola, un arond, adresa de host și, în final, un slash cu numele bazei de date. Făcând await pe această funcție, stabilești legătura de rețea și primești un connection object activ. Acum vrei să inserezi un username și o dată de naștere într-un tabel. Aici se schimbă sintaxa de query. Nu folosi la sută s sau semne de întrebare pentru parametrii din query. Pentru că asyncpg face bypass intenționat la API-ul standard de baze de date din Python, te obligă să folosești placeholderi nativi de Postgres. Scrii dolar unu, dolar doi, și așa mai departe. Query string-ul tău va arăta ca un insert statement standard, dar valorile vor fi dolar unu și dolar doi. Pentru a rula acest query fără să ceri date înapoi, faci await pe metoda execute de pe connection object-ul tău. Transmiți mai întâi query string-ul, urmat de variabilele efective pentru nume și data nașterii. Metoda execute rulează statement-ul și ignoră orice date tabulare. Returnează pur și simplu un status string din Postgres, ceva de genul insert zero unu. Nu returnează rândurile efective din baza de date. Dacă ai nevoie ca baza de date să genereze un ID unic pentru acest user nou, și ai nevoie de acel ID înapoi în Python, execute nu este tool-ul potrivit. Modifici query-ul SQL pentru a adăuga o clauză returning id la final. Pentru că acum te aștepți să primești date înapoi, folosești metoda fetchval. Metoda fetchval rulează query-ul și returnează exact o valoare specifică. Se uită la primul rând returnat, ia prima coloană și îți oferă doar acea bucată de date. Asta e perfect pentru a prelua un user ID nou generat. Dacă ai nevoie de mai mult decât de ID, poate vrei valorile default din baza de date pentru mai multe coloane, folosești în schimb fetchrow. Făcând await pe fetchrow, primești un singur record object care conține toate coloanele din acel prim rând. Poți accesa datele din acest record exact ca într-un dictionary din Python, folosind numele coloanelor pe post de chei. Când ai terminat de inserat datele, trebuie să faci await pe metoda close de pe connection object pentru a curăța network socket-ul și a elibera resursele bazei de date. Iată ideea cheie. Faptul că te obligă să folosești placeholderi nativi cu semnul dolar nu este doar o ciudățenie stilistică. Permite librăriei asyncpg să facă bypass complet la string interpolation pe partea de client, mapând tipurile din Python direct la formatele binare din Postgres pentru viteză maximă și protecție completă împotriva SQL injection. Mulțumesc pentru audiție, happy coding tuturor!
5

Conversia nativă a tipurilor

3m 16s

Descoperă cum asyncpg mapează automat tipurile de date PostgreSQL la obiecte native Python, eliminând nevoia unei parsări complexe prin ORM.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. High-Performance Python Async, episodul 5 din 17. Scrii un raw SQL query, îl execuți și te pregătești să faci parse manual la date strings, să dai split la arrays separate prin virgulă și să faci cast la valorile valutare. Dar nu e nevoie să faci asta. Nu ai nevoie de un ORM ca să scoți obiecte Python fully typed din baza ta de date. Asyncpg gestionează asta automat prin type conversion nativ. Când asyncpg comunică cu PostgreSQL, folosește protocolul binar al bazei de date. Știe exact ce data type reprezintă fiecare coloană. În loc să-ți dea raw text strings care necesită parsing secundar în Python, asyncpg traduce tipurile PostgreSQL direct în obiecte standard din librăria Python. Această traducere are loc automat în ambele direcții. Când pasezi un obiect Python ca query parameter, asyncpg îi face encode în formatul binar PostgreSQL corect. Când baza de date răspunde, asyncpg face decode la datele binare înapoi în tipul Python corespunzător. Ia în calcul un scenariu în care faci fetch la un user profile din baza ta de date. Query-ul tău SQL cere un username, un array de user tags, timpul de creare a contului și ultima adresă IP cunoscută. În multe database drivers, ai primi înapoi strings la care trebuie să le faci parse manual. Cu asyncpg, record-ul rezultat este deja typed. Username-ul este un string standard Python. Coloana tags, care este un array în PostgreSQL, ajunge ca o listă nativă de strings în Python. Timpul de creare este un obiect datetime standard. Adresa IP, stocată ca tip inet în baza de date, se mapează direct pe obiectele ipaddress built-in din Python. Scrii zero logică de parsing pentru a obține asta. Există un mapping strict pentru numere care îi ia prin surprindere pe unii developeri. Dacă coloana ta PostgreSQL este definită ca numeric, nu se convertește într-un float Python. Asyncpg mapează tipul numeric din PostgreSQL direct pe clasa decimal punct Decimal din Python. Asta păstrează precizia exactă. Dacă faci query pe înregistrări financiare sau măsurători precise, nu vei pierde date din cauza erorilor de rotunjire de tip floating-point. Tipurile floating-point standard din Postgres, cum ar fi real sau double precision, se mapează pe floats în Python. Iată ideea de bază pentru alte tipuri specifice. Dacă selectezi o coloană UUID nativă, primești un obiect Python uuid punct UUID, nu o reprezentare generică de tip string. Datele calendaristice devin obiecte datetime punct date. Intervalele Postgres se mapează perfect pe timedeltas din Python. Datele binare stocate într-o coloană bytea sunt convertite direct în bytes Python. Coloanele JSON și JSONB se comportă puțin diferit. Asyncpg convertește datele JSON și JSONB în strings standard Python by default. Nu le face parse automat în dictionaries Python. Primești raw string-ul, pe care îl poți pasa apoi modulului json standard din Python dacă ai nevoie să manipulezi datele nested. Bazându-te pe această traducere de tip binar, îți menții logica aplicației curată și muți responsabilitatea de type safety către database driver, acolo unde îi este locul. Asta e tot pentru acest episod. Mulțumesc pentru audiție și continuă să construiești!
6

Codecuri de tip personalizate

4m 51s

Învață să definești conversii de date personalizate în asyncpg. Acest episod explică modul de utilizare a set_type_codec pentru a decoda automat JSONB în dicționare Python.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. High-Performance Python Async, episodul 6 din 17. Faci un query în baza de date pentru un user record și primești înapoi un raw string. De fiecare dată, scrii același boilerplate code pentru a parsa acel string într-un dictionary înainte să poți folosi efectiv datele. Poți învăța driver-ul bazei de date să vorbească nativ formatul de date al aplicației tale, scăpând astfel de parsarea manuală la fiecare query. Asta se face folosind Custom Type Codecs. Când faci un query în PostgreSQL folosind driver-ul, tipurile de bază, cum ar fi integers și text, sunt traduse automat. Dar când folosești tipuri de date complexe, cum ar fi JSON, driver-ul trebuie să știe cum vrei să fie reprezentate acele date în Python. Un custom type codec acționează ca un translation layer. Stă între conexiunea la baza de date și logica aplicației tale. Pentru a configura asta, folosești o metodă numită set type codec. Apelezi această metodă direct pe un connection object activ. Are nevoie de patru informații principale. În primul rând, oferi numele tipului din baza de date, cum ar fi string-ul jsonb. În al doilea rând, specifici schema în care se află acest tip. Pentru tipurile built-in din PostgreSQL, aceasta este schema pg catalog. Apoi, oferi logica de traducere pasând o funcție encoder și o funcție decoder. Encoder-ul definește modul în care Python trimite datele către baza de date. Preia obiectul tău Python și returnează un format pe care PostgreSQL îl înțelege, de obicei un string. Dacă lucrezi cu JSON, poți pur și simplu să pasezi funcția json dumps din standard library. Decoder-ul definește modul în care datele se întorc din baza de date. Primește raw string-ul de la PostgreSQL și returnează obiectul Python dorit. Pentru JSON, pur și simplu pasezi funcția json loads. Gândește-te la un sistem care stochează user preferences nestructurate. În baza ta de date, coloana preferences este definită ca jsonb. În aplicația ta Python, gestionezi preferences ca un dictionary standard. Odată ce codec-ul tău este configurat, execuți un select query simplu pentru un user. Coloana preferences ajunge în aplicația ta deja structurată ca un dictionary Python. Când rulezi un insert query, pasezi un dictionary direct ca un query argument. Driver-ul declanșează automat encoder-ul tău, convertește dictionary-ul în JSON și îl trimite către baza de date. Nu apelezi niciodată manual un encoder sau decoder în codul tău de query execution. Iată care este ideea principală. Custom type codecs nu se aplică global pe întregul database driver. Metoda set type codec modifică doar conexiunea specifică pe care este apelată. Dacă configurezi un codec pe o conexiune, o a doua conexiune nu va ști nimic despre el și va returna din nou raw strings. Acest comportament cauzează frecvent probleme atunci când developerii introduc un connection pool. Nu poți configura un pool cu un singur apel de metodă codec. În schimb, trebuie să îți înregistrezi custom codec-ul de fiecare dată când se stabilește o nouă conexiune. Obții asta prin definirea unei funcții de inițializare. În interiorul acelei funcții, accepți noul connection object și apelezi set type codec pe el. Apoi pasezi această funcție de inițializare către logica ta de pool creation. Pool-ul rulează automat funcția ta de fiecare dată când deschide o conexiune nouă, garantând că acele codecs ale tale sunt mereu prezente și active. Mutarea data serialization-ului mai jos, în driver layer, prin custom type codecs, elimină logica repetitivă de parsare și se asigură că formatele tale de date rămân perfect sincronizate în întreaga ta aplicație. Mersi de audiție, happy coding tuturor!
7

Codecuri avansate cu PostGIS

3m 18s

Aprofundează codecurile de tip personalizate mapând tipurile de geometrie PostGIS din PostgreSQL la obiecte Python Shapely folosind formatul binar.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Python Async de înaltă performanță, episodul 7 din 17. Gestionarea coordonatelor geografice înseamnă de obicei parsing haotic de string-uri. Scrii un query, primești înapoi un string de text gigantic plin de numere și apoi arzi cicluri de CPU ca să-l spargi doar pentru a găsi o latitudine și o longitudine. Codec-urile avansate cu PostGIS din asyncpg rezolvă complet această problemă. Când extragi custom types din PostgreSQL, ai de-a face cu codec-uri. Un codec îi spune lui asyncpg cum să traducă un data type din PostgreSQL într-un obiect Python. Există adesea confuzie aici între formatul text și formatul binar. Formatul text este default pentru multe tool-uri de baze de date. Cu PostGIS, un query text returnează Well-Known Text. Acesta arată precum cuvântul POINT urmat de coordonatele din paranteze. Este human-readable, dar citirea lui în cod necesită alocarea de string-uri, căutarea parantezelor și un casting al caracterelor la float-uri. Parsing-ul de text este lent și scalează prost atunci când procesezi mii de rows. Vrei formatul binar. PostGIS folosește un standard numit Well-Known Binary. Când îți configurezi codec-ul în asyncpg, setezi explicit argumentul format pe binary. Baza de date sare peste generarea de text și îți dă raw bytes. Acum, ai nevoie de o modalitate de a traduce acești bytes în ceva ce aplicația ta Python poate folosi efectiv. Aici intervine un library Python precum Shapely. Shapely gestionează geometrie complexă și știe deja exact cum să citească Well-Known Binary. Îi spui lui asyncpg să folosească un custom type codec apelând metoda set type codec direct pe conexiunea ta la baza de date. Specifici numele tipului din PostgreSQL, care este geometry. Apoi oferi o funcție de encoder și o funcție de decoder. Decoder-ul preia string-ul de raw bytes din PostgreSQL și îl dă mai departe direct către binary reader-ul lui Shapely. Gândește-te la un query pentru locația Empire State Building. Fără un custom binary codec, baza de date returnează un string, aplicația ta îi face parse, construiește un dicționar și, în cele din urmă, creează un obiect geometry. Cu codec-ul binar la locul lui, execuți un select statement standard. Asyncpg interceptează datele binare, rulează funcția ta de decoder și îți dă instantaneu un obiect Shapely Point complet format. Poți accesa imediat coordonatele x și y pe obiectul returnat. Procesul funcționează invers pentru datele care se întorc în baza de date. Funcția ta de encoder pregătește datele Python pentru a fi trimise către PostgreSQL. Obiectele Shapely implementează un standard numit geo interface. Aceasta este o structură comună de dicționar Python utilizată pentru geometrie. Encoder-ul tău preia orice obiect Python care suportă această interfață, folosește Shapely pentru a-l serializa ca Well-Known Binary și trimite acei raw bytes înapoi în baza de date. Nu atingi niciodată o reprezentare text. Dacă găsești aceste deep dive-uri utile, poți susține emisiunea căutând DevStoriesEU pe Patreon. Iată ideea cheie. Prin utilizarea strictă a formatului binar pentru custom type codec-uri, elimini bottleneck-ul de serializare, permițând bazei de date și aplicației tale Python să comunice la viteza memoriei. Mulțumesc pentru ascultare, happy coding tuturor!
8

Gestionarea tranzacțiilor

3m 32s

Stăpânește tranzacțiile bazei de date în asyncpg. Acoperim comportamentul auto-commit și modul de a executa în siguranță interogări multiple folosind context managers asincroni.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Python Async de înaltă performanță, episodul 8 din 17. Execuți un query de update, apoi scriptul tău dă crash înainte să ruleze următorul query. Verifici baza de date așteptându-te să nu se fi schimbat nimic, dar primul update este chiar acolo, salvat permanent. În asyncpg, dacă nu ceri explicit o tranzacție, fiecărui query i se face commit în milisecunda în care se termină. Gestionarea tranzacțiilor este modul prin care repari acest comportament. Implicit, asyncpg funcționează în modul auto-commit. Asta înseamnă că, dacă scrii cod care execută trei query-uri unul după altul, nu rulezi un singur bloc de logică. Rulezi trei operațiuni complet izolate. Dacă al doilea query eșuează, primul query este deja finalizat în baza de date. Lipsa unui bloc explicit de tranzacție este o cauză frecventă a state-ului corupt al aplicației. Să luăm un scenariu în care transferi bani între două conturi bancare. Trebuie să scazi fonduri din contul A, și apoi să adaugi exact acele fonduri în contul B. Ambele update-uri trebuie să reușească împreună, sau ambele trebuie să eșueze împreună. Dacă deducerea reușește, dar adăugarea eșuează, banii pur și simplu dispar. Pentru a lega aceste operații între ele, folosești metoda transaction pe obiectul tău connection. Această metodă returnează un context manager asincron. În codul tău, scrii async with connection dot transaction, urmat de două puncte, și apoi îți indentezi query-urile aferente. Când Python intră în acest bloc, asyncpg îi spune lui PostgreSQL să înceapă o nouă tranzacție. În interiorul blocului, execuți query-ul de deducere, urmat de query-ul de adăugare. Dacă ambele query-uri rulează fără probleme și Python ajunge la finalul blocului, asyncpg emite automat o comandă de commit. Modificările aduse ambelor conturi devin vizibile pentru restul bazei de date exact în același moment. Iată ideea de bază. Dacă apare vreo problemă în interiorul acelui bloc, baza de date rămâne în siguranță. Problema ar putea fi o încălcare a unui constraint din baza de date, un network timeout, sau chiar o eroare pură de Python, cum ar fi o variabilă lipsă sau o împărțire la zero. Dacă este aruncată o excepție, context manager-ul o interceptează. Trimite automat o comandă de rollback către PostgreSQL, ștergând deducerea din contul A, și apoi lasă excepția Python să facă bubble up pe call stack-ul tău. De asemenea, poți face nest la aceste blocuri în siguranță. Dacă deschizi un nou context manager de tranzacție în timp ce te afli deja în interiorul unui bloc de tranzacție activ, asyncpg nu se încurcă. În schimb, creează automat un savepoint în baza de date. Un savepoint acționează ca un semn de carte într-o tranzacție în desfășurare. Dacă blocul interior se lovește de o eroare, face rollback la state-ul bazei de date doar până la acel semn de carte. Blocul exterior rămâne complet intact și încă poate face commit la propria sa muncă, sau poate alege să eșueze pe baza logicii tale. Nu trebuie să scrii comenzi manuale de savepoint, ci doar faci nest la blocurile tale async with. În cele din urmă, context manager-ul de tranzacție leagă permanent state-ul bazei de date de state-ul de execuție Python, asigurând că o excepție Python netratată este o garanție absolută împotriva update-urilor parțiale în baza de date. Asta e tot pentru acest episod. Mulțumesc pentru audiție și continuă să construiești!
9

Connection Pooling

3m 38s

Scalează-ți aplicația cu funcționalitatea integrată de connection pooling din asyncpg. Învață cum să gestionezi eficient conexiunile la baza de date în serviciile web cu trafic intens.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. High-Performance Python Async, episodul 9 din 17. Deschiderea unei noi conexiuni la baza de date pentru fiecare web request primit este o modalitate extrem de eficientă de a-ți bloca complet serverul. Doar network overhead-ul îți va bloca aplicația și vei epuiza rapid limita de conexiuni a bazei de date. Soluția pentru asta este Connection Pooling. Când folosești o librărie precum asyncpg pentru a comunica cu PostgreSQL, stabilirea unui bare connection este o operațiune costisitoare. Necesită un TCP handshake, negociere securizată și autentificare la baza de date. Dacă rulezi un web service cu trafic mare, pur și simplu nu îți permiți să plătești această taxă de latență pentru fiecare HTTP request în parte. În schimb, trebuie să menții o colecție constantă de conexiuni gata de utilizare. În asyncpg, obții asta folosind funcția create pool. De obicei, apelezi această funcție o singură dată în timpul fazei de startup a aplicației. Oferi credentials pentru baza de date, host-ul și port-ul, iar asyncpg pornește un set de conexiuni idle în background. Din acel moment, route handler-ele și background task-urile tale nu mai creează niciodată o conexiune nouă de la zero. Ele doar le împrumută pe cele existente. Există o capcană comună aici care îi încurcă pe mulți developeri. Nu confunda împrumutarea unei conexiuni cu deschiderea unei tranzacții în baza de date. Sunt operațiuni complet separate. Când împrumuți o conexiune din pool, rezervi network socket-ul doar pentru uzul tău exclusiv și temporar. Dacă operațiunea ta necesită atomicitate pe mai multe query-uri, tot trebuie să pornești explicit o tranzacție pe acea conexiune specifică împrumutată. Gândește-te la un web service FastAPI sau aiohttp cu trafic mare. Să zicem că ai un endpoint care acceptă un integer, face un query în baza de date pentru a calcula puterea a doua a acelui număr și returnează rezultatul. Când un request ajunge la endpoint-ul tău, apelezi metoda acquire pe obiectul tău pool. Faci asta folosind un context manager asincron. Asta preia temporar o conexiune din pool. Apoi folosești acea instanță specifică de conexiune pentru a executa query-ul în baza de date ca să calculezi puterea a doua. Odată ce code block-ul se termină, context manager-ul eliberează automat conexiunea. Îi curăță state-ul și o dă înapoi pool-ului, făcând-o imediat disponibilă pentru următorul HTTP request primit. Dacă un spike brusc de trafic îți lovește web server-ul și toate conexiunile din pool sunt deja preluate, următorul request nu dă crash. Așteaptă. Apelul acquire va pune pur și simplu execuția pe pauză până când un alt request se termină și își returnează conexiunea. Aici este ideea cheie. Connection pool-ul nu doar economisește timp la network handshakes. Acționează ca un concurrency throttle strict și de încredere. Îți protejează baza de date PostgreSQL de a fi copleșită de un flood neașteptat de trafic. Dacă îți configurezi pool-ul să țină exact douăzeci de conexiuni, baza de date nu va vedea niciodată mai mult de douăzeci de query-uri active concurente de la acea instanță a aplicației, indiferent câte mii de request-uri simultane ajung pe web server-ul tău. Mersi că m-ai ascultat. Până data viitoare!
10

Caching pentru Prepared Statements

4m 08s

Înțelege cum asyncpg optimizează parsarea interogărilor cu prepared statements automate și de ce poolerele externe precum PgBouncer pot cauza conflicte.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. High-Performance Python Async, episodul 10 din 17. Aplicația ta rulează perfect pe mașina locală. Dar în momentul în care îi faci deploy în producție în spatele unui connection router pentru baza de date, începi să vezi crash-uri aleatorii. Log-urile se plâng de statement-uri care există deja, sau de statement-uri care nu pot fi găsite. Nimic din codul tău nu s-a schimbat. Asta se întâmplă din cauza unei optimizări built-in numite Prepared Statement Caching. Pentru a înțelege eroarea, trebuie mai întâi să ne uităm la ce face asyncpg în spate. De fiecare dată când trimiți un query prin asyncpg, librăria îl traduce automat într-un prepared statement pe serverul PostgreSQL. În mod normal, când o bază de date primește un query, trebuie să facă parse la text, să analizeze sintaxa și să construiască un execution plan. Asta ia timp. Prin pregătirea statement-ului, PostgreSQL face toată munca asta grea o singură dată și îi atribuie un nume intern. Pentru toate execuțiile viitoare ale acelui query exact, asyncpg trimite doar noii parametri și numele statement-ului. Asta sare complet peste faza de parsing și oferă un performance boost masiv. asyncpg ține un cache al acestor prepared statements în memorie, strâns legat de conexiunea activă la baza de date. Aici e ideea cheie. Problema apare dintr-un conflict între modul în care asyncpg își gestionează conexiunile interne și modul în care funcționează poolerele externe precum PgBouncer. asyncpg presupune că are o conexiune fizică dedicată și persistentă la serverul Postgres. Când creează un prepared statement, are încredere că statement-ul va rămâne disponibil pe acea conexiune exactă până când conexiunea se închide. Acum introdu PgBouncer în arhitectură, mai exact rulând în transaction mode. PgBouncer stă între aplicația ta și baza de date. El menține un pool mic de conexiuni Postgres reale și le împarte între mii de request-uri primite de la clienți. În transaction mode, PgBouncer îi dă aplicației tale o conexiune fizică la baza de date doar pe durata unei singure tranzacții. În momentul în care acea tranzacție face commit, PgBouncer ia conexiunea fizică înapoi și o dă unui client complet diferit. Asta strică cache-ul de prepared statements. Aplicația ta trimite un query. asyncpg îl pregătește și îl pune în cache pe conexiunea fizică A. Tranzacția se termină. Câteva secunde mai târziu, aplicația ta trimite exact același query. asyncpg ține minte că a pregătit deja acest query, așa că îi spune lui Postgres să execute statement-ul salvat. Dar de data asta, PgBouncer a rutat aplicația ta către conexiunea fizică B. Conexiunea B nu are nicio înregistrare a acelui prepared statement. Baza de date aruncă o eroare care spune că statement-ul nu există. Și inversul este valabil. Un alt client ar putea fi rutat către conexiunea A, ar putea încerca să pregătească un statement cu același nume intern și să declanșeze o eroare care spune că statement-ul există deja. Fix-ul este simplu, dar necesită un compromis. Trebuie să îi spui lui asyncpg să dezactiveze această optimizare. Când îți inițializezi conexiunea asyncpg sau connection pool-ul, pasezi un argument specific care setează statement cache size la zero. Asta dezactivează complet caching-ul automat de prepared statements. Query-urile tale vor fi acum parsate de PostgreSQL de fiecare dată când rulează. Sacrifici o mică parte din performanța de parsing, dar aplicația ta va deveni instantaneu stabilă pe conexiunile distribuite. Dacă conexiunile tale la baza de date sunt rutate dinamic per tranzacție, aplicația ta nu mai poate presupune că serverul bazei de date ține minte absolut nimic între query-uri. Asta e tot pentru acest episod. Mulțumesc pentru audiție și continuă să construiești!
11

Array-uri Postgres și clauze IN

3m 46s

Rezolvă cea mai comună eroare de sintaxă la migrarea către asyncpg. Învață cum să filtrezi corect interogările pe baza unei liste de valori folosind ANY().

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. High-Performance Python Async, episodul 11 din 17. Scrii un query SQL standard pentru a filtra înregistrări. Trimiți o listă de valori în parametrii de query, exact așa cum ai făcut-o în zeci de alte librării de baze de date. Postgres aruncă imediat o eroare de sintaxă. Problema nu sunt datele tale. Problema e modul în care gestionezi array-urile Postgres și clauzele IN. Asta e eroarea de sintaxă numărul unu pentru developerii care migrează la asyncpg de la alte interfețe de baze de date. În multe librării mai vechi, driver-ul interceptează query string-ul. Dacă trimiți o listă Python către o clauză IN, librăria rescrie string-ul SQL on the fly. Îți expandează lista într-un string de parametri individuali separați prin virgulă, înainte să o trimită către baza de date. Asyncpg nu face asta. Se bazează în întregime pe prepared statements native Postgres, server-side. Iată ideea de bază. În SQL-ul standard din Postgres, operatorul IN necesită o listă de valori scalare separate prin virgulă, pusă între paranteze. Nu acceptă un singur obiect de tip array. Când trimiți o listă Python către asyncpg ca parametru, asyncpg mapează acea listă direct la un array nativ Postgres. Query-ul tău ajunge să fie evaluat ca o expresie IN un obiect array, ceea ce este o sintaxă invalidă. Postgres se așteaptă la o expresie IN valoarea unu, valoarea doi. Ca să repari asta, trebuie să nu mai folosești operatorul IN pentru liste parametrizate. În schimb, folosește funcția any din Postgres. Logica se schimbă de la a întreba dacă o valoare este IN într-o listă, la a întreba dacă o valoare este egală cu orice element dintr-un array. Operatorul any este conceput special pentru a lucra cu tipurile array din Postgres. Evaluează valoarea din stânga, verifică array-ul din dreapta și returnează true dacă găsește un match. De asemenea, trebuie să-i spui lui Postgres ce fel de array primește, făcând un cast parametrului. Dacă te aștepți la un array de string-uri de text, faci cast explicit parametrului tău la un array de text. Acest type casting explicit garantează că Postgres știe exact cum să planifice și să execute query-ul, fără să ghicească tipul de date din spate al binary stream-ului de intrare. Ia în considerare un scenariu în care filtrezi o listă de produse. Vrei să faci match între categoria produsului și o listă dinamică de categorii selectate de user. Scrii un query pentru a selecta produse unde categoria este egală cu any parametrul unu, și faci cast parametrului unu la un array de text. În codul tău Python, apelezi metoda fetch a bazei de date. Trimiți query string-ul ca prim argument, iar lista ta Python de string-uri — cum ar fi electronice și cărți — ca al doilea argument. Asyncpg împachetează lista ta Python într-un array binar de text Postgres și o trimite over the wire ca un singur parametru. Postgres primește query-ul, vede array-ul de text și face match eficient pe categorii folosind funcția any. Abordarea asta e strict mai bună decât manipularea de string-uri. Pentru că structura query-ului nu se schimbă niciodată, indiferent de câte categorii sunt în listă, Postgres face parse și planifică statement-ul exact o singură dată. Baza de date face cache la query plan, economisind timp de execuție la apelurile ulterioare. De asemenea, elimini riscul de SQL injection, din moment ce datele sunt transmise în formă binară, complet separat de textul query-ului. Dacă trimiți o listă Python către un parametru de bază de date, trateaz-o ca pe un array nativ, fă-i cast la tipul corect și evalueaz-o cu funcția any. Asta e tot pentru acest episod. Mulțumesc pentru audiție și continuă să construiești!
12

Obiecte Record vs Named Tuples

4m 18s

Explorează designul unic al obiectelor Record din asyncpg. Înțelege de ce dot-notation este omisă implicit și cum să o activezi folosind clase personalizate.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Python Async de înaltă performanță, episodul 12 din 17. Faci un query în baza de date, primești un rând înapoi și tastezi instinctiv numele variabilei punct id. Imediat, Python aruncă un AttributeError. Datele tale sunt acolo, dar nu le poți accesa așa cum te aștepți. Asta se întâmplă din cauza designului deliberat din spatele obiectelor Record versus Named Tuples. Când faci fetch la date folosind asyncpg, nu returnează dicționare Python standard și nu returnează named tuples. În schimb, returnează un obiect custom, extrem de optimizat, numit Record. Dacă ești obișnuit cu alte drivere de baze de date sau ORM-uri, te-ai aștepta ca dot-notation să funcționeze out of the box. Cu un Record din asyncpg, asta crapă intenționat. Te-ai putea întreba de ce driverul nu folosește pur și simplu un named tuple standard din Python, din moment ce named tuples suportă nativ dot-notation. Motivul este performanța pură. Un named tuple îi cere lui Python să genereze o structură de clasă complet nouă pentru fiecare combinație unică de coloane returnată de un query. Dacă aplicația ta execută sute de query-uri cu structuri diferite, generarea acelor clase dinamice creează un overhead masiv de execuție. Librăria este construită pentru viteză absolută, așa că ocolește complet acest bottleneck returnând în schimb propriul tip Record compilat. Acest obiect custom Record acționează ca un hibrid rapid. Suportă indexare cu integer exact ca un tuple standard, ceea ce înseamnă că poți accesa prima coloană folosind indexul zero. Dar oferă și un mapping de tip dicționar. Îți accesezi coloanele folosind bracket notation, pasând numele coloanei ca string. Aici e ideea cheie. Creatorii au dezactivat intenționat dot-notation pe aceste obiecte pentru a-ți proteja aplicația de namespace clashes. Gândește-te la metodele standard de mapping din Python, cum ar fi keys, items, values sau get. Dacă tabelul tău din baza de date se întâmplă să aibă o coloană numită keys, iar driverul ar suporta dot-notation, dacă ai tasta record punct keys s-ar crea un conflict structural. Python nu ar ști dacă vrei valoarea din baza de date sau metoda built-in. Prin impunerea bracket notation pe bază de string-uri, driverul garantează că numele coloanelor tale nu vor intra niciodată în coliziune cu atributele standard din Python. Totuși, dacă îți controlezi complet schema bazei de date, știi sigur că nu folosești cuvinte rezervate din Python ca nume de coloane și ai nevoie de dot-notation pentru codebase-ul tău, ai o portiță de scăpare. Poți face override la comportamentul default. Când stabilești conexiunea la baza de date, poți oferi un parametru specific numit record class. Pentru a implementa asta, scrii o clasă custom care moștenește direct din clasa de bază Record din asyncpg. În interiorul acestei noi clase, implementezi metoda built-in din Python numită double underscore getattr. Instruiești această metodă să preia numele atributului cerut și pur și simplu să îl caute folosind fallback-ul sigur cu bracket notation. Odată ce pasezi această clasă custom la setup-ul conexiunii tale, fiecare rând returnat de query-urile tale va fi o instanță a obiectului tău custom. Python îți va permite apoi să folosești dot-notation, rutând transparent request-ul de atribut prin metoda ta custom pentru a face fetch la datele coloanei de la bază. În cele din urmă, bracket notation-ul strict dintr-un Record default nu este o omisiune, ci o limită structurală de siguranță care asigură că accesul tău la date rămâne predictibil, indiferent de modul în care se modifică schema bazei de date. Asta e tot pentru acest episod. Mersi pentru audiție și continuă să construiești!
13

Streaming de rezultate cu cursoare

4m 09s

Previne epuizarea memoriei atunci când interoghezi seturi masive de date. Învață cum să folosești cursoarele asyncpg pentru a face streaming de rezultate chunk-by-chunk.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. High-Performance Python Async, episodul 13 din 17. Ai nevoie să exporți un tabel de useri cu un milion de rânduri într-un fișier, așa că rulezi un fetch standard din baza de date. Dintr-o dată, aplicația ta Python mănâncă tot RAM-ul disponibil și serverul dă crash. Nu poți să tragi dataset-uri masive în memorie toate odată, și tocmai de aceea folosim Streaming Results cu cursoare. În mod normal, când execuți un query în asyncpg, întregul result set este tras prin rețea din PostgreSQL și ținut în memoria Python. Baza de date construiește răspunsul complet, îl trimite, iar asyncpg construiește obiecte Python pentru absolut fiecare rând înainte ca al tău cod să-l poată procesa pe primul. Asta e perfect în regulă pentru cincizeci de rânduri. Dar e o problemă imediată pentru cinci milioane de rânduri. Ca să eviți un crash al serverului, trebuie să iei datele cu porția, nu să le înghiți pe toate odată. Un cursor de bază de date îți dă un pointer către rezultatele query-ului de pe serverul PostgreSQL. În loc să tragă totul, cursorul permite aplicației tale Python să dea fetch la date incremental. În asyncpg, faci asta apelând metoda cursor pe conexiunea ta. Oferi query-ul SQL și orice argumente necesare. Această metodă returnează un iterator asincron. Scrii un loop async-for ca să iterezi peste acest cursor. În spate, asyncpg dă fetch automat la rânduri în batch-uri mici, ușor de gestionat, din PostgreSQL. Codul tău procesează câteva rânduri, le scrie în fișierul de export și merge mai departe. Python curăță rândurile vechi din memorie, ceea ce menține memory footprint-ul total al aplicației complet plat, indiferent de cât de mare e tabelul. Există o regulă strictă aici care încurcă mulți developeri. Dacă încerci să iterezi un cursor direct pe o conexiune standard, asyncpg aruncă imediat o eroare InterfaceError. Mesajul de eroare va spune că cursoarele nu pot fi folosite în afara unei tranzacții. Iată ideea cheie. Cursoarele PostgreSQL sunt legate structural de tranzacțiile din baza de date. Când o tranzacție dă commit sau rollback, PostgreSQL distruge orice cursoare active asociate cu ea. By default, asyncpg operează în modul auto-commit. Asta înseamnă că fiecare query individual pe care îl rulezi este învelit în propria sa tranzacție minusculă, invizibilă, care se închide în momentul în care query-ul se termină. Dacă asyncpg ți-ar permite să deschizi un cursor în modul auto-commit, acea tranzacție implicită s-ar termina instantaneu, iar PostgreSQL ți-ar omorî cursorul înainte să poți da fetch măcar la un singur rând. Ca să faci cursoarele să meargă, trebuie să gestionezi explicit limitele tranzacției. Faci asta deschizând un context manager asincron folosind metoda transaction pe conexiunea ta. Odată ce ești în siguranță în interiorul acelui bloc de tranzacție, apelezi metoda cursor și pornești loop-ul tău async-for. Pentru că tranzacția rămâne deschisă pe întreaga durată a blocului de context manager, cursorul tău rămâne în viață pe serverul PostgreSQL, permițându-ți să faci stream la toate acel milion de rânduri în siguranță. Există o excepție rară de la această regulă. PostgreSQL suportă un feature prin care poți declara un cursor raw SQL cu sintagma WITH HOLD. Asta îi spune motorului de bază de date să materializeze rezultatul și să țină cursorul în viață chiar și după ce tranzacția se termină. Făcând asta mănânci resursele bazei de date și faci bypass la eficiența unui streaming standard. Pentru aproape toate task-urile de streaming în asyncpg, blocul explicit de tranzacție este abordarea necesară. Dacă găsești aceste episoade utile și vrei să susții show-ul, caută DevStoriesEU pe Patreon. Ține minte că un cursor îți transformă interacțiunea cu baza de date dintr-o alocare de memorie masivă și riscantă, într-un pipeline controlat și persistent, care poate procesa în siguranță orice volum de date. Asta e tot pentru acest episod. Mersi că m-ai ascultat și continuă să construiești!
14

Ingerare extrem de rapidă cu COPY

3m 59s

Accelerează-ți pipeline-urile de ingerare a datelor. Explorăm protocolul COPY din PostgreSQL pentru a încărca date în masă exponențial mai rapid decât cu instrucțiuni INSERT.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Python Async de înaltă performanță, episodul 14 din 17. Trebuie să muți un milion de rânduri în sau din baza ta de date. Dacă primul tău instinct este să faci batch la o listă masivă de statement-uri de insert sau să dai fetch la toate rândurile într-o listă Python gigantică, alegi cea mai lentă cale disponibilă. Există un mecanism construit special pentru a ocoli acest overhead. Astăzi vorbim despre ingestie ultra rapidă cu COPY. COPY este un protocol specific Postgres pentru transferul de date bulk. Când rulezi statement-uri standard de insert sau select, Postgres trebuie să facă parse la query, să-l planifice și să-l execute. Să faci asta în mod repetat adaugă un overhead masiv. Protocolul COPY sare complet peste pipeline-ul standard de query. Deschide un stream direct către layer-ul de storage, mutând datele într-un format extrem de optimizat. Din această cauză, este cu ordine de mărime mai rapid decât bulk inserts. În asyncpg, împingi date în baza de date folosind o metodă numită copy to table. Oferi numele tabelului target și o sursă de date. Acea sursă poate fi un file path local, un file-like object, sau un iterator asincron care dă yield la înregistrări. Dacă îl îndrepți către un fișier CSV local, Postgres gestionează partea de parsing nativ. Nu trebuie să deschizi fișierul în Python, să faci parse la rânduri și să le mapezi la variabile. Driverul bazei de date face stream la bytes-ii raw ai fișierului direct către server. De asemenea, poți pasa o listă simplă de tuple-uri Python dacă datele tale sunt deja în memorie, iar asyncpg le va face stream folosind protocolul COPY under the hood. Să scoți datele este la fel de rapid. Dacă ai nevoie de un export complet, folosești copy from table. Asta ia întregul conținut al unui tabel și îl aruncă într-un fișier sau stream. Totuși, să dai dump la un tabel întreg este rareori ceea ce ai de fapt nevoie. De obicei, vrei date filtrate sau cu join. Aici intră în joc copy from query. O concepție greșită comună este că această metodă dă dump la rezultatele unui query doar într-un fișier static. Asta pur și simplu nu este adevărat. Deși poate scrie direct către un file path, poți oferi și o funcție de callback. Asyncpg va executa query-ul și va face stream la rezultate în chunk-uri către callback-ul tău, lăsându-te să procesezi un dataset masiv on the fly, fără să ții vreodată tot result set-ul în memoria sistemului. Ia în considerare un scenariu în care trebuie să generezi un raport CSV cu toți userii activi. O abordare standard este să execuți un select query, să dai fetch la o sută de mii de rânduri în Python, să le formatezi folosind modulul CSV și să le scrii pe disk. Asta consumă memorie și CPU semnificativ. Iată ideea cheie. Poți sări complet peste procesarea din Python. Apelezi copy from query, îi pasezi statement-ul tău de select specific, setezi parametrul de format la CSV și oferi un file path de output. Postgres execută query-ul, formatează rezultatele în CSV nativ pe serverul bazei de date, iar asyncpg face stream la textul finalizat direct pe hard drive-ul tău. Aplicația ta Python acționează ca un simplu pipe, făcând aproape zero manipulare de date. Ar trebui să continui să folosești statement-uri standard de insert și select pentru logica de zi cu zi a aplicației, dar în momentul în care volumul de date raw devine bottleneck-ul tău, treci la protocolul COPY pentru a face bypass complet la parserul SQL. Asta e tot pentru acest episod. Mulțumesc pentru audiție și continuă să construiești!
15

Listen și Notify asincron

3m 13s

Deblochează arhitecturi event-driven în timp real direct în PostgreSQL. Învață cum să folosești add_listener din asyncpg pentru mesagerie pub/sub instantanee.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Python Async de înaltă performanță, episodul 15 din 17. Apelezi la un message broker separat, cum ar fi Redis, în momentul în care ai nevoie de evenimente în timp real. Dar dacă aplicația ta folosește deja o bază de date, s-ar putea să adaugi complexitate de infrastructură fără absolut niciun motiv. PostgreSQL are un message bus în timp real încorporat. Astăzi vorbim despre Asynchronous Listen and Notify. Postgres suportă nativ un pattern de publish-subscribe folosind două comenzi, listen și notify. Librăria asyncpg expune această capacitate în Python printr-o metodă numită add listener. În loc să scrii un loop care face polling pe un tabel din baza de date la fiecare câteva secunde pentru a verifica dacă există date noi, înregistrezi o funcție callback asincronă în Python pe un anumit canal. Când apare un eveniment în Postgres, acesta face broadcast unui mesaj către acel canal, iar callback-ul tău din Python se execută imediat. Iată ideea cheie. Un listener nu este atașat global la aplicația ta și nu este atașat la un connection pool. Este legat de o conexiune individuală și specifică la baza de date. Acesta este un punct comun în care apar erori. Dacă extragi o conexiune dintr-un pool, îți înregistrezi listener-ul și apoi eliberezi conexiunea înapoi în pool, listener-ul pică. Pentru a folosi acest feature într-un mod fiabil, trebuie să obții o conexiune de la asyncpg, să apelezi metoda add listener pe ea și să ții acea conexiune deschisă la nesfârșit. Devine un pipeline de ascultare dedicat. Să ne uităm la un scenariu practic. Ai un background worker care trebuie să se trezească și să proceseze înregistrări de fiecare dată când un rând nou este inserat într-un tabel de joburi. În loc să faci polling, setezi un trigger în baza de date. Ori de câte ori are loc un insert, trigger-ul execută o comandă notify pe un canal numit new jobs. De asemenea, trimite un scurt payload de text, cum ar fi identificatorul unic al noului rând. În codul tău Python, scrii o funcție callback asincronă. Această funcție se așteaptă să primească patru argumente de la asyncpg: obiectul conexiunii, identificatorul de proces Postgres, numele canalului și payload-ul de text în sine. Apoi, obții conexiunea ta dedicată. Apelezi add listener pe acea conexiune, pasând string-ul canalului new jobs împreună cu funcția ta de callback. În cele din urmă, menții scriptul în execuție, de obicei așteptând un eveniment asincron care nu se setează niciodată. Procesul tău Python stă acum complet idle. Folosește aproape zero resurse CPU și nu mai bombardează baza de date cu query-uri goale. În momentul în care o tranzacție dă commit la un nou rând de job, Postgres trimite notificarea prin socket-ul de rețea deschis. Asyncpg citește acel socket și programează imediat callback-ul tău pe event loop-ul din Python, pasându-i noul identificator de job. Adevărata putere a acestui pattern este consistența tranzacțională. Dacă o tranzacție din baza de date dă rollback, orice comenzi notify executate în timpul acelei tranzacții sunt eliminate automat de Postgres. Acest lucru garantează că workerii tăi Python se vor trezi și vor reacționa doar la datele care au fost salvate cu succes pe disc. Asta e tot pentru acest episod. Mulțumesc pentru audiție și continuă să construiești!
16

Telemetrie și logarea interogărilor

3m 42s

Obține o observabilitate profundă asupra performanței bazei tale de date. Descoperă cum să folosești log listeners din asyncpg pentru a urmări interogările lente și a monitoriza telemetria de execuție.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Python Async de înaltă performanță, episodul 16 din 17. Pentru a afla de ce baza ta de date este lentă, nu trebuie să ghicești. Te poți baza pe log-urile server-side, dar acestea omit latența de rețea și overhead-ul din Python. Măsurarea manuală a performanței în jurul fiecărui call către baza de date îți aglomerează rapid business logic-ul. Soluția este Telemetry și Query Logging. Oamenii confundă adesea log listeners cu query loggers. Log listeners captează notice-urile serverului Postgres. Query loggers captează telemetria de execuție client-side. Acestea gestionează două fluxuri de informații complet distincte. Query loggers gestionează telemetria. Atașezi o funcție callback la conexiunea ta folosind metoda add query logger. Odată atașată, de fiecare dată când un query se termină de executat, asyncpg transmite automat un obiect LoggedQuery către acest callback. Acest lucru se întâmplă global pentru conexiunea respectivă, complet decuplat de funcția specifică care face request-ul către baza de date. Obiectul LoggedQuery conține trei informații critice. Acesta conține textul exact al query-ului SQL, argumentele transmise acelui query și timpul de execuție. Argumentele sunt capturate exact așa cum le-a furnizat aplicația ta. Acest lucru te scutește de scrierea manuală a logicii de string formatting doar pentru a afla ce parametri au cauzat un răspuns lent. Ia în considerare un mediu de producție în care trebuie să prinzi orice query care durează mai mult de 500 de milisecunde. Definești o funcție Python standard care acceptă doi parametri: conexiunea la baza de date și obiectul LoggedQuery. În interiorul acestei funcții, verifici atributul timpului de execuție. Dacă timpul depășește zero virgulă cinci secunde, scrii textul query-ului, argumentele și durata exactă în sistemul de monitorizare al aplicației tale. Apoi transmiți această funcție callback metodei add query logger. Acum, aplicația ta urmărește automat query-urile lente, în mod silențios, în background. Dacă vreodată trebuie să oprești această urmărire, pur și simplu transmiți aceeași funcție callback metodei remove query logger. Acum, a doua parte a acestei soluții este log listener-ul. În timp ce query logger-ul gestionează timing-ul client-side, log listener-ul gestionează mesajele server-side. Uneori, Postgres trimite mesaje care nu sunt erori și nu returnează rânduri de date. Acestea sunt notice-uri asincrone, warning-uri sau mesaje de log custom, generate direct de motorul bazei de date. Pentru a captura aceste mesaje, atașezi un callback folosind metoda add log listener. Când Postgres emite un notice sau un warning, asyncpg declanșează acest callback. Acesta transmite conexiunea și un obiect de mesaj specific funcției tale. Acest lucru oferă aplicației tale vizibilitate imediată asupra warning-urilor la nivel de bază de date, complet independent de rezultatele standard ale query-urilor. La fel ca la query logger, poți detașa acest callback ulterior folosind remove log listener. Iată ideea cheie. Query logging-ul client-side îți oferă durata reală de execuție experimentată de aplicația ta Python, evitând complet presupunerile dintre network delay și procesarea bazei de date. Mulțumesc pentru audiție. Aveți grijă de voi, tuturor.
17

Securizarea conexiunilor cu SSL

3m 42s

Asigură-te că legăturile la baza ta de date sunt sigure. Acoperim configurarea contextului SSL și modul de a impune TLS direct la conectarea la baze de date în cloud.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. High-Performance Python Async, episodul 17 din 17. Conectarea la o bază de date managed în cloud fără SSL este ca și cum ai striga credențialele bazei de date într-o cameră aglomerată. Ai nevoie de criptare, dar configurarea ei corectă duce adesea la erori de conexiune confuze sau la setări default nesigure. Astăzi, securizăm conexiunile cu SSL în asyncpg. Lumea se încurcă adesea când vine vorba de configurarea SSL în logica de conexiune. Dacă folosești connection URIs, asyncpg face parse nativ la query parameters standard din PostgreSQL pentru sslmode, cum ar fi setarea sslmode pe require. Asta funcționează pentru setup-uri de bază. Dar când ai nevoie de control precis - cum ar fi conectarea securizată la o bază de date managed în cloud folosind un bundle custom de Certificate Authority - URI strings standard nu sunt de ajuns. Pentru asta, folosești parametrul ssl programatic. Parametrul ssl din funcțiile de conexiune asyncpg dictează modul în care este negociat TLS-ul. Acceptă două tipuri de valori. Primul tip este un string preset. Poți pasa string-ul prefer, care încearcă o conexiune SSL, dar face fallback la necriptat dacă serverul nu o suportă. Poți pasa require, care forțează criptarea, dar dă skip la verificarea identității serverului. Sau poți pasa verify-full, care forțează criptarea și validează strict certificatul serverului folosind trusted roots. Iată ideea cheie. Când scenariul tău cere un Certificate Authority custom, nu te baza pe string presets. În schimb, creezi un obiect SSLContext standard din Python. Configurezi acest obiect cu fișierele tale de certificat custom, impui verificarea strictă, și apoi pasezi acel obiect Python direct în parametrul ssl din asyncpg. Asta îți oferă control exact asupra handshake-ului criptografic, făcând bypass la orice certificate de sistem default. Asta acoperă regulile de criptare, dar cum începe de fapt conexiunea? Asta ne aduce la parametrul direct TLS. By default, PostgreSQL folosește un protocol numit STARTTLS. Clientul face o conexiune plain-text, întreabă serverul dacă suportă criptarea, și dacă serverul spune da, fac upgrade la TLS. Totuși, setup-urile proxy moderne - cum ar fi anumite connection poolers sau cloud load balancers - așteaptă adesea o conexiune direct TLS de la primul byte. Nu vor negocierea plain-text. Dacă infrastructura ta este construită așa, pasezi true parametrului de conexiune direct TLS. Când faci asta, asyncpg dă skip la negocierea STARTTLS și inițiază imediat un raw TLS handshake. Bineînțeles, asta funcționează doar dacă oferi și o configurație ssl validă. Dacă activezi direct TLS, dar lași parametrul ssl gol, conexiunea va da fail. Când îți securizezi conexiunile la baza de date, ține minte că, deși string presets sunt convenabile, pasarea unui obiect SSLContext explicit este singura modalitate de a garanta absolut că aplicația ta are încredere în identitatea corectă a serverului. Fiindcă acesta este ultimul episod al seriei, te încurajez să explorezi documentația oficială asyncpg și să încerci acești parametri de conexiune hands-on. Poți vizita devstories dot eu pentru a sugera subiecte pentru viitoarele noastre serii. Asta e tot pentru acest episod. Îți mulțumesc că m-ai ascultat, și keep building!