Volver al catálogo
Season 13 17 Episodios 1h 10m 2026

High-Performance Python Async

Edición 2026. Un análisis en profundidad sobre cómo acelerar asyncio de Python con uvloop y cómo interactuar directamente con PostgreSQL utilizando el protocolo binario de asyncpg.

Python Core Programación asíncrona
High-Performance Python Async
Reproduciendo ahora
Click play to start
0:00
0:00
1
La necesidad de velocidad: arquitectura de uvloop
Descubre las diferencias arquitectónicas entre el asyncio estándar de Python y uvloop. Exploramos cómo uvloop aprovecha Cython y libuv para alcanzar un rendimiento similar al de Go.
4m 03s
2
Integrando uvloop
Aprende a integrar uvloop en tu aplicación Python. Este episodio cubre el enfoque de EventLoopPolicy para reemplazar sin problemas el bucle de eventos por defecto.
4m 20s
3
Introducción a asyncpg: el protocolo binario
Explora el diseño fundamental de asyncpg. Debatimos por qué omitir la DB-API estándar en favor del protocolo binario de PostgreSQL produce mejoras masivas de rendimiento.
3m 58s
4
Conexión y ejecución básica
Empieza a usar asyncpg conectándote a una base de datos y ejecutando consultas básicas. Comprende la sintaxis nativa de argumentos de Postgres.
4m 01s
5
Conversión de tipos nativa
Descubre cómo asyncpg mapea automáticamente los tipos de datos de PostgreSQL a objetos nativos de Python, eliminando la necesidad de un análisis complejo por parte del ORM.
3m 53s
6
Codecs de tipos personalizados
Aprende a definir conversiones de datos personalizadas en asyncpg. Este episodio explica cómo usar set_type_codec para decodificar automáticamente JSONB en diccionarios de Python.
4m 29s
7
Codecs avanzados con PostGIS
Profundiza en los codecs de tipos personalizados mapeando los tipos de geometría PostGIS de PostgreSQL a objetos Shapely de Python utilizando el formato binario.
4m 29s
8
Gestión de transacciones
Domina las transacciones de base de datos en asyncpg. Cubrimos el comportamiento del auto-commit y cómo ejecutar múltiples consultas de forma segura utilizando gestores de contexto asíncronos.
4m 09s
9
Pool de conexiones
Escala tu aplicación con el pool de conexiones integrado de asyncpg. Aprende a gestionar eficientemente las conexiones a la base de datos en servicios web de alto tráfico.
3m 55s
10
Caché de prepared statements
Comprende cómo asyncpg optimiza el análisis de consultas con prepared statements automáticos, y por qué los poolers externos como PgBouncer pueden causar conflictos.
4m 48s
11
Arrays de Postgres y cláusulas IN
Resuelve el error de sintaxis más común al migrar a asyncpg. Aprende a filtrar correctamente las consultas frente a una lista de valores utilizando ANY().
4m 32s
12
Objetos Record vs Named Tuples
Explora el diseño único de los objetos Record de asyncpg. Comprende por qué se omite por defecto la notación de puntos y cómo habilitarla con clases personalizadas.
4m 02s
13
Streaming de resultados con cursores
Evita el agotamiento de memoria al consultar conjuntos de datos masivos. Aprende a utilizar los cursores de asyncpg para hacer streaming de resultados bloque por bloque.
4m 47s
14
Ingesta ultrarrápida con COPY
Potencia tus pipelines de ingesta de datos. Exploramos el protocolo COPY de PostgreSQL para realizar cargas masivas de datos de forma exponencialmente más rápida que con sentencias INSERT.
3m 58s
15
Listen y Notify asíncronos
Desbloquea arquitecturas orientadas a eventos en tiempo real directamente dentro de PostgreSQL. Aprende a usar add_listener de asyncpg para mensajería pub/sub instantánea.
3m 33s
16
Telemetría y logging de consultas
Obtén una observabilidad profunda del rendimiento de tu base de datos. Descubre cómo usar los log listeners de asyncpg para rastrear consultas lentas y monitorizar la telemetría de ejecución.
3m 45s
17
Asegurando conexiones con SSL
Asegúrate de que las conexiones a tu base de datos sean seguras. Cubrimos la configuración del contexto SSL y cómo forzar TLS directo al conectarse a bases de datos en la nube.
3m 49s

Episodios

1

La necesidad de velocidad: arquitectura de uvloop

4m 03s

Descubre las diferencias arquitectónicas entre el asyncio estándar de Python y uvloop. Exploramos cómo uvloop aprovecha Cython y libuv para alcanzar un rendimiento similar al de Go.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python async de alto rendimiento, episodio 1 de 17. ¿Qué pasaría si pudieras duplicar el rendimiento de tu código async en Python sin reescribir una sola función? Esa es precisamente la promesa de la tecnología que analizaremos hoy: la arquitectura de uvloop. Cuando los desarrolladores hablan de Python async estándar, suelen confundir dos capas distintas. La primera capa es la API. Esta incluye las keywords async y await que escribes, los futures y las tasks. La segunda capa es el propio event loop. El event loop es el scheduler interno que monitoriza los sockets, gestiona las conexiones de red y decide qué task se ejecuta a continuación. Python estándar proporciona tanto la interfaz como un event loop por defecto escrito en Python puro. Aquí está la clave. La interfaz y el event loop están desacoplados. Python te permite cambiar el scheduler subyacente sin modificar la sintaxis que escribes. Piénsalo como un coche. La API async es el volante, los pedales y el salpicadero. Interactúas con ellos directamente. El event loop es el motor bajo el capó. Cambiar el event loop por defecto de Python por uvloop es como meterle un motor V8 a tu vehículo. Sigues conduciendo y frenando exactamente igual, pero el coche se mueve mucho más rápido. El núcleo de uvloop se basa en dos decisiones arquitectónicas para lograr esta velocidad. Primero, es un drop-in replacement escrito completamente en Cython. Cython compila código similar a Python en extensiones C altamente optimizadas. Esto elimina el overhead del intérprete estándar de Python al ejecutar los hot paths del scheduler. Los event loops en Python puro dedican mucho tiempo a crear objetos internos y gestionar el estado del intérprete solo para manejar eventos de red rutinarios. Cython elimina todo eso. Cada vez que el loop comprueba un socket o despierta una task, ejecuta código C nativo en lugar de pasar por Python puro. Segundo, uvloop delega las interacciones reales con el sistema operativo a una librería C llamada libuv. Si ese nombre te suena, es porque libuv es el motor de async I/O que hace funcionar Node.js. Está probado en producción, altamente optimizado para workloads intensivos de red, y gestiona todos los detalles complejos multiplataforma del async networking. Al envolver libuv en una capa compacta de Cython, uvloop lleva exactamente ese mismo perfil de rendimiento directo a Python. El resultado a nivel de arquitectura es enorme. Al saltarse el scheduler de Python puro y depender de un motor C compilado, uvloop hace que tus aplicaciones asyncio sean al menos el doble de rápidas. En muchos escenarios de benchmark que implican una alta concurrency de conexiones, permite que Python rivalice con el rendimiento de lenguajes compilados como Go. Obtienes la velocidad de desarrollo de Python con la velocidad de ejecución bruta del networking nativo en C. La transición requiere cero cambios en tu business logic, tus queries de base de datos o tus endpoints de la API. La conclusión fundamental aquí es que los bottlenecks de rendimiento en Python async estándar rara vez tienen que ver con la sintaxis del lenguaje, sino más bien con el motor de ejecución, y reemplazar ese motor te da velocidades nativas de C mientras conservas tus abstracciones de Python existentes. Si quieres apoyar el programa, puedes buscar DevStoriesEU en Patreon. Eso es todo por este episodio. Gracias por escuchar, y sigue construyendo.
2

Integrando uvloop

4m 20s

Aprende a integrar uvloop en tu aplicación Python. Este episodio cubre el enfoque de EventLoopPolicy para reemplazar sin problemas el bucle de eventos por defecto.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python async de alto rendimiento, episodio 2 de 17. La optimización de rendimiento más potente para tu aplicación async en Python no requiere cambios de arquitectura, ni refactoring, ni configuraciones complejas. Ocupa exactamente dos líneas de código. Hoy hablamos de integrar uvloop como drop-in. El event loop de la standard library asyncio está escrito en Python puro. Uvloop es un drop-in replacement construido sobre libuv, el mismo motor que impulsa otros runtimes de alta concurrencia. Hace que tu código async estándar de Python se ejecute muchísimo más rápido. Reemplazar el mecanismo de scheduling principal de tu aplicación requiere decirle a Python que abandone su comportamiento por defecto antes de que empiece a hacer cualquier trabajo real. En el script del entry point de un servidor web, como el archivo main de una aplicación FastAPI o aiohttp, implementas este reemplazo usando una event loop policy. Una event loop policy es un objeto de configuración global dentro del módulo estándar asyncio. Dicta qué tipo de event loop se instancia cada vez que la aplicación pide uno nuevo. Para cambiar el loop, importas el módulo asyncio y el módulo uvloop. Luego, llamas a la función set event loop policy en el módulo asyncio. Le pasas una instancia nueva de la event loop policy que proporciona el módulo uvloop. Aquí está la clave. Tienes que configurar esta policy muy pronto. La llamada tiene que estar en lo más alto de tu script de ejecución main, justo después de los imports. La event loop policy solo afecta a la creación de loops nuevos. Si esperas a configurar la policy hasta después de que tu framework web ya haya arrancado, o después de que se haya inicializado un driver de base de datos async, lo más probable es que el loop estándar de Python puro ya se esté ejecutando. Cambiar la policy en ese punto no le hace nada al loop existente. Tu código ignorará uvloop por completo, o acabará con event loops mixtos que causan deadlocks y conexiones rotas. Hay una alternativa al enfoque de la policy. En lugar de cambiar las reglas globales para la creación de loops, puedes crear explícitamente una única instancia de uvloop. Esto lo haces llamando a la función new event loop directamente desde el módulo uvloop. Una vez que tienes ese objeto loop en memoria, se lo pasas a asyncio llamando a la función set event loop. ¿Por qué elegirías un enfoque sobre el otro? Configurar la event loop policy es un override global. Garantiza que cualquier library de terceros, background task o componente del framework en tu proceso que le pida a asyncio un loop nuevo, recibirá de forma segura un uvloop. Es la opción estándar para un servidor web donde quieres un rendimiento uniforme en todo el stack de la aplicación. El enfoque de new event loop explícito es más limitado. Inyecta una instancia específica en lugar de cambiar las reglas de la factory. Usas este método explícito cuando estás gestionando entornos complejos con múltiples threads, o cuando necesitas un control estricto sobre qué loop exacto se está ejecutando en un contexto aislado sin mutar el estado global del proceso. Para aplicaciones web estándar, el override de la policy es todo lo que necesitas. El mecanismo exacto que elijas para hacer el drop-in de uvloop importa menos que el timing de cuándo lo aplicas. La event loop policy dicta la base de toda tu arquitectura async, así que tiene que ser la primerísima instrucción que ejecute tu aplicación antes de que se establezca ningún contexto async. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue programando!
3

Introducción a asyncpg: el protocolo binario

3m 58s

Explora el diseño fundamental de asyncpg. Debatimos por qué omitir la DB-API estándar en favor del protocolo binario de PostgreSQL produce mejoras masivas de rendimiento.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python Async de Alto Rendimiento, episodio 3 de 17. Optimizas tus índices, mejoras tu red y ajustas tus consultas, pero tu aplicación sigue consumiendo ciclos de CPU en la comunicación con la base de datos. El mayor cuello de botella en tus consultas a la base de datos no suele ser la latencia de la red. Es el tiempo que tu aplicación dedica a parsear texto. Para eliminar este overhead, presentamos asyncpg y su implementación del protocolo binario de PostgreSQL. Es un error común pensar que asyncpg es simplemente un wrapper asíncrono sobre psycopg2. No lo es. Tampoco es una adaptación de la DB-API estándar de Python. La especificación DB-API guía por naturaleza a los drivers de bases de datos hacia ciertos patrones estandarizados de manejo de datos. asyncpg ignora por completo esta especificación. Es una reescritura desde cero diseñada exclusivamente para asyncio y PostgreSQL, saltándose las interfaces de base de datos estándar para hablar con Postgres en sus propios términos. Para comprender por qué este diseño es importante, observa cómo los drivers tradicionales manejan la transferencia de datos. La mayoría de los drivers de bases de datos se comunican con PostgreSQL usando un formato basado en texto. Cuando consultas a la base de datos para obtener un número, un timestamp o un array complejo, la base de datos toma su representación interna en memoria de esos datos y la convierte en un string. Luego, envía ese string a través de la red. Cuando tu aplicación Python lo recibe, el driver tiene que parsear ese string de texto y convertirlo de nuevo en un entero de Python, un objeto datetime o una lista. Imagina este enfoque tradicional como un equipo que depende de un traductor para cada conversación interna. La base de datos lee sus estructuras de datos nativas, las escribe como documentos de texto estandarizados y las envía a través de la red. Tu aplicación Python recibe estos documentos y, con gran esfuerzo, traduce el texto de nuevo a sus propios objetos de memoria estructurada. Todo este encoding, conversión a string y parsing consume tiempo de CPU y memoria. asyncpg resuelve este problema hablando directamente el protocolo binario de frontend y backend de PostgreSQL. Obliga a la base de datos a usar exclusivamente entrada y salida binarias. En lugar de depender de un traductor, la base de datos y el driver hablan exactamente el mismo lenguaje nativo. Si consultas un entero de sesenta y cuatro bits, PostgreSQL envía los raw bytes que representan ese entero. asyncpg lee esos bytes directamente en un objeto entero de Python. No hay formateo de strings. No hay parsing de texto. Esta comprensión nativa se extiende a datos complejos. Cuando pides un bloque JSON, un identificador único universal o un tipo de dato geométrico, el protocolo binario garantiza que el payload se mantenga compacto y estrictamente estructurado. El driver sabe exactamente cuántos bytes leer para cada columna sin tener que buscar nunca delimitadores de texto. Aquí está la clave. La velocidad de asyncpg no proviene principalmente de la naturaleza no bloqueante de asyncio de Python. Las enormes mejoras de rendimiento provienen de eliminar por completo la capa de traducción de texto. Estás haciendo mucho menos trabajo por cada fila devuelta. Al imponer estrictamente la transferencia de datos binarios, tu aplicación deja de desperdiciar recursos leyendo texto y dedica ese tiempo de CPU a ejecutar tu lógica de negocio real. Eso es todo por este episodio. ¡Gracias por escuchar, y sigue desarrollando!
4

Conexión y ejecución básica

4m 01s

Empieza a usar asyncpg conectándote a una base de datos y ejecutando consultas básicas. Comprende la sintaxis nativa de argumentos de Postgres.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python Async de alto rendimiento, episodio 4 de 17. Si estás acostumbrado a los drivers de bases de datos estándar de Python, la forma de pasar variables a las queries te va a pillar desprevenido, y tu código se romperá inmediatamente. Los drivers estándar de Python se basan en marcadores de string formatting, pero esta librería habla directamente con el motor de la base de datos. Hoy veremos la conexión y la ejecución básica. Para empezar a hablar con tu base de datos, usas la función connect de asyncpg. Haces un await de esta función y le pasas un Data Source Name, que es una connection URI estándar de Postgres. Esto es exactamente igual que una dirección web. Empieza con postgresql dos puntos barra barra, seguido de tu nombre de usuario, dos puntos, tu contraseña, una arroba, la dirección del host y, finalmente, una barra con el nombre de la base de datos. Hacer await de esta función establece el enlace de red y te da un objeto connection activo. Ahora quieres insertar un nombre de usuario y una fecha de nacimiento en una tabla. Aquí es donde cambia la sintaxis de la query. No utilices porcentaje s ni signos de interrogación para tus query parameters. Como asyncpg se salta deliberadamente la API de base de datos estándar de Python, te obliga a usar placeholders nativos de Postgres. Escribes signo de dólar uno, signo de dólar dos, y así sucesivamente. Tu query string se verá como una sentencia insert estándar, pero los valores serán dólar uno y dólar dos. Para ejecutar esta query sin pedir que te devuelva datos, haces un await del método execute en tu objeto connection. Pasas primero la query string, seguida de las variables reales para el nombre y la fecha de nacimiento. El método execute ejecuta la sentencia y descarta cualquier dato tabular. Simplemente devuelve un status string de Postgres, algo como insert zero one. No devuelve las filas reales de la base de datos. Si necesitas que la base de datos genere un ID único para este nuevo usuario, y necesitas que ese ID vuelva a Python, execute no es la herramienta adecuada. Cambias tu query SQL para añadir una cláusula returning id al final. Como ahora esperas recibir datos de vuelta, usas el método fetchval. El método fetchval ejecuta la query y devuelve exactamente un valor específico. Mira la primera fila devuelta, coge la primera columna y te da solo ese dato. Esto es perfecto para obtener un ID de usuario recién generado. Si necesitas algo más que el ID, tal vez quieras los defaults de la base de datos para varias columnas, entonces usas fetchrow. Hacer await de fetchrow devuelve un único objeto record que contiene todas las columnas de esa primera fila. Puedes acceder a los datos dentro de este record exactamente igual que en un diccionario de Python, usando los nombres de las columnas como keys. Cuando termines de insertar tus datos, debes hacer un await del método close en el objeto connection para limpiar el socket de red y liberar los recursos de la base de datos. Aquí está la clave. Obligarte a usar placeholders nativos con el signo de dólar no es solo una peculiaridad estilística. Permite a asyncpg saltarse por completo la string interpolation del lado del cliente, mapeando los tipos de Python directamente a formatos binarios de Postgres para obtener la máxima velocidad y una protección completa contra SQL injection. ¡Gracias por escuchar, happy coding a todos!
5

Conversión de tipos nativa

3m 53s

Descubre cómo asyncpg mapea automáticamente los tipos de datos de PostgreSQL a objetos nativos de Python, eliminando la necesidad de un análisis complejo por parte del ORM.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python Async de alto rendimiento, episodio 5 de 17. Escribes una query SQL raw, la ejecutas, y te preparas para parsear manualmente strings de fechas, hacer split de arrays separados por comas, y hacer cast de valores de moneda. Pero no tienes por qué hacerlo. No necesitas un ORM para obtener objetos de Python totalmente tipados de tu base de datos. Asyncpg lo gestiona automáticamente mediante conversión de tipos nativa. Cuando asyncpg se comunica con PostgreSQL, utiliza el protocolo binario de la base de datos. Sabe exactamente qué tipo de datos representa cada columna. En lugar de entregarte strings de texto raw que requieren un parseo secundario en Python, asyncpg traduce los tipos de PostgreSQL directamente a objetos de la librería estándar de Python. Esta traducción se realiza automáticamente en ambas direcciones. Cuando pasas un objeto de Python como parámetro de la query, asyncpg lo codifica en el formato binario correcto de PostgreSQL. Cuando la base de datos responde, asyncpg decodifica los datos binarios de vuelta al tipo de Python correspondiente. Imagina un escenario en el que haces un fetch del perfil de un usuario de tu base de datos. Tu query SQL pide un nombre de usuario, un array de etiquetas de usuario, la hora de creación de la cuenta y la última dirección IP conocida. En muchos drivers de bases de datos, recibirías strings que tendrías que parsear manualmente. Con asyncpg, el registro resultante ya viene tipado. El nombre de usuario es un string estándar de Python. La columna de etiquetas, que es un array en PostgreSQL, llega como una lista de strings nativa de Python. La hora de creación es un objeto datetime estándar. La dirección IP, almacenada como un tipo inet en la base de datos, se mapea directamente a objetos ipaddress built-in de Python. Escribes cero lógica de parseo para conseguir esto. Hay un mapeo estricto para los números que pilla a algunos desarrolladores desprevenidos. Si tu columna de PostgreSQL está definida como numeric, no se convierte en un float de Python. Asyncpg mapea el tipo numeric de PostgreSQL directamente a la clase decimal punto Decimal de Python. Esto preserva la precisión exacta. Si estás haciendo queries de registros financieros o mediciones precisas, no perderás datos por errores de redondeo de coma flotante. Los tipos de coma flotante estándar en Postgres, como real o double precision, sí se mapean a floats de Python. Aquí está la clave para otros tipos específicos. Si haces un select de una columna UUID nativa, recibes un objeto uuid punto UUID de Python, no una representación de string genérica. Las fechas se convierten en objetos datetime punto date. Los intervals de Postgres se mapean perfectamente a timedeltas de Python. Los datos binarios almacenados en una columna bytea se convierten directamente en bytes de Python. Las columnas JSON y JSONB se comportan de forma ligeramente diferente. Asyncpg convierte los datos JSON y JSONB en strings estándar de Python por defecto. No los parsea automáticamente a diccionarios de Python. Recibes el string raw, que luego puedes pasar al módulo json estándar de Python si necesitas manipular los datos anidados. Confiar en esta traducción de tipos binarios mantiene la lógica de tu aplicación limpia y traslada la carga del type safety al driver de la base de datos, que es donde corresponde. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue programando!
6

Codecs de tipos personalizados

4m 29s

Aprende a definir conversiones de datos personalizadas en asyncpg. Este episodio explica cómo usar set_type_codec para decodificar automáticamente JSONB en diccionarios de Python.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python Async de alto rendimiento, episodio 6 de 17. Consultas una base de datos para obtener un registro de usuario y recibes una raw string. Cada vez, escribes el mismo código boilerplate para parsear esa string a un diccionario antes de poder usar realmente los datos. Puedes enseñarle al driver de tu base de datos a hablar el formato de datos de tu aplicación de forma nativa, ahorrándote el parsing manual en cada query. Esto lo consigues usando Custom Type Codecs. Cuando haces una query a PostgreSQL usando el driver, los tipos básicos como enteros y texto se traducen automáticamente. Pero cuando usas tipos de base de datos complejos como JSON, el driver necesita saber cómo quieres que se representen esos datos en Python. Un Custom Type Codec actúa como una capa de traducción. Se sitúa entre la conexión a la base de datos y la lógica de tu aplicación. Para configurarlo, usas un método llamado set type codec. Llamas a este método directamente en un objeto de conexión activo. Requiere cuatro datos principales. Primero, proporcionas el nombre del tipo de base de datos, como la string jsonb. Segundo, especificas el schema donde reside este tipo. Para los tipos integrados de PostgreSQL, este es el schema pg catalog. A continuación, proporcionas la lógica de traducción pasando una función encoder y una función decoder. El encoder define cómo Python envía datos a la base de datos. Toma tu objeto de Python y devuelve un formato que PostgreSQL entiende, normalmente una string. Si estás trabajando con JSON, puedes simplemente pasar la función json dumps de la librería estándar. El decoder define cómo vuelven los datos de la base de datos. Recibe la raw string de PostgreSQL y devuelve tu objeto de Python deseado. Para JSON, simplemente pasas la función json loads. Considera un sistema que almacena preferencias de usuario no estructuradas. En tu base de datos, la columna de preferencias está definida como jsonb. En tu aplicación de Python, gestionas las preferencias como un diccionario estándar. Una vez que tu codec está configurado, ejecutas una query select básica para un usuario. La columna de preferencias llega a tu aplicación ya estructurada como un diccionario de Python. Cuando ejecutas una query insert, pasas un diccionario directamente como argumento de la query. El driver activa automáticamente tu encoder, convierte el diccionario a JSON y lo envía a la base de datos. Nunca llamas a un encoder o decoder manualmente en tu código de ejecución de la query. Aquí está la clave. Los Custom Type Codecs no se aplican globalmente a todo el driver de la base de datos. El método set type codec solo modifica la conexión específica en la que lo llamas. Si configuras un codec en una conexión, una segunda conexión no sabrá nada al respecto y devolverá raw strings de nuevo. Este comportamiento suele causar problemas cuando los desarrolladores introducen un connection pool. No puedes configurar un pool con una sola llamada al método del codec. En su lugar, debes registrar tu custom codec cada vez que estableces una nueva conexión. Consigues esto definiendo una función de inicialización. Dentro de esa función, aceptas el nuevo objeto de conexión y llamas a set type codec sobre él. Luego, pasas esta función de inicialización a la lógica de creación de tu pool. El pool ejecuta tu función automáticamente cada vez que abre una nueva conexión, garantizando que tus codecs estén siempre presentes y activos. Llevar la serialización de datos a la capa del driver mediante Custom Type Codecs elimina la lógica de parsing repetitiva y garantiza que tus formatos de datos permanezcan perfectamente sincronizados en toda tu aplicación. ¡Gracias por escuchar, feliz programación a todos!
7

Codecs avanzados con PostGIS

4m 29s

Profundiza en los codecs de tipos personalizados mapeando los tipos de geometría PostGIS de PostgreSQL a objetos Shapely de Python utilizando el formato binario.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python Async de alto rendimiento, episodio 7 de 17. Manejar coordenadas geográficas suele implicar un parsing de strings bastante engorroso. Escribes una query, recibes un string de texto gigante lleno de números, y luego quemas ciclos de CPU destripándolo solo para encontrar una latitud y una longitud. Los códecs avanzados con PostGIS dentro de asyncpg resuelven por completo esta fricción. Cuando extraes custom types de PostgreSQL, estás lidiando con códecs. Un códec le dice a asyncpg cómo traducir un data type de PostgreSQL a un objeto de Python. Aquí suele haber confusión entre el formato de texto y el formato binario. El formato de texto es el que viene por defecto en muchas herramientas de bases de datos. Con PostGIS, una query de texto devuelve Well-Known Text. Esto se ve como la palabra POINT seguida de las coordenadas entre paréntesis. Es human-readable, pero leerlo en código requiere reservar memoria para strings, buscar paréntesis y castear caracteres a números de coma flotante. Hacer parsing de texto es lento, y escala fatal cuando estás procesando miles de rows. Lo que quieres es el formato binario. PostGIS usa un estándar llamado Well-Known Binary. Cuando configuras tu códec en asyncpg, le pasas explícitamente el argumento format como binary. La base de datos se salta la generación de texto y te entrega los raw bytes. Ahora, necesitas una forma de traducir esos bytes a algo que tu aplicación de Python pueda usar de verdad. Aquí es donde entra en juego una librería de Python como Shapely. Shapely maneja geometría compleja, y ya sabe exactamente cómo leer Well-Known Binary. Le dices a asyncpg que use un códec de custom type llamando al método set type codec directamente en tu conexión a la base de datos. Especificas el nombre del type de PostgreSQL, que es geometry. Luego le pasas una función encoder y una función decoder. El decoder coge el byte string en crudo de PostgreSQL y se lo pasa directamente al binary reader de Shapely. Piensa en hacer una query de la ubicación del Empire State Building. Sin un códec binario custom, tu base de datos devuelve un string, tu aplicación hace el parsing, construye un diccionario y, al final, crea un objeto geometry. Con el códec binario ya configurado, ejecutas un statement select estándar. Asyncpg intercepta los datos binarios, ejecuta tu función decoder y te entrega un objeto Point de Shapely totalmente formado al instante. Puedes acceder inmediatamente a las coordenadas x e y en el objeto devuelto. El proceso funciona a la inversa para los datos que vuelven a la base de datos. Tu función encoder prepara los datos de Python para enviarlos a PostgreSQL. Los objetos de Shapely implementan un estándar llamado geo interface. Esta es una estructura de diccionario de Python muy común que se usa para geometría. Tu encoder coge cualquier objeto de Python que soporte esta interface, usa Shapely para serializarlo como Well-Known Binary, y envía esos raw bytes de vuelta a la base de datos. Nunca llegas a tocar una representación de texto. Si estos deep dives te están resultando útiles, puedes apoyar el programa buscando DevStoriesEU en Patreon. Aquí está la clave. Al usar estrictamente el formato binario para los códecs de custom types, eliminas el cuello de botella de la serialización, permitiendo que tu base de datos y tu aplicación de Python se comuniquen a la velocidad de la memoria. ¡Gracias por escuchar, happy coding a todos!
8

Gestión de transacciones

4m 09s

Domina las transacciones de base de datos en asyncpg. Cubrimos el comportamiento del auto-commit y cómo ejecutar múltiples consultas de forma segura utilizando gestores de contexto asíncronos.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. High-Performance Python Async, episodio 8 de 17. Ejecutas una query de update, y luego tu script falla antes de que se ejecute la siguiente query. Revisas la base de datos esperando que no haya cambiado nada, pero el primer update está justo ahí, guardado permanentemente. En asyncpg, si no pides explícitamente una transaction, cada query hace commit en el milisegundo en que termina. Gestionar las transactions es la forma de arreglar este comportamiento. Por defecto, asyncpg funciona en modo auto-commit. Esto significa que si escribes código que ejecuta tres queries una detrás de otra, no estás ejecutando un solo bloque de lógica. Estás ejecutando tres operaciones completamente aisladas. Si la segunda query falla, la primera query ya se ha finalizado en la base de datos. Olvidar un bloque de transaction explícito es una causa frecuente de que el estado de la aplicación se corrompa. Imagina un escenario en el que estás transfiriendo dinero entre dos cuentas bancarias. Tienes que deducir fondos de la cuenta A, y luego añadir esos mismos fondos a la cuenta B. Ambos updates deben tener éxito juntos, o ambos deben fallar juntos. Si la deducción tiene éxito pero la suma falla, el dinero simplemente desaparece. Para vincular estas operaciones, usas el método transaction en tu objeto connection. Este método devuelve un context manager asíncrono. En tu código, escribes async with connection punto transaction, seguido de dos puntos, y luego indentas tus queries relacionadas. Cuando Python entra en este bloque, asyncpg le dice a PostgreSQL que inicie una nueva transaction. Dentro del bloque, ejecutas la query de deducción, seguida de la query de suma. Si ambas queries se ejecutan sin problemas y Python llega al final del bloque, asyncpg emite automáticamente un comando commit. Los cambios en ambas cuentas se hacen visibles para el resto de la base de datos en ese mismo instante. Aquí está la clave. Si ocurre cualquier problema dentro de ese bloque, la base de datos permanece segura. El problema podría ser una violación de una constraint de la base de datos, un timeout de red, o incluso un error puro de Python como una variable que falta o una división por cero. Si salta una exception, el context manager la intercepta. Automáticamente envía un comando rollback a PostgreSQL, borrando la deducción de la cuenta A, y luego deja que la exception de Python siga subiendo por tu call stack. También puedes anidar estos bloques de forma segura. Si abres un nuevo context manager de transaction mientras ya estás dentro de un bloque de transaction activo, asyncpg no se confunde. En su lugar, crea automáticamente un savepoint en la base de datos. Un savepoint actúa como un marcador dentro de una transaction en curso. Si el bloque interno da un error, hace un rollback del estado de la base de datos solo hasta ese marcador. El bloque externo permanece completamente intacto y todavía puede hacer commit de su propio trabajo, o elegir fallar basándose en tu lógica. No tienes que escribir comandos savepoint manualmente, simplemente anidas tus bloques async with. En definitiva, el context manager de transaction vincula permanentemente el estado de tu base de datos con tu estado de ejecución de Python, asegurando que una exception de Python no controlada sea una garantía absoluta contra updates parciales en la base de datos. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue programando!
9

Pool de conexiones

3m 55s

Escala tu aplicación con el pool de conexiones integrado de asyncpg. Aprende a gestionar eficientemente las conexiones a la base de datos en servicios web de alto tráfico.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python Async de alto rendimiento, episodio 9 de 17. Abrir una nueva conexión a la base de datos para cada request web entrante es una forma muy efectiva de colapsar tu servidor. El overhead de red por sí solo bloqueará tu aplicación, y agotarás rápidamente el límite de conexiones de tu base de datos. La solución a esto es el Connection Pooling. Cuando usas una librería como asyncpg para hablar con PostgreSQL, establecer una conexión básica es una operación costosa. Requiere un handshake TCP, negociación segura y autenticación en la base de datos. Si corres un servicio web con mucho tráfico, simplemente no te puedes permitir pagar este coste de latencia en cada request HTTP. En su lugar, necesitas mantener una colección constante de conexiones listas para usar. En asyncpg, consigues esto usando la función create pool. Normalmente, llamas a esta función una vez durante la fase de startup de tu aplicación. Proporcionas las credenciales de tu base de datos, el host y el puerto, y asyncpg levanta un conjunto de conexiones idle en background. A partir de ese momento, tus route handlers y background tasks nunca crean una nueva conexión desde cero. Solo toman prestadas las que ya existen. Aquí hay una trampa común en la que caen muchos developers. No confundas tomar prestada una conexión con abrir una transacción en la base de datos. Son operaciones completamente distintas. Cuando tomas prestada una conexión del pool, solo estás reservando el socket de red para tu uso exclusivo y temporal. Si tu operación requiere atomicidad en múltiples queries, todavía tienes que iniciar explícitamente una transacción en esa conexión prestada específica. Piensa en un servicio web FastAPI o aiohttp con mucho tráfico. Supongamos que tienes un endpoint que acepta un número entero, hace una query a la base de datos para calcular la potencia de dos de ese número, y devuelve el resultado. Cuando una request llega a tu endpoint, llamas al método acquire en tu objeto pool. Haces esto usando un context manager asíncrono. Esto toma prestada temporalmente una conexión del pool. Luego, usas esa instancia de conexión específica para ejecutar tu query en la base de datos y calcular la potencia de dos. Una vez que el bloque de código termina, el context manager libera automáticamente la conexión. Limpia su estado y la devuelve al pool, dejándola disponible de inmediato para la siguiente request HTTP entrante. Si tu servidor web recibe un pico repentino de tráfico y todas las conexiones del pool están ocupadas, la siguiente request no crashea. Espera. La llamada a acquire simplemente pausará la ejecución hasta que otra request termine y devuelva su conexión. Aquí está la clave. El connection pool no solo ahorra tiempo en los handshakes de red. Actúa como un limitador de concurrencia estricto y fiable. Protege tu base de datos PostgreSQL de verse abrumada por una avalancha inesperada de tráfico. Si configuras tu pool para mantener exactamente veinte conexiones, la base de datos nunca verá más de veinte queries activas concurrentes de esa instancia de la aplicación, sin importar cuántos miles de requests simultáneas lleguen a tu servidor web. Gracias por escuchar. ¡Hasta la próxima!
10

Caché de prepared statements

4m 48s

Comprende cómo asyncpg optimiza el análisis de consultas con prepared statements automáticos, y por qué los poolers externos como PgBouncer pueden causar conflictos.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. High-Performance Python Async, episodio 10 de 17. Tu aplicación funciona perfectamente en tu máquina local. Pero en el momento en que haces el deploy a producción detrás de un router de conexiones a la base de datos, empiezas a ver crashes aleatorios. Los logs se quejan de statements que ya existen, o statements que no se pueden encontrar. No ha cambiado nada en tu código. Esto pasa por una optimización nativa llamada Prepared Statement Caching. Para entender el error, primero tenemos que ver qué hace asyncpg por debajo. Cada vez que envías una query a través de asyncpg, la librería la traduce automáticamente a un prepared statement en el servidor PostgreSQL. Normalmente, cuando una base de datos recibe una query, tiene que parsear el texto, analizar la sintaxis y montar un plan de ejecución. Esto lleva tiempo. Al preparar el statement, PostgreSQL hace este trabajo pesado una sola vez y le asigna un nombre interno. Para todas las ejecuciones futuras de esa misma query, asyncpg solo envía los nuevos parámetros y el nombre del statement. Esto se salta por completo la fase de parseo y te da un boost de rendimiento brutal. asyncpg guarda una caché de estos prepared statements en memoria, fuertemente vinculada a la conexión activa de la base de datos. Aquí está la clave. El problema viene de un conflicto entre cómo asyncpg gestiona sus conexiones internas y cómo funcionan los poolers externos como PgBouncer. asyncpg asume que tiene una conexión física dedicada y persistente con el servidor de Postgres. Cuando crea un prepared statement, confía en que el statement seguirá disponible en esa misma conexión hasta que la conexión se cierre. Ahora metemos a PgBouncer en la arquitectura, concretamente corriendo en transaction mode. PgBouncer se pone entre tu aplicación y la base de datos. Mantiene un pequeño pool de conexiones reales a Postgres y las comparte entre miles de requests entrantes de clientes. En transaction mode, PgBouncer le da a tu aplicación una conexión física a la base de datos solo durante lo que dura una única transacción. En el momento en que esa transacción hace commit, PgBouncer recupera la conexión física y se la pasa a un cliente totalmente distinto. Esto rompe la caché de prepared statements. Tu aplicación envía una query. asyncpg la prepara y la guarda en caché en la conexión física A. La transacción termina. Unos segundos después, tu aplicación envía exactamente la misma query. asyncpg recuerda que ya preparó esta query, así que le dice a Postgres que ejecute el statement guardado. Pero esta vez, PgBouncer ha enrutado tu aplicación a la conexión física B. La conexión B no tiene ningún registro de ese prepared statement. La base de datos lanza un error diciendo que el statement no existe. Y al revés pasa lo mismo. Otro cliente distinto podría ser enrutado a la conexión A, intentar preparar un statement con el mismo nombre interno, y lanzar un error diciendo que el statement ya existe. El fix es sencillo, pero requiere un trade-off. Tienes que decirle a asyncpg que desactive esta optimización. Cuando inicializas tu conexión o tu pool de conexiones de asyncpg, le pasas un argumento específico poniendo el tamaño de la caché de statements a cero. Esto desactiva por completo el caching automático de prepared statements. Ahora tus queries serán parseadas por PostgreSQL cada vez que se ejecuten. Sacrificas un poco de rendimiento de parseo, pero tu aplicación se volverá estable al instante en conexiones distribuidas. Si tus conexiones a la base de datos se están enrutando dinámicamente por transacción, tu aplicación ya no puede asumir que el servidor de base de datos recuerde absolutamente nada entre queries. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue programando!
11

Arrays de Postgres y cláusulas IN

4m 32s

Resuelve el error de sintaxis más común al migrar a asyncpg. Aprende a filtrar correctamente las consultas frente a una lista de valores utilizando ANY().

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python Async de alto rendimiento, episodio 11 de 17. Escribes una query SQL estándar para filtrar registros. Pasas una lista de valores a los parámetros de tu query, igual que has hecho en docenas de otras librerías de bases de datos. Postgres lanza inmediatamente un syntax error. El problema no son tus datos. El problema es cómo manejas los arrays de Postgres y las IN clauses. Este es el syntax error número uno para los desarrolladores que migran a asyncpg desde otras interfaces de bases de datos. En muchas librerías más antiguas, el driver intercepta tu query string. Si pasas una lista de Python a una IN clause, la librería reescribe el string SQL sobre la marcha. Expande tu lista en un string de parámetros individuales separados por comas antes de enviarlo a la base de datos. Asyncpg no hace esto. Se basa completamente en prepared statements nativos del lado del servidor de Postgres. Aquí está la clave. En el SQL estándar de Postgres, el operador IN requiere una lista de valores escalares separados por comas y entre paréntesis. No acepta un único objeto array. Cuando pasas una lista de Python a asyncpg como parámetro, asyncpg mapea esa lista directamente a un array nativo de Postgres. Tu query termina evaluándose como una expresión IN un objeto array, lo cual es una sintaxis inválida. Postgres espera una expresión IN valor uno, valor dos. Para solucionar esto, debes dejar de usar el operador IN para listas parametrizadas. En su lugar, usa la función any de Postgres. La lógica cambia de preguntar si un valor está IN una lista, a preguntar si un valor es igual a cualquier elemento dentro de un array. El operador any está diseñado específicamente para funcionar con tipos array de Postgres. Evalúa el valor de la izquierda, comprueba el array de la derecha y devuelve true si encuentra una coincidencia. También necesitas decirle a Postgres qué tipo de array está recibiendo haciendo un cast del parámetro. Si esperas un array de strings de texto, haces un cast explícito de tu parámetro a un array de texto. Este type casting explícito garantiza que Postgres sepa exactamente cómo planificar y ejecutar la query sin tener que adivinar el tipo de datos subyacente del binary stream entrante. Imagina un escenario donde estás filtrando una lista de productos. Quieres emparejar la categoría del producto con una lista dinámica de categorías seleccionadas por el usuario. Escribes una query para seleccionar productos donde la categoría sea igual a any parámetro uno, y haces un cast del parámetro uno a un array de texto. En tu código Python, llamas a tu método fetch de la base de datos. Pasas el query string como primer argumento, y tu lista de strings de Python, como electrónica y libros, como segundo argumento. Asyncpg empaqueta tu lista de Python en un array de texto binario de Postgres y lo envía por la red como un único parámetro. Postgres recibe la query, ve el array de texto y empareja eficientemente las categorías usando la función any. Este enfoque es estrictamente mejor que la manipulación de strings. Como la estructura de la query nunca cambia independientemente de cuántas categorías haya en la lista, Postgres parsea y planifica el statement exactamente una vez. La base de datos guarda en caché el query plan, ahorrando tiempo de ejecución en llamadas posteriores. También eliminas el riesgo de SQL injection, ya que los datos se transmiten en formato binario, completamente separados del texto de la query. Si pasas una lista de Python a un parámetro de la base de datos, trátala como un array nativo, hazle un cast al tipo correcto y evalúala con la función any. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue programando!
12

Objetos Record vs Named Tuples

4m 02s

Explora el diseño único de los objetos Record de asyncpg. Comprende por qué se omite por defecto la notación de puntos y cómo habilitarla con clases personalizadas.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Async en Python de alto rendimiento, episodio 12 de 17. Consultas tu base de datos, obtienes una fila y, por instinto, escribes el nombre de la variable punto id. Inmediatamente, Python lanza un AttributeError. Tus datos están ahí, pero no puedes acceder a ellos como esperas. Esto ocurre por el diseño deliberado que hay detrás de los objetos Record frente a las named tuples. Cuando obtienes datos usando asyncpg, no devuelve diccionarios estándar de Python, y tampoco devuelve named tuples. En su lugar, devuelve un objeto custom altamente optimizado llamado Record. Si estás acostumbrado a otros drivers de bases de datos u ORMs, puede que esperes que la dot-notation funcione out of the box. Con un Record de asyncpg, esto falla a propósito. Quizás te preguntes por qué el driver no usa simplemente una named tuple estándar de Python, ya que las named tuples soportan dot-notation de forma nativa. La razón es puro rendimiento. Una named tuple requiere que Python genere una estructura de clase completamente nueva para cada combinación única de columnas que devuelve una query. Si tu aplicación ejecuta cientos de queries con estructuras diferentes, generar esas clases dinámicas crea un overhead de ejecución masivo. La librería está construida para una velocidad absoluta, así que se salta ese cuello de botella por completo devolviendo en su lugar su propio tipo Record compilado. Este objeto Record custom actúa como un híbrido rápido. Soporta indexación por enteros exactamente igual que una tuple estándar, lo que significa que puedes acceder a la primera columna usando el índice cero. Pero también proporciona un mapping similar al de un diccionario. Puedes acceder a tus columnas usando notación de corchetes, pasando el nombre de la columna como un string. Aquí está la clave. Los creadores deshabilitaron activamente la dot-notation en estos objetos para proteger tu aplicación de colisiones de namespace. Piensa en los métodos de mapping estándar en Python, como keys, items, values o get. Si tu tabla de base de datos da la casualidad de que tiene una columna llamada keys, y el driver soportara dot-notation, escribir record punto keys crearía un conflicto estructural. Python no sabría si quieres el valor de la base de datos o el método built-in. Al forzar la notación de corchetes basada en strings, el driver garantiza que los nombres de tus columnas nunca colisionarán con los atributos estándar de Python. Sin embargo, si controlas por completo el esquema de tu base de datos, sabes a ciencia cierta que no usas palabras reservadas de Python como nombres de columna, y necesitas dot-notation para tu codebase, tienes una salida. Puedes hacer un override del comportamiento por defecto. Cuando estableces tu conexión a la base de datos, puedes pasar un parámetro específico llamado record class. Para implementar esto, escribes una clase custom que hereda directamente del Record base de asyncpg. Dentro de esta nueva clase, implementas el método built-in de Python llamado doble guion bajo getattr. Le indicas a este método que tome el nombre del atributo solicitado y simplemente lo busque usando el fallback seguro de la notación de corchetes. Una vez que pasas esta clase custom al setup de tu conexión, cada una de las filas devueltas por tus queries será una instancia de tu objeto custom. Python te permitirá entonces usar dot-notation, enrutando de forma transparente la petición del atributo a través de tu método custom para obtener los datos de la columna subyacente. En definitiva, la estricta notación de corchetes en un Record por defecto no es un descuido, sino una barrera de seguridad estructural que asegura que tu acceso a los datos siga siendo predecible sin importar cómo cambie el esquema de tu base de datos. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue programando!
13

Streaming de resultados con cursores

4m 47s

Evita el agotamiento de memoria al consultar conjuntos de datos masivos. Aprende a utilizar los cursores de asyncpg para hacer streaming de resultados bloque por bloque.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python Async de alto rendimiento, episodio 13 de 17. Necesitas exportar una tabla de usuarios de un millón de filas a un archivo, así que ejecutas un fetch estándar a la base de datos. De repente, tu aplicación Python consume toda la RAM disponible y el servidor crashea. No puedes traerte datasets masivos a la memoria de una sola vez, que es exactamente por lo que usamos Streaming Results con Cursors. Normalmente, cuando ejecutas una query en asyncpg, todo el result set se trae por la red desde PostgreSQL y se mantiene en la memoria de Python. La base de datos construye la respuesta completa, la envía, y asyncpg crea objetos de Python para cada fila antes de que tu código pueda procesar la primera. Esto está perfectamente bien para cincuenta filas. Pero es un problema inmediato para cinco millones de filas. Para evitar que el servidor crashee, necesitas consumir los datos poco a poco en lugar de tragártelos de golpe. Un cursor de la base de datos te da un puntero a los resultados de la query en el servidor PostgreSQL. En lugar de traerte todo, el cursor permite que tu aplicación Python haga fetch de los datos de forma incremental. En asyncpg, haces esto llamando al método cursor en tu conexión. Proporcionas tu query SQL y cualquier argumento necesario. Este método devuelve un iterador asíncrono. Escribes un bucle async-for para iterar sobre este cursor. Por debajo, asyncpg hace fetch automático de filas en lotes pequeños y manejables desde PostgreSQL. Tu código procesa unas cuantas filas, las escribe en tu archivo de exportación y continúa. Python limpia las filas antiguas de la memoria, lo que mantiene el memory footprint total de tu aplicación completamente plano, sin importar lo grande que sea la tabla. Hay una regla estricta aquí que confunde a muchos desarrolladores. Si intentas iterar un cursor directamente en una conexión estándar, asyncpg lanza inmediatamente un InterfaceError. El mensaje de error indicará que los cursors no se pueden usar fuera de una transacción. Aquí está la clave. Los cursors de PostgreSQL están estructuralmente vinculados a las transacciones de la base de datos. Cuando una transacción hace commit o rollback, PostgreSQL destruye cualquier cursor activo asociado a ella. Por defecto, asyncpg opera en modo auto-commit. Esto significa que cada query individual que ejecutas se envuelve en su propia transacción pequeña e invisible que se cierra en el momento en que la query termina. Si asyncpg te permitiera abrir un cursor en modo auto-commit, esa transacción implícita terminaría al instante, y PostgreSQL mataría tu cursor antes de que pudieras hacer fetch de una sola fila. Para que los cursors funcionen, tienes que gestionar explícitamente el límite de la transacción. Haces esto abriendo un context manager asíncrono usando el método transaction en tu conexión. Una vez que estás a salvo dentro de ese bloque de transacción, llamas al método cursor y empiezas tu bucle async-for. Como la transacción permanece abierta durante toda la duración del bloque del context manager, tu cursor se mantiene vivo en el servidor PostgreSQL, permitiéndote hacer streaming del millón de filas de forma segura. Hay una rara excepción a esta regla. PostgreSQL soporta una feature donde puedes declarar un cursor de raw SQL con la frase WITH HOLD. Esto le dice al motor de la base de datos que materialice el resultado y mantenga el cursor vivo incluso después de que la transacción termine. Hacer esto consume recursos de la base de datos, y se salta la eficiencia del streaming estándar. Para casi todas las tareas de streaming en asyncpg, el bloque de transacción explícito es el enfoque necesario. Si estos episodios te resultan útiles y quieres apoyar el programa, busca DevStoriesEU en Patreon. Recuerda que un cursor transforma tu interacción con la base de datos de un memory allocation masivo y arriesgado a un pipeline controlado y persistente que puede procesar de forma segura cualquier volumen de datos. Eso es todo por hoy. ¡Gracias por escuchar, y sigue programando!
14

Ingesta ultrarrápida con COPY

3m 58s

Potencia tus pipelines de ingesta de datos. Exploramos el protocolo COPY de PostgreSQL para realizar cargas masivas de datos de forma exponencialmente más rápida que con sentencias INSERT.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python Async de alto rendimiento, episodio 14 de 17. Necesitas meter o sacar un millón de filas de tu base de datos. Si tu primer instinto es agrupar en un batch una lista enorme de sentencias insert, o traerte todas las filas a una lista gigante de Python, estás eligiendo el camino más lento posible. Existe un mecanismo diseñado específicamente para saltarse este overhead. Hoy hablamos de la ingesta ultrarrápida con COPY. COPY es un protocolo específico de Postgres para la transferencia masiva de datos. Cuando ejecutas sentencias insert o select estándar, Postgres tiene que parsear la query, planificarla y ejecutarla. Hacer esto repetidamente añade un overhead enorme. El protocolo COPY se salta por completo el pipeline de queries estándar. Abre un stream directo a la capa de almacenamiento, moviendo los datos en un formato altamente optimizado. Por esto, es órdenes de magnitud más rápido que los bulk inserts. En asyncpg, metes datos en la base de datos usando un método llamado copy to table. Le pasas el nombre de la tabla de destino y un origen de datos. Ese origen puede ser un file path local, un file-like object, o un iterador asíncrono que hace yield de registros. Si lo apuntas a un archivo CSV local, Postgres se encarga del parseo de forma nativa. No necesitas abrir el archivo en Python, parsear las filas y mapearlas a variables. El driver de la base de datos hace un stream de los bytes raw del archivo directamente al servidor. También puedes pasar una simple lista de tuplas de Python si tus datos ya están en memoria, y asyncpg hará un stream usando el protocolo COPY por debajo. Sacar datos es igual de rápido. Si necesitas un export completo, usas copy from table. Esto coge todo el contenido de una tabla y lo escupe a un archivo o stream. Sin embargo, hacer un dump de una tabla entera rara vez es lo que realmente necesitas. Normalmente, quieres datos filtrados o con joins. Aquí es donde copy from query entra en juego. Un error común es pensar que este método solo hace un dump de los resultados de la query a un archivo estático. Eso simplemente no es verdad. Aunque puede escribir directamente a un file path, también puedes pasarle una función callback. Asyncpg ejecutará la query y hará un stream de los resultados en chunks a tu callback, permitiéndote procesar un dataset masivo al vuelo sin tener que guardar todo el result set en la memoria del sistema. Imagina un escenario donde necesitas generar un reporte CSV de todos los usuarios activos. Un enfoque estándar es ejecutar una query select, hacer un fetch de cien mil filas en Python, formatearlas usando el módulo CSV y escribirlas a disco. Eso consume muchísima memoria y CPU. Aquí está la clave. Puedes saltarte el procesamiento en Python por completo. Llamas a copy from query, le pasas tu sentencia select específica, pones el parámetro format en CSV y le das un file path de salida. Postgres ejecuta la query, formatea los resultados en CSV de forma nativa en el servidor de la base de datos, y asyncpg hace un stream del texto terminado directo a tu disco duro. Tu aplicación Python actúa como un simple pipe, haciendo casi cero manipulación de datos. Deberías seguir usando sentencias insert y select estándar para la lógica del día a día de la aplicación, pero en el momento en que el volumen de datos raw se convierta en tu cuello de botella, pásate al protocolo COPY para saltarte el parser de SQL por completo. Eso es todo por este episodio. ¡Gracias por escuchar, y sigue programando!
15

Listen y Notify asíncronos

3m 33s

Desbloquea arquitecturas orientadas a eventos en tiempo real directamente dentro de PostgreSQL. Aprende a usar add_listener de asyncpg para mensajería pub/sub instantánea.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. High-Performance Python Async, episodio 15 de 17. Normalmente, recurres a un message broker independiente como Redis en el momento en que necesitas eventos en tiempo real. Pero si tu aplicación ya utiliza una base de datos, podrías estar añadiendo complejidad a la infraestructura sin ningún motivo. PostgreSQL tiene un message bus en tiempo real integrado. Hoy hablamos de Listen y Notify asíncronos. Postgres soporta de forma nativa un patrón publish-subscribe usando dos comandos, listen y notify. La librería asyncpg expone esta capacidad en Python a través de un método llamado add listener. En lugar de escribir un loop que haga polling a una tabla de la base de datos cada pocos segundos para comprobar si hay datos nuevos, registras una función callback asíncrona de Python en un channel específico. Cuando ocurre un evento dentro de Postgres, este emite un mensaje a ese channel, y tu callback de Python se ejecuta inmediatamente. Aquí está la clave. Un listener no está asociado a tu aplicación a nivel global, y tampoco está asociado a un connection pool. Está vinculado a una conexión de base de datos específica e individual. Este es un punto de fallo común. Si sacas una conexión de un pool, registras tu listener, y luego devuelves la conexión al pool, el listener se pierde. Para usar esta funcionalidad de forma fiable, tienes que adquirir una conexión de asyncpg, llamar al método add listener sobre ella, y mantener esa conexión abierta indefinidamente. Se convierte en un pipeline de escucha dedicado. Veamos un escenario práctico. Tienes un background worker que necesita despertarse y procesar registros cada vez que se inserta una nueva fila en una tabla de jobs. En lugar de hacer polling, configuras un trigger en la base de datos. Cada vez que ocurre un insert, el trigger ejecuta un comando notify en un channel llamado new jobs. También envía un pequeño payload de texto, como el identificador único de la nueva fila. En tu código Python, escribes una función callback asíncrona. Esta función espera recibir cuatro argumentos de asyncpg: el objeto de conexión, el identificador del proceso de Postgres, el nombre del channel, y el propio payload de texto. A continuación, adquieres tu conexión dedicada. Llamas a add listener en esa conexión, pasándole el string del channel new jobs junto con tu función callback. Finalmente, mantienes el script en ejecución, normalmente haciendo await de un evento asíncrono que nunca se activa. Tu proceso de Python ahora se queda completamente inactivo. Consume casi cero recursos de CPU y deja de machacar la base de datos con queries vacías. En el momento en que una transacción hace commit de una nueva fila de job, Postgres envía la notificación a través del socket de red abierto. Asyncpg lee ese socket e inmediatamente programa tu callback en el event loop de Python, pasándole el identificador del nuevo job. El verdadero poder de este patrón es la consistencia transaccional. Si una transacción de base de datos hace rollback, Postgres descarta automáticamente cualquier comando notify ejecutado durante esa transacción. Esto garantiza que tus workers de Python solo se despertarán y reaccionarán a los datos que se hayan guardado correctamente en el disco. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue programando!
16

Telemetría y logging de consultas

3m 45s

Obtén una observabilidad profunda del rendimiento de tu base de datos. Descubre cómo usar los log listeners de asyncpg para rastrear consultas lentas y monitorizar la telemetría de ejecución.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python Async de alto rendimiento, episodio 16 de 17. Para descubrir por qué tu base de datos va lenta, no tienes que adivinar. Puedes basarte en los logs del servidor, pero estos no tienen en cuenta la latencia de red ni el overhead del lado de Python. Medir el rendimiento manualmente en cada llamada a la base de datos ensucia rápidamente tu lógica de negocio. La solución es la telemetría y el Query Logging. A menudo la gente confunde los log listeners con los query loggers. Los log listeners capturan los notices del servidor Postgres. Los query loggers capturan la telemetría de ejecución del lado del cliente. Gestionan dos flujos de información completamente distintos. Los query loggers gestionan la telemetría. Puedes adjuntar un callback a tu conexión usando el método add query logger. Una vez adjunto, cada vez que una consulta termina de ejecutarse, asyncpg pasa automáticamente un objeto LoggedQuery a este callback. Esto ocurre a nivel global para esa conexión, completamente desacoplado de la función específica que hace la petición a la base de datos. El objeto LoggedQuery contiene tres datos fundamentales. Almacena el texto exacto de la consulta SQL, los argumentos pasados a esa consulta y el tiempo de ejecución. Los argumentos se capturan exactamente tal y como los proporcionó tu aplicación. Esto te ahorra tener que escribir lógica de string formatting manual solo para averiguar qué parámetros causaron una respuesta lenta. Considera un entorno de producción donde necesitas capturar cualquier consulta que tarde más de 500 milisegundos. Defines una función estándar de Python que acepta dos parámetros: la conexión a la base de datos y el objeto LoggedQuery. Dentro de esta función, compruebas el atributo de tiempo de ejecución. Si el tiempo supera los cero coma cinco segundos, escribes el texto de la consulta, los argumentos y la duración exacta en tu sistema de monitorización de aplicaciones. Luego, pasas este callback al método add query logger. Ahora, tu aplicación rastrea automáticamente las consultas lentas de forma silenciosa en segundo plano. Si alguna vez necesitas detener este rastreo, simplemente pasas el mismo callback al método remove query logger. Ahora bien, la segunda parte de esto es el log listener. Mientras que el query logger gestiona los tiempos del lado del cliente, el log listener gestiona la mensajería del lado del servidor. A veces, Postgres envía mensajes que no son errores y no devuelven filas de datos. Se trata de notices asíncronos, warnings o mensajes de log personalizados generados directamente por el motor de la base de datos. Para capturar estos mensajes, adjuntas un callback usando el método add log listener. Cuando Postgres emite un notice o un warning, asyncpg activa este callback. Le pasa la conexión y un objeto de mensaje específico a tu función. Esto le da a tu aplicación visibilidad inmediata de los warnings a nivel de base de datos, de forma completamente independiente a los resultados de tus consultas estándar. Al igual que con el query logger, puedes desvincular este callback más adelante usando remove log listener. Esta es la clave. El query logging del lado del cliente te da la duración real de ejecución que experimenta tu aplicación Python, evitando por completo las conjeturas entre el retardo de red y el procesamiento de la base de datos. Gracias por escuchar. Cuidaos todos.
17

Asegurando conexiones con SSL

3m 49s

Asegúrate de que las conexiones a tu base de datos sean seguras. Cubrimos la configuración del contexto SSL y cómo forzar TLS directo al conectarse a bases de datos en la nube.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Python Async de alto rendimiento, episodio 17 de 17. Conectarse a una base de datos gestionada en la nube sin SSL es como gritar las credenciales de tu base de datos en una sala llena de gente. Necesitas cifrado, pero configurarlo correctamente suele generar errores de conexión confusos o defaults inseguros. Hoy, vamos a proteger las conexiones con SSL en asyncpg. A menudo, la gente se lía al configurar SSL en su connection logic. Si usas connection URIs, asyncpg parsea de forma nativa los query parameters estándar de sslmode de PostgreSQL, como poner sslmode a require. Eso funciona para configuraciones básicas. Pero cuando necesitas un control preciso, como conectarte de forma segura a una base de datos gestionada en la nube usando un bundle de Certificate Authority personalizado, los strings de URI estándar se quedan cortos. Para eso, usas el parámetro ssl programático. El parámetro ssl en las funciones de conexión de asyncpg determina cómo se negocia TLS. Acepta dos tipos de valores. El primer tipo es un string preset. Puedes pasar el string prefer, que intenta una conexión SSL pero hace fallback a una conexión sin cifrar si el servidor no lo soporta. Puedes pasar require, que fuerza el cifrado pero se salta la verificación de la identidad del servidor. O puedes pasar verify-full, que fuerza el cifrado y valida estrictamente el certificado del servidor contra trusted roots. Aquí está la clave. Cuando tu escenario requiera una Certificate Authority personalizada, no confíes en los string presets. En su lugar, creas un objeto SSLContext estándar de Python. Configuras este objeto con tus archivos de certificado personalizados, aplicas una verificación estricta y luego pasas ese objeto de Python directamente al parámetro ssl de asyncpg. Esto te da un control exacto sobre el handshake criptográfico, haciendo bypass de cualquier certificado del sistema por defecto. Esto cubre las reglas de cifrado, pero ¿cómo se inicia realmente la conexión? Esto nos lleva al parámetro direct TLS. Por defecto, PostgreSQL utiliza un protocolo llamado STARTTLS. El cliente hace una conexión en texto plano, le pregunta al servidor si soporta cifrado y, si el servidor dice que sí, hacen un upgrade de la conexión a TLS. Sin embargo, los setups de proxy modernos, como ciertos connection poolers o cloud load balancers, suelen esperar una conexión direct TLS desde el primer byte. No quieren la negociación en texto plano. Si tu infraestructura está montada de esta manera, pasas true al parámetro de conexión direct TLS. Cuando haces esto, asyncpg se salta la negociación STARTTLS e inicia inmediatamente un raw TLS handshake. Naturalmente, esto solo funciona si también proporcionas una configuración ssl válida. Si habilitas direct TLS pero dejas el parámetro ssl vacío, la conexión fallará. Al proteger tus conexiones a la base de datos, recuerda que, aunque los string presets son convenientes, pasar un objeto SSLContext explícito es la única forma de garantizar absolutamente que tu aplicación confía en la identidad correcta del servidor. Como este es el último episodio de la serie, te animo a explorar la documentación oficial de asyncpg y a probar estos parámetros de conexión de forma práctica. Puedes visitar devstories dot eu para sugerir temas para nuestras futuras series. Eso es todo por este episodio. Gracias por escuchar, y ¡sigue programando!