Retour au catalogue
Season 13 17 Épisodes 59 min 2026

High-Performance Python Async

Édition 2026. Une plongée en profondeur dans l'accélération de Python asyncio avec uvloop et l'interfaçage direct avec PostgreSQL en utilisant le protocole binaire d'asyncpg.

Fondamentaux de Python Programmation asynchrone
High-Performance Python Async
Lecture en cours
Click play to start
0:00
0:00
1
Le besoin de vitesse : L'architecture d'uvloop
Découvrez les différences architecturales entre l'asyncio standard de Python et uvloop. Nous explorons comment uvloop exploite Cython et libuv pour atteindre des performances similaires à celles de Go.
3m 21s
2
Intégration d'uvloop
Apprenez à intégrer uvloop dans votre application Python. Cet épisode couvre l'approche EventLoopPolicy pour remplacer de manière transparente la boucle d'événements par défaut.
3m 55s
3
Introduction à asyncpg : Le protocole binaire
Explorez la conception fondamentale d'asyncpg. Nous discutons des raisons pour lesquelles le contournement de la DB-API standard au profit du protocole binaire de PostgreSQL permet des gains de performances massifs.
3m 22s
4
Connexion et exécution de base
Démarrez avec asyncpg en vous connectant à une base de données et en exécutant des requêtes de base. Comprenez la syntaxe native des arguments Postgres.
3m 18s
5
Conversion de types native
Découvrez comment asyncpg mappe automatiquement les types de données PostgreSQL vers des objets Python natifs, éliminant ainsi le besoin d'une analyse ORM complexe.
3m 26s
6
Codecs de types personnalisés
Apprenez à définir des conversions de données personnalisées dans asyncpg. Cet épisode explique comment utiliser set_type_codec pour décoder automatiquement le JSONB en dictionnaires Python.
3m 52s
7
Codecs avancés avec PostGIS
Plongez au cœur des codecs de types personnalisés en mappant les types de géométrie PostGIS de PostgreSQL vers des objets Python Shapely en utilisant le format binaire.
3m 34s
8
Gestion des transactions
Maîtrisez les transactions de base de données dans asyncpg. Nous couvrons le comportement d'auto-commit et la manière d'exécuter plusieurs requêtes en toute sécurité à l'aide de gestionnaires de contexte asynchrones.
3m 18s
9
Pool de connexions
Mettez à l'échelle votre application avec le pool de connexions intégré d'asyncpg. Apprenez à gérer efficacement les connexions à la base de données dans les services web à fort trafic.
3m 17s
10
Mise en cache des requêtes préparées
Comprenez comment asyncpg optimise l'analyse des requêtes avec des requêtes préparées automatiques, et pourquoi des poolers externes comme PgBouncer peuvent causer des conflits.
3m 38s
11
Tableaux Postgres et clauses IN
Résolvez l'erreur de syntaxe la plus courante lors de la migration vers asyncpg. Apprenez à filtrer correctement les requêtes par rapport à une liste de valeurs en utilisant ANY().
3m 42s
12
Objets Record vs Named Tuples
Explorez la conception unique des objets Record d'asyncpg. Comprenez pourquoi la notation pointée est omise par défaut et comment l'activer avec des classes personnalisées.
3m 27s
13
Streaming de résultats avec les curseurs
Évitez l'épuisement de la mémoire lors de l'interrogation d'ensembles de données massifs. Apprenez à utiliser les curseurs d'asyncpg pour streamer les résultats par morceaux.
3m 39s
14
Ingestion ultra-rapide avec COPY
Boostez vos pipelines d'ingestion de données. Nous explorons le protocole COPY de PostgreSQL pour charger des données en masse de manière exponentiellement plus rapide qu'avec des instructions INSERT.
3m 25s
15
Listen et Notify asynchrones
Débloquez des architectures événementielles en temps réel directement dans PostgreSQL. Apprenez à utiliser add_listener d'asyncpg pour une messagerie pub/sub instantanée.
3m 00s
16
Télémétrie et journalisation des requêtes
Obtenez une observabilité approfondie des performances de votre base de données. Découvrez comment utiliser les écouteurs de journaux d'asyncpg pour suivre les requêtes lentes et surveiller la télémétrie d'exécution.
3m 29s
17
Sécurisation des connexions avec SSL
Assurez-vous que vos connexions à la base de données sont sécurisées. Nous couvrons la configuration du contexte SSL et la manière d'imposer le TLS direct lors de la connexion à des bases de données cloud.
3m 28s

Épisodes

1

Le besoin de vitesse : L'architecture d'uvloop

3m 21s

Découvrez les différences architecturales entre l'asyncio standard de Python et uvloop. Nous explorons comment uvloop exploite Cython et libuv pour atteindre des performances similaires à celles de Go.

Télécharger
Bonjour, ici Alex de DEV STORIES DOT EU. Python async haute performance, épisode 1 sur 17. Et si tu pouvais doubler les performances de ton code Python async sans réécrire une seule fonction ? C’est exactement la promesse de la technologie qu'on va regarder aujourd’hui : l’architecture uvloop. Quand les développeurs parlent de l'async Python standard, ils ont tendance à confondre deux couches distinctes. La première couche, c'est l'application programming interface. Ça inclut les mots-clés async et await que tu tapes, les futures, et les tasks. La deuxième couche, c'est l'event loop en elle-même. L'event loop, c'est le scheduler interne qui surveille les sockets, gère les connexions réseau et décide quelle task exécuter ensuite. Le Python standard fournit à la fois l’interface et une event loop par défaut écrite en pur Python. Voici l’idée clé. L’interface et l'event loop sont découplées. Python te permet de remplacer le scheduler sous-jacent sans changer la syntaxe que tu écris. Imagine une voiture. L'API async, c'est le volant, les pédales et le tableau de bord. Tu interagis directement avec ces éléments. L'event loop, c'est le moteur sous le capot. Remplacer l'event loop Python par défaut par uvloop, c'est comme installer un moteur V8 dans ton véhicule. Tu continues de tourner le volant et de freiner exactement de la même façon, mais la voiture avance beaucoup plus vite. Le cœur d'uvloop repose sur deux choix d'architecture pour atteindre cette vitesse. Premièrement, c'est un drop-in replacement écrit entièrement en Cython. Cython compile du code similaire à du Python en extensions C hautement optimisées. Ça élimine l'overhead de l'interpréteur Python standard lors de l'exécution des hot paths du scheduler. Les event loops en pur Python passent beaucoup de temps à créer des objets internes et à gérer l'état de l'interpréteur juste pour traiter des événements réseau de routine. Cython supprime tout ça. À chaque fois que la loop vérifie un socket ou réveille une task, elle exécute du code C natif au lieu de passer par du pur Python. Deuxièmement, uvloop délègue les interactions avec le système d'exploitation à une librairie C appelée libuv. Si ce nom te dit quelque chose, c'est parce que libuv est le moteur d'I/O async qui fait tourner Node.js. Il est battle-tested, ultra optimisé pour les workloads réseau intensifs, et gère tous les détails cross-platform complexes du networking async. En enveloppant libuv dans un shell Cython très léger, uvloop apporte exactement le même profil de performance directement dans Python. Le résultat sur l'architecture est massif. En contournant le scheduler en pur Python et en s'appuyant sur un moteur C compilé, uvloop rend tes applications asyncio au moins deux fois plus rapides. Dans de nombreux scénarios de benchmark impliquant une forte concurrency de connexions, ça permet à Python de rivaliser avec les performances de langages compilés comme Go. Tu obtiens la vélocité développeur de Python avec la vitesse d'exécution brute du networking en C natif. La transition ne nécessite aucun changement dans ta business logic, tes requêtes de base de données, ou tes endpoints d'API. Ce qu'il faut retenir ici, c'est que les bottlenecks de performance dans l'async Python standard sont rarement liés à la syntaxe du langage, mais plutôt au moteur d'exécution, et remplacer ce moteur te donne des vitesses de C natif tout en préservant tes abstractions Python existantes. Si tu veux soutenir l'émission, tu peux chercher DevStoriesEU sur Patreon. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de builder !
2

Intégration d'uvloop

3m 55s

Apprenez à intégrer uvloop dans votre application Python. Cet épisode couvre l'approche EventLoopPolicy pour remplacer de manière transparente la boucle d'événements par défaut.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. Python async haute performance, épisode 2 sur 17. L'optimisation de performance la plus puissante pour ton application Python async ne nécessite aucun changement d'architecture, aucun refactoring, et aucune configuration complexe. Elle tient en exactement deux lignes de code. Aujourd'hui, on parle de l'intégration de uvloop. L'event loop de la librairie standard asyncio est écrite en pur Python. uvloop est un drop-in replacement construit au-dessus de libuv, le même moteur qui fait tourner d'autres runtimes à haute concurrence. Il permet d'exécuter ton code Python async standard beaucoup plus rapidement. Remplacer le mécanisme de scheduling principal de ton application demande de dire à Python d'abandonner son comportement par défaut avant même qu'il ne commence à vraiment travailler. Dans un script d'entry point de serveur web, comme le fichier main pour une application FastAPI ou aiohttp, tu implémentes ce remplacement en utilisant une event loop policy. Une event loop policy est un objet de configuration global à l'intérieur du module standard asyncio. Elle dicte quel type d'event loop est instancié à chaque fois que l'application en demande une nouvelle. Pour remplacer la loop, tu importes le module asyncio et le module uvloop. Ensuite, tu appelles la fonction set event loop policy sur le module asyncio. Tu lui passes une nouvelle instance de l'event loop policy fournie par le module uvloop. Voici le point clé. Tu dois définir cette policy très tôt. L'appel doit être tout en haut de ton script d'exécution principal, juste après tes imports. L'event loop policy n'affecte que la création de nouvelles loops. Si tu attends pour définir la policy que ton framework web ait déjà démarré, ou qu'un driver de base de données async se soit initialisé, la loop standard en pur Python est probablement déjà en train de tourner. Changer la policy à ce moment-là ne fait rien à la loop existante. Ton code va soit ignorer uvloop complètement, soit se retrouver avec des event loops mélangées qui causent des deadlocks et des connexions cassées. Il y a une alternative à l'approche par policy. Au lieu de changer les règles globales pour la création de loops, tu peux créer explicitement une seule instance de uvloop. Tu fais ça en appelant la fonction new event loop directement depuis le module uvloop. Une fois que tu as cet objet loop en mémoire, tu le passes à asyncio en appelant la fonction set event loop. Pourquoi est-ce que tu choisirais une approche plutôt que l'autre ? Définir l'event loop policy est un override global. Ça garantit que n'importe quelle librairie tierce, background task, ou composant de framework dans ton process qui demande une nouvelle loop à asyncio recevra une uvloop en toute sécurité. C'est le choix standard pour un serveur web où tu veux des performances uniformes sur toute la stack applicative. L'approche explicite avec new event loop est plus ciblée. Elle injecte une instance spécifique plutôt que de changer les règles de la factory. Tu utilises cette méthode explicite quand tu gères des environnements complexes avec plusieurs threads, ou quand tu as besoin d'un contrôle strict sur exactement quelle loop tourne dans un contexte isolé sans muter l'état global du process. Pour des applications web standards, l'override de la policy est tout ce dont tu as besoin. Le mécanisme exact que tu choisis pour intégrer uvloop importe moins que le timing auquel tu l'appliques. L'event loop policy dicte les fondations de toute ton architecture async, ça doit donc être la toute première instruction que ton application exécute avant même qu'aucun contexte async ne soit établi. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de coder !
3

Introduction à asyncpg : Le protocole binaire

3m 22s

Explorez la conception fondamentale d'asyncpg. Nous discutons des raisons pour lesquelles le contournement de la DB-API standard au profit du protocole binaire de PostgreSQL permet des gains de performances massifs.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. Python Async haute performance, épisode 3 sur 17. Tu optimises tes index, tu mets à niveau ton réseau et tu peaufines tes requêtes, mais ton application continue de brûler des cycles CPU pour communiquer avec la base de données. Le plus gros bottleneck dans tes requêtes de base de données n'est souvent pas la latence réseau. C'est le temps que ton application passe à parser du texte. Pour éliminer cet overhead, on te présente asyncpg et son implémentation du protocole binaire de PostgreSQL. On croit souvent à tort qu'asyncpg n'est qu'un wrapper asynchrone autour de psycopg2. Ce n'est pas le cas. Ce n'est pas non plus une adaptation de la DB-API standard de Python. La spécification DB-API oriente naturellement les drivers de base de données vers certains patterns de gestion de données standardisés. asyncpg ignore complètement cette spécification. C'est une réécriture complète conçue exclusivement pour asyncio et PostgreSQL, qui contourne les interfaces de base de données standards pour parler à Postgres selon ses propres règles. Pour comprendre pourquoi ce design est important, regarde comment les drivers traditionnels gèrent le transfert de données. La plupart des drivers de base de données parlent à PostgreSQL en utilisant un format texte. Quand tu fais une requête à la base de données pour un nombre, un timestamp ou un array complexe, la base de données prend sa représentation interne en mémoire de ces données et la convertit en string. Ensuite, elle envoie cette string sur le réseau. Quand ton application Python la reçoit, le driver doit parser cette string de texte pour la reconvertir en un integer, un objet datetime ou une list Python. Imagine cette approche traditionnelle comme une équipe qui dépend d'un traducteur pour chaque conversation interne. La base de données lit ses structures de données natives, les écrit sous forme de documents texte standardisés et les envoie sur le réseau. Ton application Python reçoit ces documents et traduit laborieusement le texte pour le reconvertir en ses propres objets structurés en mémoire. Tout cet encodage, ce stringifying et ce parsing brûlent du temps CPU et consomment de la mémoire. asyncpg résout ce problème en parlant directement le protocole binaire frontend et backend de PostgreSQL. Il force la base de données à utiliser exclusivement des inputs et outputs binaires. Au lieu de s'appuyer sur un traducteur, la base de données et le driver parlent exactement le même langage natif. Si tu fais une requête pour un integer 64 bits, PostgreSQL envoie les raw bytes qui représentent cet integer. asyncpg lit ces bytes directement dans un objet integer Python. Il n'y a pas de formatage de string. Il n'y a pas de parsing de texte. Cette compréhension native s'étend aux données complexes. Quand tu demandes un bloc JSON, un universally unique identifier ou un type de données géométrique, le protocole binaire garantit que le payload reste compact et strictement structuré. Le driver sait exactement combien de bytes lire pour chaque colonne sans jamais avoir à scanner de délimiteurs de texte. Voici l'idée clé. La vitesse d'asyncpg ne vient pas principalement de la nature non bloquante de l'asyncio de Python. Les gains de performance massifs viennent de la suppression totale de la couche de traduction de texte. Tu fais beaucoup moins de travail par ligne retournée. En forçant strictement le transfert de données binaires, ton application arrête de gaspiller des ressources à lire du texte et passe ce temps CPU à exécuter ta véritable business logic. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de développer !
4

Connexion et exécution de base

3m 18s

Démarrez avec asyncpg en vous connectant à une base de données et en exécutant des requêtes de base. Comprenez la syntaxe native des arguments Postgres.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. Python Async haute performance, épisode 4 sur 17. Si tu as l'habitude des drivers de base de données Python standards, la façon dont tu passes des variables dans tes requêtes va te surprendre, et ton code va tout de suite planter. Les drivers Python standards se basent sur des marqueurs de formatage de string, mais cette librairie parle directement au moteur de base de données. Aujourd'hui, on va voir la connexion et l'exécution de base. Pour commencer à parler à ta base de données, tu utilises la fonction connect de asyncpg. Tu fais un await sur cette fonction et tu lui passes un Data Source Name, qui est une URI de connexion Postgres standard. Ça ressemble exactement à une adresse web. Ça commence par postgresql deux-points slash slash, suivi de ton username, un deux-points, ton mot de passe, une arobase, l'adresse du host, et enfin un slash avec le nom de la base de données. Faire un await sur cette fonction établit le lien réseau et te donne un objet connection actif. Maintenant, tu veux insérer un nom d'utilisateur et une date de naissance dans une table. C'est là que la syntaxe de la query change. N'utilise pas pourcent s ou des points d'interrogation pour tes paramètres de query. Comme asyncpg contourne délibérément l'API de base de données Python standard, il te force à utiliser les placeholders Postgres natifs. Tu écris signe dollar un, signe dollar deux, et ainsi de suite. Ta query string ressemblera à un statement insert standard, mais les valeurs seront dollar un et dollar deux. Pour lancer cette query sans demander de données en retour, tu fais un await sur la méthode execute de ton objet connection. Tu passes d'abord la query string, suivie des vraies variables pour le nom et la date de naissance. La méthode execute lance le statement et ignore toute donnée tabulaire. Elle retourne simplement une string de statut de Postgres, quelque chose comme insert zéro un. Elle ne retourne pas les rows de la base de données. Si tu as besoin que la base de données génère un ID unique pour ce nouvel utilisateur, et que tu as besoin de récupérer cet ID dans Python, execute n'est pas le bon outil. Tu modifies ta query SQL pour ajouter une clause returning id à la fin. Comme tu attends maintenant des données en retour, tu utilises la méthode fetchval. La méthode fetchval lance la query et retourne exactement une valeur spécifique. Elle regarde la première row retournée, prend la première colonne, et te donne juste cette donnée. C'est parfait pour récupérer un user ID nouvellement généré. Si tu as besoin de plus que juste l'ID, peut-être que tu veux les defaults de la base de données pour plusieurs colonnes, tu utilises fetchrow à la place. Faire un await sur fetchrow retourne un seul objet record contenant toutes les colonnes de cette première row. Tu peux accéder aux données à l'intérieur de ce record exactement comme un dictionnaire Python, en utilisant les noms de colonnes comme clés. Quand tu as fini d'insérer tes données, tu dois faire un await sur la méthode close de l'objet connection pour nettoyer la socket réseau et libérer les ressources de la base de données. Voici le point clé. Te forcer à utiliser des placeholders natifs avec le signe dollar n'est pas juste une bizarrerie de style. Ça permet à asyncpg de contourner complètement l'interpolation de string côté client, en mappant les types Python directement aux formats binaires Postgres pour une vitesse maximale et une protection complète contre les injections SQL. Merci d'avoir écouté, happy coding tout le monde !
5

Conversion de types native

3m 26s

Découvrez comment asyncpg mappe automatiquement les types de données PostgreSQL vers des objets Python natifs, éliminant ainsi le besoin d'une analyse ORM complexe.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. Python Async Haute Performance, épisode 5 sur 17. Tu écris une requête SQL raw, tu l'exécutes, et tu te prépares à parser manuellement des strings de dates, à splitter des arrays séparés par des virgules, et à caster des valeurs monétaires. Mais tu n'as pas à le faire. Tu n'as pas besoin d'un ORM pour récupérer des objets Python entièrement typés depuis ta base de données. Asyncpg gère ça automatiquement grâce à la conversion de type native. Quand asyncpg communique avec PostgreSQL, il utilise le protocole binaire de la base de données. Il sait exactement quel type de données chaque colonne représente. Au lieu de te renvoyer des strings de texte raw qui nécessitent un parsing secondaire en Python, asyncpg traduit directement les types PostgreSQL en objets de la librairie standard Python. Cette traduction se fait automatiquement dans les deux sens. Quand tu passes un objet Python comme paramètre de requête, asyncpg l'encode dans le bon format binaire PostgreSQL. Quand la base de données répond, asyncpg décode les données binaires pour retrouver le type Python correspondant. Imagine un scénario où tu fetch un profil utilisateur depuis ta base de données. Ta requête SQL demande un username, un array de tags utilisateur, la date de création du compte, et la dernière adresse IP connue. Avec beaucoup de drivers de base de données, tu récupérerais des strings que tu devrais parser manuellement. Avec asyncpg, le record de résultat est déjà typé. Le username est une string Python standard. La colonne des tags, qui est un array dans PostgreSQL, arrive sous forme de list native de strings Python. La date de création est un objet datetime standard. L'adresse IP, stockée comme un type inet dans la base de données, se mappe directement aux objets ipaddress built-in de Python. Tu as zéro logique de parsing à écrire pour ça. Il y a un mapping strict pour les nombres qui prend certains développeurs au dépourvu. Si ta colonne PostgreSQL est définie comme numeric, elle ne se convertit pas en un float Python. Asyncpg mappe le type numeric de PostgreSQL directement vers la classe Python decimal point Decimal. Ça préserve la précision exacte. Si tu requêtes des données financières ou des mesures précises, tu ne perdras pas de données à cause des erreurs d'arrondi de type floating-point. Les types floating-point standards dans Postgres, comme real ou double precision, se mappent bien aux floats Python. Voici l'info clé pour d'autres types spécifiques. Si tu sélectionnes une colonne UUID native, tu reçois un objet Python uuid point UUID, et pas une représentation en string générique. Les dates deviennent des objets datetime point date. Les intervals Postgres se mappent parfaitement aux timedeltas Python. Les données binaires stockées dans une colonne bytea sont converties directement en bytes Python. Les colonnes JSON et JSONB se comportent un peu différemment. Asyncpg convertit les données JSON et JSONB en strings Python standards par défaut. Il ne les parse pas automatiquement en dicts Python. Tu reçois la string raw, que tu peux ensuite passer au module json standard de Python si tu as besoin de manipuler les données imbriquées. S'appuyer sur cette traduction de type binaire garde ta logique applicative clean et transfère le fardeau du type safety au driver de base de données, là où est sa place. C'est tout pour cette fois. Merci d'avoir écouté, et continue à builder !
6

Codecs de types personnalisés

3m 52s

Apprenez à définir des conversions de données personnalisées dans asyncpg. Cet épisode explique comment utiliser set_type_codec pour décoder automatiquement le JSONB en dictionnaires Python.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. High-Performance Python Async, épisode 6 sur 17. Tu fais une query sur une base de données pour un user record et tu récupères une raw string en retour. À chaque fois, tu écris le même boilerplate code pour parser cette string en un dictionary avant de pouvoir vraiment utiliser les données. Tu peux apprendre à ton database driver à parler nativement le format de données de ton application, ce qui t'évite de faire du parsing manuel à chaque query. Ça se fait en utilisant des Custom Type Codecs. Quand tu fais une query sur PostgreSQL en utilisant le driver, les types de base comme les entiers et le texte sont traduits automatiquement. Mais quand tu utilises des types de base de données riches comme JSON, le driver a besoin de savoir comment tu veux que ces données soient représentées en Python. Un custom type codec agit comme une couche de traduction. Il se place entre la connexion à la base de données et la logique de ton application. Pour configurer ça, tu utilises une méthode qui s'appelle set type codec. Tu appelles cette méthode directement sur un objet de connexion actif. Elle a besoin de quatre informations principales. Premièrement, tu fournis le nom du type de la base de données, comme la string jsonb. Deuxièmement, tu spécifies le schéma où ce type se trouve. Pour les types intégrés de PostgreSQL, c'est le schéma pg catalog. Ensuite, tu fournis la logique de traduction en passant une fonction encoder et une fonction decoder. L'encoder définit comment Python envoie les données à la base de données. Il prend ton objet Python et renvoie un format que PostgreSQL comprend, généralement une string. Si tu travailles avec JSON, tu peux simplement passer la fonction json dumps de la librairie standard. Le decoder définit comment les données reviennent de la base de données. Il reçoit la raw string de PostgreSQL et renvoie l'objet Python que tu veux. Pour JSON, tu passes juste la fonction json loads. Prends l'exemple d'un système qui stocke des préférences utilisateur non structurées. Dans ta base de données, la colonne preferences est définie comme jsonb. Dans ton application Python, tu gères les préférences comme un dictionary standard. Une fois que ton codec est configuré, tu exécutes une select query basique pour un utilisateur. La colonne preferences arrive dans ton application déjà structurée comme un dictionary Python. Quand tu lances une insert query, tu passes directement un dictionary comme argument de la query. Le driver déclenche automatiquement ton encoder, convertit le dictionary en JSON, et l'envoie à la base de données. Tu n'appelles jamais un encoder ou un decoder manuellement dans ton code d'exécution de query. Voici le point essentiel. Les Custom Type Codecs ne s'appliquent pas globalement à tout le database driver. La méthode set type codec modifie uniquement la connexion spécifique sur laquelle elle est appelée. Si tu configures un codec sur une connexion, une deuxième connexion n'en saura rien et renverra à nouveau des raw strings. Ce comportement pose souvent problème quand les développeurs introduisent un connection pool. Tu ne peux pas configurer un pool avec un seul appel de méthode de codec. À la place, tu dois enregistrer ton custom codec à chaque fois qu'une nouvelle connexion est établie. Tu fais ça en définissant une fonction d'initialisation. Dans cette fonction, tu acceptes le nouvel objet de connexion et tu appelles set type codec dessus. Ensuite, tu passes cette fonction d'initialisation à ta logique de création de pool. Le pool exécute ta fonction automatiquement à chaque fois qu'il ouvre une nouvelle connexion, ce qui garantit que tes codecs sont toujours présents et actifs. Pousser la sérialisation des données dans la couche du driver via des Custom Type Codecs élimine la logique de parsing répétitive et garantit que tes formats de données restent parfaitement synchronisés dans toute ton application. Merci d'avoir écouté, happy coding tout le monde !
7

Codecs avancés avec PostGIS

3m 34s

Plongez au cœur des codecs de types personnalisés en mappant les types de géométrie PostGIS de PostgreSQL vers des objets Python Shapely en utilisant le format binaire.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. Python Async haute performance, épisode 7 sur 17. Gérer des coordonnées géographiques, ça implique généralement du parsing de string un peu galère. Tu écris une query, tu récupères une string de texte géante pleine de nombres, et ensuite tu crames des cycles CPU pour la décortiquer juste pour trouver une latitude et une longitude. Les codecs avancés avec PostGIS dans asyncpg résolvent complètement cette friction. Quand tu récupères des custom types depuis PostgreSQL, tu as affaire à des codecs. Un codec dit à asyncpg comment traduire un data type PostgreSQL en un objet Python. Il y a souvent une confusion ici entre le format texte et le format binaire. Le format texte est le format par défaut pour beaucoup d'outils de base de données. Avec PostGIS, une query texte renvoie du Well-Known Text. Ça ressemble au mot POINT suivi des coordonnées entre parenthèses. C'est lisible par un humain, mais le lire dans le code nécessite d'allouer des strings, de chercher des parenthèses et de caster des caractères en floats. Le parsing de texte est lent, et ça scale mal quand tu traites des milliers de rows. Ce que tu veux, c'est le format binaire. PostGIS utilise un standard appelé Well-Known Binary. Quand tu configures ton codec dans asyncpg, tu mets explicitement l'argument format sur binary. La base de données zappe la génération de texte et te passe directement des raw bytes. Maintenant, il te faut un moyen de traduire ces bytes en quelque chose que ton application Python peut vraiment utiliser. C'est là qu'une library Python comme Shapely entre en jeu. Shapely gère la géométrie complexe, et elle sait déjà exactement comment lire le Well-Known Binary. Tu dis à asyncpg d'utiliser un custom type codec en appelant la méthode set type codec directement sur ta connexion à la base de données. Tu spécifies le nom du type PostgreSQL, qui est geometry. Ensuite, tu fournis une fonction encoder et une fonction decoder. Le decoder prend la byte string brute de PostgreSQL et la passe directement au binary reader de Shapely. Imagine que tu fasses une query pour trouver la localisation de l'Empire State Building. Sans custom binary codec, ta base de données renvoie une string, ton application la parse, construit un dictionnaire, et finit par créer un objet geometry. Avec le binary codec en place, tu exécutes un select statement standard. Asyncpg intercepte les données binaires, lance ta fonction decoder, et te donne instantanément un objet Shapely Point complètement formé. Tu peux immédiatement accéder aux coordonnées x et y sur l'objet retourné. Le processus fonctionne dans l'autre sens pour les données qui retournent dans la base de données. Ta fonction encoder prépare les données Python à envoyer à PostgreSQL. Les objets Shapely implémentent un standard appelé la geo interface. C'est une structure de dictionnaire Python courante utilisée pour la géométrie. Ton encoder prend n'importe quel objet Python qui supporte cette interface, utilise Shapely pour le sérialiser en Well-Known Binary, et renvoie ces raw bytes à la base de données. Tu ne touches jamais à une représentation texte. Si tu trouves ces deep dives utiles, tu peux soutenir l'émission en cherchant DevStoriesEU sur Patreon. Voici le point clé. En utilisant strictement le format binaire pour les custom type codecs, tu élimines le bottleneck de sérialisation, ce qui permet à ta base de données et à ton application Python de communiquer à la vitesse de la mémoire. Merci d'avoir écouté, et happy coding tout le monde !
8

Gestion des transactions

3m 18s

Maîtrisez les transactions de base de données dans asyncpg. Nous couvrons le comportement d'auto-commit et la manière d'exécuter plusieurs requêtes en toute sécurité à l'aide de gestionnaires de contexte asynchrones.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. High-Performance Python Async, épisode 8 sur 17. Tu exécutes une query d'update, puis ton script plante avant que la query suivante ne tourne. Tu vérifies la base de données en t'attendant à ce que rien n'ait changé, mais le premier update est bien là, sauvegardé définitivement. Dans asyncpg, si tu ne demandes pas explicitement une transaction, chaque query est committed à la milliseconde où elle se termine. Gérer les transactions, c'est comme ça que tu corriges ce comportement. Par défaut, asyncpg fonctionne en mode auto-commit. Ça veut dire que si tu écris du code qui exécute trois queries l'une après l'autre, tu ne fais pas tourner un seul bloc de logique. Tu exécutes trois opérations complètement isolées. Si la deuxième query échoue, la première est déjà finalisée dans la base de données. Oublier un bloc de transaction explicite est une cause fréquente de corruption du state de l'application. Prends un scénario où tu transfères de l'argent entre deux comptes bancaires. Tu dois déduire des fonds du compte A, puis ajouter ces mêmes fonds au compte B. Les deux updates doivent réussir ensemble, ou échouer ensemble. Si la déduction réussit mais que l'ajout échoue, l'argent disparaît tout simplement. Pour lier ces opérations entre elles, tu utilises la méthode transaction sur ton objet connection. Cette méthode retourne un context manager asynchrone. Dans ton code, tu écris async with connection dot transaction, suivi de deux points, et ensuite tu indentes tes queries associées. Quand Python entre dans ce bloc, asyncpg dit à PostgreSQL de démarrer une nouvelle transaction. À l'intérieur du bloc, tu exécutes la query de déduction, suivie de la query d'ajout. Si les deux queries tournent sans problème et que Python atteint le bas du bloc, asyncpg lance automatiquement une commande commit. Les changements sur les deux comptes deviennent visibles pour le reste de la base de données exactement au même moment. Voici le point clé. Si un problème survient à l'intérieur de ce bloc, la base de données reste en sécurité. Le problème pourrait être une violation de contrainte de la base de données, un timeout réseau, ou même une pure erreur Python comme une variable manquante ou une division par zéro. Si une exception est levée, le context manager l'intercepte. Il envoie automatiquement une commande rollback à PostgreSQL, effaçant la déduction du compte A, puis laisse l'exception Python continuer à remonter ta call stack. Tu peux aussi imbriquer ces blocs en toute sécurité. Si tu ouvres un nouveau context manager de transaction alors que tu es déjà dans un bloc de transaction actif, asyncpg ne s'emmêle pas les pinceaux. À la place, il crée automatiquement un savepoint dans la base de données. Un savepoint agit comme un marque-page au sein d'une transaction en cours. Si le bloc interne rencontre une erreur, il fait un rollback de l'état de la base de données uniquement jusqu'à ce marque-page. Le bloc externe reste complètement intact et peut toujours commit son propre travail, ou choisir d'échouer selon ta logique. Tu n'as pas besoin d'écrire des commandes savepoint manuelles, tu as juste à imbriquer tes blocs async with. Au final, le context manager de transaction lie de manière permanente le state de ta base de données au state d'exécution de ton Python, garantissant qu'une exception Python non gérée est une garantie absolue contre les updates partiels de la base de données. C'est tout pour cet épisode. Merci d'avoir écouté, et continue à développer !
9

Pool de connexions

3m 17s

Mettez à l'échelle votre application avec le pool de connexions intégré d'asyncpg. Apprenez à gérer efficacement les connexions à la base de données dans les services web à fort trafic.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. Python Async haute performance, épisode 9 sur 17. Ouvrir une nouvelle connexion à la base de données pour chaque web request entrante est un moyen très efficace de faire complètement crasher ton serveur. Rien que l'overhead réseau va bloquer ton application, et tu vas vite épuiser la limite de connexions sur ta base de données. La solution à ça, c'est le Connection Pooling. Quand tu utilises une library comme asyncpg pour parler à PostgreSQL, établir une connexion brute est une opération coûteuse. Ça demande un TCP handshake, une négociation sécurisée et l'authentification à la base de données. Si tu fais tourner un web service à fort trafic, tu ne peux tout simplement pas te permettre de payer cette taxe de latence sur chaque HTTP request. À la place, tu dois maintenir une collection stable de connexions prêtes à l'emploi. Dans asyncpg, tu fais ça en utilisant la fonction create pool. Généralement, tu appelles cette fonction une seule fois pendant la phase de startup de ton application. Tu fournis tes credentials de base de données, le host et le port, et asyncpg lance un ensemble de connexions idle en background. À partir de là, tes route handlers et tes background tasks ne créent plus jamais de nouvelle connexion from scratch. Ils font juste emprunter celles qui existent déjà. Il y a un piège classique ici qui fait trébucher pas mal de devs. Ne confonds pas l'emprunt d'une connexion avec l'ouverture d'une transaction en base de données. Ce sont des opérations complètement séparées. Quand tu empruntes une connexion au pool, tu réserves uniquement le socket réseau pour ton usage exclusif et temporaire. Si ton opération demande de l'atomicité sur plusieurs queries, tu dois quand même démarrer explicitement une transaction sur cette connexion empruntée spécifique. Pense à un web service FastAPI ou aiohttp à fort trafic. Disons que tu as un endpoint qui accepte un entier, fait une query à la base de données pour calculer la puissance de deux de ce nombre, et renvoie le résultat. Quand une request tape ton endpoint, tu appelles la méthode acquire sur ton objet pool. Tu fais ça en utilisant un context manager asynchrone. Ça extrait temporairement une connexion du pool. Ensuite, tu utilises cette instance de connexion spécifique pour exécuter ta query en base de données pour calculer la puissance de deux. Une fois que le bloc de code se termine, le context manager libère automatiquement la connexion. Il nettoie son état et la rend au pool, ce qui la rend immédiatement disponible pour la prochaine HTTP request entrante. Si un pic de trafic soudain frappe ton web server et que toutes les connexions du pool sont déjà sorties, la request suivante ne crashe pas. Elle attend. L'appel à acquire va simplement mettre l'exécution en pause jusqu'à ce qu'une autre request se termine et rende sa connexion. Voici le point clé. Le connection pool ne fait pas juste gagner du temps sur les network handshakes. Il agit comme un throttle de concurrence strict et fiable. Il protège ta base de données PostgreSQL pour éviter qu'elle soit submergée par un flood de trafic inattendu. Si tu configures ton pool pour contenir exactement vingt connexions, la base de données ne verra jamais plus de vingt queries actives en simultané venant de cette instance d'application, peu importe les milliers de requests simultanées qui tapent ton web server. Merci d'avoir écouté. À la prochaine !
10

Mise en cache des requêtes préparées

3m 38s

Comprenez comment asyncpg optimise l'analyse des requêtes avec des requêtes préparées automatiques, et pourquoi des poolers externes comme PgBouncer peuvent causer des conflits.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. High-Performance Python Async, épisode 10 sur 17. Ton application tourne parfaitement en local. Mais dès que tu la déploies en production derrière un routeur de connexion à la base de données, tu commences à voir des crashs aléatoires. Les logs se plaignent de statements qui existent déjà, ou de statements introuvables. Pourtant, rien n'a changé dans ton code. Ça arrive à cause d'une optimisation intégrée qui s'appelle le Prepared Statement Caching. Pour comprendre l'erreur, on doit d'abord regarder ce que fait asyncpg en coulisses. À chaque fois que tu envoies une query via asyncpg, la librairie la traduit automatiquement en un prepared statement sur le serveur PostgreSQL. Normalement, quand une base de données reçoit une query, elle doit parser le texte, analyser la syntaxe et construire un plan d'exécution. Ça prend du temps. En préparant le statement, PostgreSQL fait ce gros du travail une seule fois et lui assigne un nom interne. Pour toutes les exécutions futures de cette query exacte, asyncpg envoie seulement les nouveaux paramètres et le nom du statement. Ça permet de sauter complètement la phase de parsing et d'offrir un énorme boost de performance. asyncpg garde un cache de ces prepared statements en mémoire, étroitement lié à la connexion active à la base de données. Voici le point clé. Le problème vient d'un conflit entre la façon dont asyncpg gère ses connexions internes, et le fonctionnement des poolers externes comme PgBouncer. asyncpg part du principe qu'il a une connexion physique dédiée et persistante au serveur Postgres. Quand il crée un prepared statement, il part du principe que le statement restera disponible sur cette connexion exacte jusqu'à ce qu'elle se ferme. Maintenant, introduisons PgBouncer dans l'architecture, plus précisément en mode transaction. PgBouncer se place entre ton application et la base de données. Il maintient un petit pool de vraies connexions Postgres et les partage entre des milliers de requêtes clientes entrantes. En mode transaction, PgBouncer donne à ton application une connexion physique à la base de données uniquement pour la durée d'une seule transaction. Au moment où cette transaction commit, PgBouncer récupère la connexion physique et la passe à un client complètement différent. Ça casse le cache des prepared statements. Ton application envoie une query. asyncpg la prépare et la met en cache sur la connexion physique A. La transaction se termine. Quelques secondes plus tard, ton application envoie exactement la même query. asyncpg se souvient qu'il a déjà préparé cette query, donc il dit à Postgres d'exécuter le statement sauvegardé. Mais cette fois, PgBouncer a routé ton application vers la connexion physique B. La connexion B n'a aucune trace de ce prepared statement. La base de données lève une erreur disant que le statement n'existe pas. L'inverse est aussi vrai. Un client différent pourrait être routé vers la connexion A, essayer de préparer un statement avec le même nom interne, et déclencher une erreur disant que le statement existe déjà. Le fix est simple mais demande un compromis. Tu dois dire à asyncpg de désactiver cette optimisation. Quand tu initialises ta connexion ou ton pool de connexions asyncpg, tu passes un argument spécifique qui met la taille du statement cache à zéro. Ça désactive complètement le caching automatique des prepared statements. Tes queries seront maintenant parsées par PostgreSQL à chaque fois qu'elles tournent. Tu sacrifies un peu de performance de parsing, mais ton application deviendra instantanément stable sur des connexions distribuées. Si tes connexions à la base de données sont routées dynamiquement par transaction, ton application ne peut plus supposer que le serveur de base de données se souvient de quoi que ce soit entre les queries. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de développer !
11

Tableaux Postgres et clauses IN

3m 42s

Résolvez l'erreur de syntaxe la plus courante lors de la migration vers asyncpg. Apprenez à filtrer correctement les requêtes par rapport à une liste de valeurs en utilisant ANY().

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. Python Async haute performance, épisode 11 sur 17. Tu écris une query SQL standard pour filtrer des records. Tu passes une liste de valeurs dans les paramètres de ta query, exactement comme tu l'as fait dans des dizaines d'autres libraries de base de données. Postgres te renvoie immédiatement une syntax error. Le problème ne vient pas de tes données. Le problème, c'est la façon dont tu gères les Arrays Postgres et les clauses IN. C'est la syntax error numéro un pour les devs qui migrent vers asyncpg depuis d'autres interfaces de base de données. Dans beaucoup d'anciennes libraries, le driver intercepte ta query string. Si tu passes une liste Python à une clause IN, la library réécrit la string SQL à la volée. Elle étend ta liste en une string de paramètres individuels séparés par des virgules avant de l'envoyer à la base de données. Asyncpg ne fait pas ça. Il repose entièrement sur les prepared statements natifs côté serveur de Postgres. Voici le point clé. En SQL Postgres standard, l'opérateur IN demande une liste de valeurs scalaires séparées par des virgules et entourées de parenthèses. Il n'accepte pas un seul objet array. Quand tu passes une liste Python à asyncpg comme paramètre, asyncpg mappe cette liste directement vers un array Postgres natif. Ta query finit par être évaluée comme une expression IN un objet array, ce qui est une syntaxe invalide. Postgres s'attend à une expression IN valeur un, valeur deux. Pour corriger ça, tu dois arrêter d'utiliser l'opérateur IN pour les listes paramétrées. À la place, utilise la fonction any de Postgres. La logique change : au lieu de demander si une valeur est IN une liste, on demande si une valeur est égale à n'importe quel élément à l'intérieur d'un array. L'opérateur any est conçu spécifiquement pour fonctionner avec les types array de Postgres. Il évalue la valeur à gauche, vérifie l'array à droite, et renvoie true s'il trouve un match. Tu dois aussi dire à Postgres quel type d'array il reçoit en castant le paramètre. Si tu t'attends à un array de text strings, tu castes ton paramètre explicitement en un text array. Ce type casting explicite garantit que Postgres sait exactement comment planifier et exécuter la query sans avoir à deviner le data type sous-jacent du flux binaire entrant. Imagine un scénario où tu filtres une liste de produits. Tu veux matcher la catégorie du produit avec une liste dynamique de catégories sélectionnées par l'utilisateur. Tu écris une query pour sélectionner les produits où la catégorie est égale à any paramètre un, et tu castes le paramètre un en un text array. Dans ton code Python, tu appelles la méthode fetch de ta base de données. Tu passes la query string comme premier argument, et ta liste Python de strings, comme électronique et livres, comme deuxième argument. Asyncpg package ta liste Python dans un text array Postgres binaire et l'envoie sur le réseau comme un seul paramètre. Postgres reçoit la query, voit le text array, et matche efficacement les catégories en utilisant la fonction any. Cette approche est strictement meilleure que la manipulation de strings. Parce que la structure de la query ne change jamais, peu importe le nombre de catégories dans la liste, Postgres parse et planifie le statement exactement une fois. La base de données met en cache le query plan, ce qui fait gagner du temps d'exécution sur les appels suivants. Tu élimines aussi le risque d'injection SQL, puisque les données sont transmises sous forme binaire, complètement séparées du texte de la query. Si tu passes une liste Python à un paramètre de base de données, traite-la comme un array natif, caste-la dans le bon type, et évalue-la avec la fonction any. C'est tout pour cet épisode. Merci d'avoir écouté, et continue à développer !
12

Objets Record vs Named Tuples

3m 27s

Explorez la conception unique des objets Record d'asyncpg. Comprenez pourquoi la notation pointée est omise par défaut et comment l'activer avec des classes personnalisées.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. High-Performance Python Async, épisode 12 sur 17. Tu fais une query sur ta base de données, tu récupères une row, et instinctivement, tu tapes le nom de la variable point id. Immédiatement, Python lève une AttributeError. Tes données sont là, mais tu ne peux pas y accéder comme tu t'y attends. C'est à cause du design volontaire des objets Record par rapport aux named tuples. Quand tu fetch des données avec asyncpg, il ne te retourne pas des dictionnaires Python standards, et il ne retourne pas non plus des named tuples. À la place, il retourne un objet custom ultra optimisé qui s'appelle un Record. Si tu as l'habitude d'autres drivers de base de données ou d'ORM, tu t'attends peut-être à ce que la dot-notation fonctionne out of the box. Avec un Record asyncpg, ça casse intentionnellement. Tu te demandes peut-être pourquoi le driver n'utilise pas simplement un named tuple Python standard, vu que les named tuples supportent nativement la dot-notation. La raison, c'est la performance pure. Un named tuple oblige Python à générer une toute nouvelle structure de classe pour chaque combinaison unique de colonnes retournée par une query. Si ton application exécute des centaines de queries avec des structures différentes, générer ces classes dynamiques crée un overhead d'exécution massif. La librairie est construite pour une vitesse absolue, donc elle contourne complètement ce bottleneck en retournant son propre type Record compilé à la place. Cet objet Record custom agit comme un hybride rapide. Il supporte l'indexation par entier exactement comme un tuple standard, ce qui veut dire que tu peux accéder à la première colonne en utilisant l'index zéro. Mais il fournit aussi un mapping de type dictionnaire. Tu accèdes à tes colonnes en utilisant la bracket notation, en passant le nom de la colonne sous forme de string. Voici le point clé. Les créateurs ont volontairement désactivé la dot-notation sur ces objets pour protéger ton application des conflits de namespace. Pense aux méthodes de mapping standards en Python, comme keys, items, values, ou get. Si ta table de base de données se trouve avoir une colonne nommée keys, et que le driver supportait la dot-notation, taper record point keys créerait un conflit structurel. Python ne saurait pas si tu veux la valeur de la base de données ou la méthode built-in. En forçant la bracket notation basée sur des strings, le driver garantit que tes noms de colonnes n'entreront jamais en conflit avec les attributs Python standards. Cependant, si tu contrôles entièrement ton schéma de base de données, que tu sais de source sûre que tu n'utilises pas de mots réservés Python comme noms de colonnes, et que tu as absolument besoin de la dot-notation pour ta codebase, tu as une porte de sortie. Tu peux override le comportement par défaut. Quand tu établis ta connexion à la base de données, tu peux fournir un paramètre spécifique qui s'appelle record class. Pour implémenter ça, tu écris une classe custom qui hérite directement du Record asyncpg de base. À l'intérieur de cette nouvelle classe, tu implémentes la méthode Python built-in qui s'appelle double underscore getattr. Tu indiques à cette méthode de prendre le nom de l'attribut demandé et de simplement le chercher en utilisant le fallback sécurisé de la bracket notation. Une fois que tu passes cette classe custom au setup de ta connexion, chaque row retournée par tes queries sera une instance de ton objet custom. Python te permettra alors d'utiliser la dot-notation, en routant de manière transparente la requête d'attribut via ta méthode custom pour fetch les données de la colonne sous-jacente. Au final, la bracket notation stricte dans un Record par défaut n'est pas un oubli, mais une limite de sécurité structurelle qui garantit que ton accès aux données reste prévisible, peu importe comment ton schéma de base de données évolue. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de développer !
13

Streaming de résultats avec les curseurs

3m 39s

Évitez l'épuisement de la mémoire lors de l'interrogation d'ensembles de données massifs. Apprenez à utiliser les curseurs d'asyncpg pour streamer les résultats par morceaux.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. Python Async haute performance, épisode 13 sur 17. Tu dois exporter une table utilisateur d'un million de lignes vers un fichier, donc tu lances un fetch standard sur la base de données. Soudain, ton application Python sature toute la RAM disponible et le serveur plante. Tu ne peux pas charger des datasets massifs en mémoire d'un seul coup, et c'est exactement pour ça qu'on utilise le streaming de résultats avec des cursors. Normalement, quand tu exécutes une query dans asyncpg, tout le result set est récupéré sur le réseau depuis PostgreSQL et gardé dans la mémoire de Python. La base de données construit la réponse complète, l'envoie, et asyncpg construit des objets Python pour chaque ligne avant même que ton code puisse traiter la première. C'est tout à fait gérable pour cinquante lignes. Mais c'est un problème immédiat pour cinq millions de lignes. Pour éviter de faire planter le serveur, tu dois siroter les données plutôt que de les engloutir d'un coup. Un cursor de base de données te donne un pointeur vers les résultats de la query sur le serveur PostgreSQL. Au lieu de tout récupérer, le cursor permet à ton application Python de fetch les données de manière incrémentale. Dans asyncpg, tu fais ça en appelant la méthode cursor sur ta connexion. Tu fournis ta query SQL et tous les arguments requis. Cette méthode retourne un itérateur asynchrone. Tu écris une boucle async-for pour itérer sur ce cursor. Sous le capot, asyncpg va automatiquement fetch les lignes par petits batches gérables depuis PostgreSQL. Ton code traite quelques lignes, les écrit dans ton fichier d'export, et passe à la suite. Python nettoie les anciennes lignes de la mémoire, ce qui garde l'empreinte mémoire totale de ton application complètement plate, peu importe la taille de la table. Il y a une règle stricte ici qui piège beaucoup de développeurs. Si tu essaies d'itérer un cursor directement sur une connexion standard, asyncpg lève immédiatement une InterfaceError. Le message d'erreur indiquera que les cursors ne peuvent pas être utilisés en dehors d'une transaction. Voici le point clé. Les cursors PostgreSQL sont structurellement liés aux transactions de la base de données. Quand une transaction fait un commit ou un rollback, PostgreSQL détruit tous les cursors actifs qui y sont associés. Par défaut, asyncpg fonctionne en mode auto-commit. Ça veut dire que chaque query individuelle que tu lances est enveloppée dans sa propre petite transaction invisible qui se ferme au moment où la query se termine. Si asyncpg t'autorisait à ouvrir un cursor en mode auto-commit, cette transaction implicite se terminerait instantanément, et PostgreSQL tuerait ton cursor avant même que tu puisses fetch une seule ligne. Pour que les cursors fonctionnent, tu dois gérer explicitement la limite de la transaction. Tu fais ça en ouvrant un context manager asynchrone en utilisant la méthode transaction sur ta connexion. Une fois que tu es bien à l'intérieur de ce bloc de transaction, tu appelles la méthode cursor et tu lances ta boucle async-for. Parce que la transaction reste ouverte pendant toute la durée du bloc du context manager, ton cursor reste en vie sur le serveur PostgreSQL, ce qui te permet de streamer le million de lignes en toute sécurité. Il y a une rare exception à cette règle. PostgreSQL supporte une fonctionnalité où tu peux déclarer un cursor SQL brut avec la phrase WITH HOLD. Ça dit au moteur de base de données de matérialiser le résultat et de garder le cursor en vie même après que la transaction soit terminée. Faire ça consomme des ressources de la base de données, et ça contourne l'efficacité du streaming standard. Pour presque toutes les tâches de streaming dans asyncpg, le bloc de transaction explicite est l'approche requise. Si tu trouves ces épisodes utiles et que tu veux soutenir l'émission, cherche DevStoriesEU sur Patreon. N'oublie pas qu'un cursor transforme ton interaction avec la base de données d'une allocation mémoire massive et risquée en un pipeline contrôlé et persistant qui peut traiter en toute sécurité n'importe quel volume de données. C'est tout pour aujourd'hui. Merci d'avoir écouté, et continue de développer !
14

Ingestion ultra-rapide avec COPY

3m 25s

Boostez vos pipelines d'ingestion de données. Nous explorons le protocole COPY de PostgreSQL pour charger des données en masse de manière exponentiellement plus rapide qu'avec des instructions INSERT.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. Python Async haute performance, épisode 14 sur 17. Tu dois déplacer un million de lignes vers ou depuis ta base de données. Si ton premier réflexe est de faire un batch d'une liste massive de statements insert ou de fetcher toutes les lignes dans une immense liste Python, tu choisis le chemin le plus lent possible. Il y a un mécanisme conçu spécifiquement pour contourner cet overhead. Aujourd'hui, on parle d'ingestion ultra-rapide avec COPY. COPY est un protocole spécifique à Postgres pour le transfert de données en bulk. Quand tu lances des statements insert ou select standards, Postgres doit parser la query, la planifier et l'exécuter. Faire ça de façon répétée ajoute un overhead massif. Le protocole COPY saute complètement le pipeline de query standard. Il ouvre un stream direct vers le storage layer, en déplaçant les données dans un format hautement optimisé. Grâce à ça, c'est des ordres de grandeur plus rapide que des bulk inserts. Dans asyncpg, tu pousses des données dans la base en utilisant une méthode qui s'appelle copy to table. Tu fournis le nom de la table cible et une data source. Cette source peut être un file path local, un objet file-like, ou un itérateur asynchrone qui yield des records. Si tu le pointes vers un fichier CSV local, Postgres gère le parsing nativement. Tu n'as pas besoin d'ouvrir le fichier en Python, de parser les lignes et de les mapper à des variables. Le driver de la base de données streame les bytes bruts du fichier directement vers le serveur. Tu peux aussi passer une simple liste Python de tuples si tes données sont déjà en mémoire, et asyncpg va la streamer en utilisant le protocole COPY sous le capot. Sortir des données est tout aussi rapide. Si tu as besoin d'un export complet, tu utilises copy from table. Ça prend le contenu entier d'une table et ça le balance vers un fichier ou un stream. Cependant, dumper une table entière est rarement ce dont tu as vraiment besoin. Généralement, tu veux des données filtrées ou jointes. C'est là que copy from query entre en jeu. Une idée reçue courante est que cette méthode dumpe seulement les résultats de la query dans un fichier statique. C'est tout simplement faux. Même si ça peut écrire directement vers un file path, tu peux aussi fournir un callback. Asyncpg va exécuter la query et streamer les résultats par chunks vers ton callback, te permettant de traiter un dataset massif à la volée sans jamais garder le result set complet dans la mémoire système. Imagine un scénario où tu dois générer un rapport CSV de tous les utilisateurs actifs. Une approche standard est d'exécuter une select query, de fetcher cent mille lignes dans Python, de les formater en utilisant le module CSV, et de les écrire sur le disque. Ça consomme énormément de mémoire et de CPU. Voici le point clé. Tu peux complètement sauter le processing Python. Tu appelles copy from query, tu lui passes ton select statement spécifique, tu mets le paramètre format sur CSV, et tu fournis un file path de sortie. Postgres exécute la query, formate les résultats en CSV nativement sur le serveur de base de données, et asyncpg streame le texte final directement sur ton disque dur. Ton application Python agit comme un simple pipe, en faisant presque zéro manipulation de données. Tu devrais continuer à utiliser des statements insert et select standards pour la logique applicative de tous les jours, mais au moment où le volume de données brutes devient ton bottleneck, passe au protocole COPY pour bypasser complètement le parser SQL. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de développer !
15

Listen et Notify asynchrones

3m 00s

Débloquez des architectures événementielles en temps réel directement dans PostgreSQL. Apprenez à utiliser add_listener d'asyncpg pour une messagerie pub/sub instantanée.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. Python Async Haute Performance, épisode 15 sur 17. Tu te tournes vers un message broker séparé comme Redis dès que tu as besoin d'événements en temps réel. Mais si ton application utilise déjà une base de données, tu risques d'ajouter de la complexité à ton infrastructure pour absolument rien. PostgreSQL intègre directement un message bus en temps réel. Aujourd'hui, on parle de Listen et Notify asynchrones. Postgres supporte nativement le pattern publish-subscribe avec deux commandes, listen et notify. La librairie asyncpg expose cette fonctionnalité en Python via une méthode appelée add listener. Au lieu d'écrire une boucle qui poll une table de la base de données toutes les quelques secondes pour vérifier s'il y a de nouvelles données, tu enregistres un callback Python asynchrone sur un channel spécifique. Quand un événement se produit dans Postgres, il broadcast un message sur ce channel, et ton callback Python s'exécute immédiatement. Voici le point clé. Un listener n'est pas attaché globalement à ton application, et il n'est pas attaché à un connection pool. Il est lié à une connexion à la base de données spécifique et individuelle. C'est un point of failure courant. Si tu tires une connexion d'un pool, que tu enregistres ton listener, puis que tu relâches la connexion dans le pool, le listener saute. Pour utiliser cette feature de manière fiable, tu dois acquérir une connexion depuis asyncpg, appeler la méthode add listener dessus, et garder cette connexion ouverte indéfiniment. Ça devient un pipeline d'écoute dédié. Prenons un scénario pratique. Tu as un background worker qui doit se réveiller et traiter des records à chaque fois qu'une nouvelle ligne est insérée dans une table jobs. Au lieu de faire du polling, tu mets en place un trigger sur la base de données. À chaque insert, le trigger exécute une commande notify sur un channel appelé new jobs. Il envoie aussi un court payload texte, comme l'identifiant unique de la nouvelle ligne. Dans ton code Python, tu écris une fonction callback asynchrone. Cette fonction s'attend à recevoir quatre arguments de asyncpg : l'objet de connexion, l'identifiant du process Postgres, le nom du channel, et le payload texte lui-même. Ensuite, tu acquiers ta connexion dédiée. Tu appelles add listener sur cette connexion, en lui passant la string du channel new jobs avec ta fonction callback. Enfin, tu laisses le script tourner, généralement en faisant un await sur un event asynchrone qui ne se déclenche jamais. Ton process Python reste maintenant complètement idle. Il n'utilise presque aucune ressource CPU et arrête de marteler la base de données avec des requêtes vides. Au moment où une transaction commit une nouvelle ligne job, Postgres pousse la notification sur le socket réseau ouvert. Asyncpg lit ce socket et planifie immédiatement ton callback sur l'event loop Python, en lui passant le nouvel identifiant du job. La véritable force de ce pattern, c'est la cohérence transactionnelle. Si une transaction en base de données fait un rollback, toutes les commandes notify exécutées pendant cette transaction sont automatiquement ignorées par Postgres. Ça garantit que tes workers Python ne se réveilleront et ne réagiront qu'aux données qui ont été sauvegardées sur le disque avec succès. C'est tout pour cet épisode. Merci d'avoir écouté, et continue à développer !
16

Télémétrie et journalisation des requêtes

3m 29s

Obtenez une observabilité approfondie des performances de votre base de données. Découvrez comment utiliser les écouteurs de journaux d'asyncpg pour suivre les requêtes lentes et surveiller la télémétrie d'exécution.

Télécharger
Bonjour, ici Alex de DEV STORIES DOT EU. High-Performance Python Async, épisode 16 sur 17. Pour découvrir pourquoi ta base de données est lente, tu n'as pas besoin de deviner. Tu pourrais te fier aux logs côté serveur, mais ils ratent la latence réseau et l'overhead côté Python. Mesurer manuellement les performances autour de chaque appel à la base de données encombre rapidement ta logique métier. La solution, c'est la télémétrie et le query logging. On confond souvent les log listeners et les query loggers. Les log listeners captent les notices du serveur Postgres. Les query loggers capturent la télémétrie d'exécution côté client. Ils gèrent deux flux d'informations totalement distincts. Les query loggers gèrent la télémétrie. Tu attaches un callback à ta connexion en utilisant la méthode add query logger. Une fois attaché, à chaque fois qu'une requête termine son exécution, asyncpg passe automatiquement un objet LoggedQuery à ce callback. Ça se passe de façon globale pour cette connexion, totalement découplé de la fonction spécifique qui fait la requête à la base de données. L'objet LoggedQuery contient trois données critiques. Il contient le texte exact de la requête SQL, les arguments passés à cette requête, et le temps d'exécution. Les arguments sont capturés exactement comme ton application les a fournis. Ça t'évite d'écrire manuellement une logique de string formatting juste pour comprendre quels paramètres ont causé une réponse lente. Imagine un environnement de production où tu dois capturer toute requête qui prend plus de 500 millisecondes. Tu définis une fonction Python standard qui accepte deux paramètres : la connexion à la base de données et l'objet LoggedQuery. Dans cette fonction, tu vérifies l'attribut du temps d'exécution. Si le temps dépasse 0,5 seconde, tu écris le texte de la requête, les arguments et la durée exacte dans ton système de monitoring d'application. Tu passes ensuite ce callback à la méthode add query logger. Maintenant, ton application traque automatiquement les requêtes lentes, silencieusement en arrière-plan. Si jamais tu as besoin d'arrêter ce tracking, tu passes simplement le même callback à la méthode remove query logger. Maintenant, la deuxième partie de tout ça, c'est le log listener. Alors que le query logger gère le timing côté client, le log listener gère la messagerie côté serveur. Parfois, Postgres envoie des messages qui ne sont pas des erreurs et qui ne retournent pas de lignes de données. Ce sont des notices asynchrones, des warnings, ou des messages de log personnalisés générés directement par le moteur de base de données. Pour capturer ces messages, tu attaches un callback en utilisant la méthode add log listener. Quand Postgres émet une notice ou un warning, asyncpg déclenche ce callback. Il passe la connexion et un objet message spécifique à ta fonction. Ça donne à ton application une visibilité immédiate sur les warnings au niveau de la base de données, de façon totalement indépendante des résultats de tes requêtes standards. Tout comme pour le query logger, tu peux détacher ce callback plus tard en utilisant remove log listener. Voici le point clé. Le query logging côté client te donne la vraie durée d'exécution vécue par ton application Python, en évitant complètement de devoir deviner entre le délai réseau et le traitement de la base de données. Merci de m'avoir écouté. Prenez soin de vous tous.
17

Sécurisation des connexions avec SSL

3m 28s

Assurez-vous que vos connexions à la base de données sont sécurisées. Nous couvrons la configuration du contexte SSL et la manière d'imposer le TLS direct lors de la connexion à des bases de données cloud.

Télécharger
Bonjour, c'est Alex de DEV STORIES DOT EU. Python Async haute performance, épisode 17 sur 17. Te connecter à une base de données cloud managée sans SSL, c'est comme crier tes credentials de base de données au milieu d'une pièce bondée. Tu as besoin de chiffrement, mais le configurer correctement mène souvent à des erreurs de connexion bizarres ou à des valeurs par défaut pas sécurisées. Aujourd'hui, on sécurise les connexions avec SSL dans asyncpg. C'est facile de se planter sur la façon de configurer SSL dans ta logique de connexion. Si tu utilises des URI de connexion, asyncpg parse nativement les query parameters standard de PostgreSQL pour sslmode, comme mettre sslmode à require. Ça marche pour les setups de base. Mais quand tu as besoin d'un contrôle précis, comme pour te connecter de façon sécurisée à une base de données cloud managée en utilisant un bundle d'Autorité de Certification custom, les strings d'URI standard ne suffisent plus. Pour ça, tu utilises le paramètre ssl de façon programmatique. Le paramètre ssl dans les fonctions de connexion d'asyncpg dicte comment TLS est négocié. Il accepte deux types de valeurs. Le premier type, c'est un preset sous forme de string. Tu peux passer la string prefer, qui tente une connexion SSL mais fait un fallback vers du non chiffré si le serveur ne le supporte pas. Tu peux passer require, qui force le chiffrement mais skip la vérification de l'identité du serveur. Ou tu peux passer verify-full, qui force le chiffrement et valide strictement le certificat du serveur par rapport aux racines de confiance. Voici le point clé. Quand ton scénario demande une Autorité de Certification custom, ne te repose pas sur les presets sous forme de string. À la place, tu crées un objet SSLContext Python standard. Tu configures cet objet avec tes fichiers de certificats custom, tu forces une vérification stricte, et ensuite tu passes cet objet Python directement dans le paramètre ssl d'asyncpg. Ça te donne un contrôle exact sur le handshake cryptographique, en bypassant tous les certificats système par défaut. Ça couvre les règles de chiffrement, mais comment la connexion démarre-t-elle vraiment ? Ça nous amène au paramètre direct TLS. Par défaut, PostgreSQL utilise un protocole appelé STARTTLS. Le client fait une connexion en plain-text, demande au serveur s'il supporte le chiffrement, et si le serveur dit oui, ils upgradent la connexion vers TLS. Cependant, les setups de proxy modernes, comme certains connection poolers ou load balancers cloud, s'attendent souvent à une connexion TLS directe dès le tout premier octet. Ils ne veulent pas de la négociation en plain-text. Si ton infrastructure est construite comme ça, tu passes true au paramètre de connexion direct TLS. Quand tu fais ça, asyncpg skip la négociation STARTTLS et initie immédiatement un handshake TLS brut. Naturellement, ça ne marche que si tu fournis aussi une configuration ssl valide. Si tu actives direct TLS mais que tu laisses le paramètre ssl vide, la connexion va échouer. Quand tu sécurises tes connexions à la base de données, rappelle-toi que même si les presets sous forme de string sont pratiques, passer un objet SSLContext explicite est le seul moyen de garantir absolument que ton application fait confiance à la bonne identité du serveur. Comme c'est le dernier épisode de la série, je t'encourage à explorer la documentation officielle d'asyncpg et à tester ces paramètres de connexion en pratique. Tu peux visiter devstories dot eu pour suggérer des sujets pour nos futures séries. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de développer !