Volver al catálogo
Season 17 12 Episodios 47 min 2026

Apache Cassandra with Python

Edición 2026. Una serie de podcasts técnicos que explora la arquitectura distribuida de Apache Cassandra y cómo interactuar con ella utilizando el DataStax Python Driver. Cubre el modelado de datos, Execution Profiles, LWTs, consultas asíncronas y el Object Mapper cqlengine.

Bases de datos Computación distribuida
Apache Cassandra with Python
Reproduciendo ahora
Click play to start
0:00
0:00
1
La visión general
Una introducción a Apache Cassandra. Aprende por qué las aplicaciones a escala global eligen esta base de datos NoSQL distribuida y en qué se diferencia de los sistemas relacionales tradicionales.
3m 48s
2
Consistent Hashing y el anillo
Sumérgete en la arquitectura de Cassandra. Exploramos el Consistent Hashing, el anillo de tokens y cómo se particionan los datos en múltiples nodos sin un servidor maestro.
3m 59s
3
Modelado de datos orientado a consultas
Desaprende todo lo que sabes sobre bases de datos relacionales. Aprende cómo el modelado orientado a consultas de Cassandra requiere desnormalización, y la diferencia crucial entre partition keys y clustering keys.
3m 25s
4
Conectando con Python
Empieza a usar el DataStax Python Driver. Aprende a instanciar un Cluster, conectarte a una Session y establecer comunicación con tus nodos de Cassandra.
4m 05s
5
Execution Profiles
Gestiona cargas de trabajo complejas sin problemas utilizando Execution Profiles. Aprende a configurar el balanceo de carga, los tiempos de espera y los niveles de consistencia por consulta sin ensuciar la configuración de tu clúster.
4m 22s
6
Prepared Statements
Aprende a ejecutar comandos CQL desde Python. Cubrimos las sentencias simples y los beneficios críticos de rendimiento al usar Prepared Statements para consultas frecuentes.
3m 36s
7
Paginación de consultas grandes
Nunca bloquees tu aplicación cargando un conjunto de datos masivo en la memoria. Descubre cómo el driver de Python pagina automáticamente los resultados de consultas grandes y cómo gestionar los fetch sizes.
3m 44s
8
Consultas asíncronas de alto rendimiento
Maximiza el rendimiento de tu aplicación. Aprende a usar execute_async, ResponseFutures y callbacks para ejecutar peticiones concurrentes contra Cassandra.
3m 55s
9
Lightweight Transactions
Implementa operaciones compare-and-set de forma segura. Aprende cómo funcionan las Lightweight Transactions (LWTs) en Cassandra y cómo inspeccionar la columna especializada applied en tus resultados de Python.
4m 01s
10
Los modelos del Object Mapper
Evita las cadenas CQL en crudo y modela tus datos usando clases de Python. Aprende a usar cqlengine para definir tablas, especificar primary keys y sincronizar tu esquema.
4m 13s
11
Haciendo consultas con cqlengine
Recupera y filtra datos de forma fluida usando objetos QuerySet en el Object Mapper cqlengine. Cubrimos los operadores de filtrado, la inmutabilidad y las limitaciones en la ordenación.
3m 58s
12
Vector Search para IA
Prepara tus habilidades para el futuro con el Vector Search de Cassandra 5.0. Descubre cómo almacenar y consultar vectores de alta dimensionalidad para impulsar aplicaciones modernas de IA y machine learning.
4m 15s

Episodios

1

La visión general

3m 48s

Una introducción a Apache Cassandra. Aprende por qué las aplicaciones a escala global eligen esta base de datos NoSQL distribuida y en qué se diferencia de los sistemas relacionales tradicionales.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 1 de 12. Las bases de datos relacionales se chocan contra un muro cuando intentas hacer deploy en varios continentes. Terminas peleando contra la latencia, el downtime, o una arquitectura frágil donde un solo servidor que falla tumba tus write operations. La escala global masiva requiere un paradigma de base de datos completamente diferente. Ese paradigma es Apache Cassandra. Es una base de datos NoSQL distribuida y open-source, diseñada para una escala inmensa. Cuando los ingenieros ven Cassandra por primera vez, a menudo traen consigo ideas preconcebidas de los sistemas relacionales. Buscan el nodo primary que gestiona las writes y las read-only replicas que lo siguen. Tienes que abandonar ese modelo mental de inmediato. Cassandra no utiliza un diseño primary-replica. Opera completamente con una arquitectura masterless y multi-primary. Cada nodo del cluster es idéntico. Cualquier nodo puede aceptar una read request, y cualquier nodo puede aceptar una write request. Para entender por qué funciona así, mira su historia. Cassandra se desarrolló originalmente combinando dos grandes avances de investigación. Primero, adoptó el diseño de red totalmente distribuido y masterless de Amazon Dynamo. Esto dicta cómo los nodos se comunican, se descubren entre sí y replican los datos en la red. Segundo, adoptó el log-structured storage engine de Google Bigtable, que gestiona cómo se escriben físicamente los datos en el disco. El resultado es un sistema diseñado específicamente para una disponibilidad continua en múltiples datacenters. Imagina una red social global. Tienes usuarios activos en Tokio, Londres y Nueva York simultáneamente. Si un usuario en Londres actualiza su perfil, esa write operation tiene que ocurrir al instante. Enrutar esa request a través del Atlántico hasta una única base de datos central es demasiado lento. Con Cassandra, el usuario escribe en un nodo local del datacenter de Londres. Ese nodo acepta la write localmente e inmediatamente se encarga de replicarla en background a Tokio y Nueva York. Aquí está la clave. Como cada nodo actúa como primary, no hay un single point of failure. Cassandra organiza sus nodos en un anillo lógico. Cuando los datos entran al sistema, un hash matemático determina exactamente qué nodos del anillo poseen ese dato específico. Si un gran outage deja offline todo el datacenter de Londres, la red social no se cae. Tokio y Nueva York siguen aceptando reads y writes sin interrupción. Cuando Londres vuelve a estar online, los otros datacenters sincronizan automáticamente los datos que faltan con los nodos recuperados. Consigues una verdadera disponibilidad global con cero downtime. Este diseño masterless también significa que el escalado es predecible y lineal. Si necesitas más almacenamiento o más write capacity, simplemente conectas otro nodo al anillo. El cluster detecta automáticamente el nuevo hardware y redistribuye los datos para balancear la carga entre las máquinas activas. Cassandra te obliga a cambiar la comodidad de las queries de bases de datos tradicionales por algo mucho más difícil de construir a gran escala: la garantía absoluta de que, sin importar qué hardware falle, tu base de datos se mantiene online y tus operaciones tienen éxito. Si quieres apoyar el programa, puedes buscar DevStoriesEU en Patreon. Eso es todo por este episodio. ¡Gracias por escuchar, y sigue construyendo!
2

Consistent Hashing y el anillo

3m 59s

Sumérgete en la arquitectura de Cassandra. Exploramos el Consistent Hashing, el anillo de tokens y cómo se particionan los datos en múltiples nodos sin un servidor maestro.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 2 de 12. El hashing de datos simple falla por completo en el momento en que añades un nuevo servidor al cluster de tu base de datos. Cuando cambia el número de servidores, casi todos los datos tienen que moverse a una nueva ubicación. La solución a este caos de escalabilidad es el consistent hashing y el ring. Una forma estándar de distribuir datos entre varios servidores es el hashing por módulo. Si tienes ocho nodos, coges una partition key como un user ID, le aplicas un hash y divides el resultado entre ocho. El resto te dice qué nodo se queda con el perfil de usuario. Esto funciona perfectamente hasta que tu almacenamiento se llena y conectas un noveno nodo. Ahora divides entre nueve. Los restos cambian. De repente, casi todos los perfiles de usuario de tu sistema pertenecen a un servidor diferente, provocando una tormenta masiva de movimiento de datos que colapsa el cluster. Cassandra evita esto por completo. Utiliza consistent hashing para distribuir los datos de forma predecible por todo el cluster sin depender de ningún coordinador central. En lugar de un simple cálculo de módulo, Cassandra mapea tanto los datos como los nodos a un espacio circular fijo y continuo llamado token ring. Por defecto, Cassandra utiliza una función hash que genera un rango enorme de números posibles. El valor hash más bajo se conecta directamente con el más alto, formando un círculo cerrado. A cada nodo físico del cluster se le asigna un número específico, o token, en algún lugar de este ring. Cuando insertas un perfil de usuario, Cassandra le aplica un hash al user ID para generar un token. Para averiguar qué nodo es el dueño de este perfil, el sistema localiza el token de los datos en el ring y se mueve en el sentido de las agujas del reloj. El primer nodo que encuentra es el dueño. Piensa de nuevo en nuestro cluster de ocho nodos. Si añadimos un noveno nodo físico, se le asigna un único token nuevo en el ring, cayendo entre dos nodos existentes. Como la propiedad de los datos se determina avanzando en el sentido de las agujas del reloj, este nuevo noveno nodo solo se hace cargo de una porción específica de datos de su vecino inmediato en el sentido de las agujas del reloj. Los otros siete nodos no hacen nada. Sus datos permanecen completamente intactos. Aquí está la clave. Asignar exactamente un token a un nodo físico genera problemas operativos. Es difícil balancear los datos a la perfección, y cuando añades un nuevo nodo, solo un servidor vecino es responsable de entregar los datos. Ese único vecino se satura bajo una carga pesada. Para solucionar esto, Cassandra utiliza virtual nodes, o vnodes. En lugar de darle a un servidor físico una enorme porción contigua del ring, los vnodes cortan el ring en muchos rangos más pequeños. A un único nodo físico se le asignan cientos de tokens diferentes distribuidos aleatoriamente por el ring. Cuando añades ese noveno nodo físico usando vnodes, este reclama muchas porciones pequeñas del ring de todos los servidores existentes a la vez. Ahora, en lugar de que un solo vecino haga todo el trabajo pesado, todo el cluster comparte equitativamente el trabajo de hacer streaming de los datos a la nueva máquina. El consistent hashing y los virtual nodes desacoplan la ubicación de los datos del número bruto de servidores físicos, permitiendo que un cluster escale sin problemas y funcione de forma predecible sin ningún master central dictando dónde deben ir los datos. Eso es todo por este episodio. ¡Hasta la próxima!
3

Modelado de datos orientado a consultas

3m 25s

Desaprende todo lo que sabes sobre bases de datos relacionales. Aprende cómo el modelado orientado a consultas de Cassandra requiere desnormalización, y la diferencia crucial entre partition keys y clustering keys.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 3 de 12. En una base de datos relacional, primero creas tus tablas, defines tus relaciones y luego escribes tus queries. En Cassandra, si no conoces tus queries exactas antes de empezar, tu base de datos fallará. Este es el principio fundamental del Query Driven Data Modeling. Muchos desarrolladores llegan a Cassandra con fuertes hábitos relacionales. Naturalmente, buscas formas de normalizar tus datos, configurar foreign keys y evitar la duplicación. Tienes que abandonar esa mentalidad por completo. Cassandra no soporta joins. Si intentas normalizar tus datos en varias tablas, acabarás haciendo joins en el código de tu aplicación, lo que destruye los beneficios de rendimiento por los que elegiste Cassandra en primer lugar. Cassandra requiere un modelado de datos basado en queries. Empiezas mapeando las preguntas exactas que tu aplicación necesita hacerle a la base de datos. En este modelo, una query normalmente equivale a una tabla. Si necesitas acceder a los mismos datos de tres formas diferentes, creas tres tablas distintas que contengan los mismos datos. Esto nos lleva a la desnormalización. Duplicar datos no es un error aquí; es la estrategia fundamental. El espacio en disco es barato, pero las lecturas distribuidas a través de una red son caras. Al escribir los datos juntos con la estructura exacta de tu query de lectura, Cassandra puede recuperarlos en una sola operación sin buscar en todo el cluster. Para que esto funcione, tienes que entender la primary key. No es solo un identificador único. Controla exactamente dónde y cómo se almacenan tus datos en el disco. La primary key tiene dos partes distintas: la partition key y la clustering key. La partition key dicta qué nodo físico de tu cluster contiene los datos. Todas las filas que comparten la misma partition key se almacenan juntas en el mismo nodo. La clustering key determina el orden de clasificación de esas filas en el disco dentro de esa partición específica. Tomemos como ejemplo el escenario de publicación de revistas de la documentación. Supongamos que tu aplicación necesita obtener todas las revistas publicadas por una editorial específica. Tu partition key debe ser el nombre de la editorial. Cuando llega la query, Cassandra hace un hash del nombre de la editorial, identifica el nodo exacto que contiene los datos de esa editorial y va directo a él. Para organizar los resultados de forma natural, podrías usar la fecha de publicación como tu clustering key. Ahora, no solo todas las revistas de esa editorial están agrupadas en un nodo, sino que están almacenadas físicamente en orden cronológico. La base de datos simplemente te devuelve los datos preordenados. Aquí está la clave. Estás intercambiando complejidad de escritura por una velocidad de lectura masiva. Escribes los mismos datos en varias tablas para satisfacer diferentes vistas de la aplicación, pero cuando un usuario pide esos datos, te los devuelve en milisegundos porque la base de datos hace cero cálculos para ensamblarlos. Gracias por escuchar. Cuidaos todos.
4

Conectando con Python

4m 05s

Empieza a usar el DataStax Python Driver. Aprende a instanciar un Cluster, conectarte a una Session y establecer comunicación con tus nodos de Cassandra.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 4 de 12. Al conectarte a una base de datos tradicional, apuntas tu aplicación a una única dirección de host. Al conectarte a una base de datos distribuida, podrías pensar que tienes que gestionar manualmente decenas de direcciones de servidor en tus archivos de configuración. No es así, porque el driver de tu base de datos en realidad actúa como un router inteligente. Hoy veremos cómo conectarnos con Python. Imagina un microservicio de Python arrancando. Necesita establecer comunicación con un cluster local de Cassandra de tres nodos. Para empezar, importas la clase Cluster del módulo cassandra punto cluster. Inicializas esta clase pasándole una lista de direcciones IP conocidas como contact points. Si tus nodos usan un puerto no estándar, también puedes especificar un argumento port aquí; de lo contrario, usa el 9042 por defecto. Los desarrolladores suelen confundir este paso con la creación de una connection string estándar de un solo DSN, donde debes listar explícitamente con qué quieres hablar. Con Cassandra, no necesitas listar cada nodo de tu infraestructura. Si tienes un cluster masivo de treinta nodos, pasar solo dos o tres direcciones IP como contact points está perfectamente bien. Aquí está la clave. Cuando el driver de Python arranca, se conecta a uno de esos contact points para hacer el bootstrap. Consulta las tablas del sistema para descargar la topology actual del cluster. Al hacer esto, descubre automáticamente las direcciones IP del resto de los nodos. El driver mantiene dinámicamente este mapa de red en segundo plano. Si haces un scale out y añades nodos más tarde, el driver detecta el cambio y ajusta su routing automáticamente sin requerir un reinicio de la aplicación. Una vez que instancias tu objeto Cluster con esos contact points iniciales, llamas a su método connect. Esta acción devuelve un objeto Session. El Session gestiona el connection pooling real hacia los nodos que acaba de descubrir. Al llamar a connect, puedes pasar opcionalmente un nombre de keyspace. Un keyspace funciona como un namespace para tus datos. Si lo proporcionas, el driver lo establece como predeterminado para todas las operaciones futuras en esa Session. Como el Session gestiona connection pools complejos por debajo, está diseñado para ser de larga duración y thread-safe. Normalmente, creas un Session al arrancar la aplicación y lo reutilizas. Ahora, la segunda parte de esto es conectarse a un servicio cloud gestionado como DataStax Astra. Astra funciona de forma diferente y no expone raw IP addresses para los contact points. En su lugar, descargas un secure connect bundle. Este es un archivo zip que contiene los certificados necesarios y los detalles de conexión mutual TLS. En tu código Python, omites la lista de direcciones IP. En su lugar, le pasas un dictionary de configuración cloud al objeto Cluster. Este dictionary contiene una key llamada secure connect bundle, que apunta a la ruta local de tu archivo zip. Combinas esto con un objeto plaintext authentication provider configurado con tu client ID y secret. Al llamar a connect, obtienes un objeto Session estándar, que funciona exactamente igual que la configuración del cluster local. La conclusión principal es que, tanto si pasas unas pocas direcciones IP locales como un secure connect bundle de cloud, el driver de Python toma tu entry point inicial, mapea la red de la base de datos distribuida y abstrae completamente la lógica de routing del código de tu aplicación. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue construyendo!
5

Execution Profiles

4m 22s

Gestiona cargas de trabajo complejas sin problemas utilizando Execution Profiles. Aprende a configurar el balanceo de carga, los tiempos de espera y los niveles de consistencia por consulta sin ensuciar la configuración de tu clúster.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 5 de 12. A medida que tu aplicación crece, aplicar un timeout o consistency level único a todas las queries de la base de datos se convierte en un enorme cuello de botella operativo. Acabas haciendo que tus background jobs fallen prematuramente o bloqueando tu interfaz de usuario mientras esperas una lectura pesada. Los Execution Profiles son el mecanismo que resuelve esta tensión. Muchos desarrolladores confunden la execution configuration con el setup heredado a nivel de cluster. En paradigmas anteriores, definías parámetros globales directamente en el cluster object, lo que significaba que todas las queries compartían exactamente el mismo timeout. Los Execution Profiles reemplazan ese patrón rígido. Te permiten mantener múltiples configuraciones distintas simultáneamente dentro de una única session activa. Piensa en una aplicación web multi-tenant. Tu interfaz de front-end requiere un timeout estricto de un segundo para asegurar que las páginas web vayan fluidas. Mientras tanto, tus tareas de reporting en background necesitan treinta segundos para agregar particiones grandes de forma segura. Un execution profile es un paquete independiente y con nombre de request settings, adaptado exactamente a estas diferentes workloads. Para montar esto, empiezas creando instancias de un execution profile object. Para la tarea de reporting, instancias un perfil y configuras el request timeout parameter en treinta segundos. Puedes ir más allá y asociar una load balancing policy específica a este object, quizás enrutando esas lecturas analíticas pesadas exclusivamente a un data center dedicado a analítica. Luego, creas un segundo profile object distinto para tu interfaz de usuario, asignándole un timeout de un segundo y una load balancing policy local. También puedes agrupar distintas retry policies o consistency levels en estos perfiles, dependiendo de lo que exija la query. Aquí está la clave. Registras estos perfiles una sola vez, durante tu cluster setup inicial, en lugar de construirlos durante la propia query execution. Al instanciar el cluster, pasas un dictionary al execution profiles argument. Este dictionary mapea string names simples a los profile objects que acabas de crear. El driver gestiona estas configuraciones por debajo. Siempre hay un fallback profile integrado, que puedes sobrescribir mapeando una configuración a una constante específica llamada execution profile default. Si ejecutas una query sin nombrar explícitamente un perfil, el driver aplica automáticamente estos default settings. Cuando realmente necesites ejecutar una query, usas los métodos estándar session execute o execute async. Junto a tu query string o prepared statement, pasas un execution profile argument usando el string name simple que registraste antes. El driver intercepta este nombre, recupera los settings empaquetados asociados a él y los aplica a esa request específica. La query de tu interfaz de usuario tiene un límite estricto de un segundo, y el background job se ejecuta cómodamente durante treinta segundos. Ambas queries se ejecutan de forma concurrente, multiplexadas sobre exactamente la misma session y exactamente el mismo connection pool. Mover tu configuración a execution profiles desacopla el comportamiento de tu query de tu conexión física a la base de datos. Permite que una sola aplicación moldee su tráfico de base de datos de forma dinámica basándose en las necesidades específicas de cada función, sin tener que establecer sessions separadas en ningún momento. Gracias por estar ahí. Espero que hayas aprendido algo nuevo.
6

Prepared Statements

3m 36s

Aprende a ejecutar comandos CQL desde Python. Cubrimos las sentencias simples y los beneficios críticos de rendimiento al usar Prepared Statements para consultas frecuentes.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 6 de 12. Si utilizas un formateo de strings simple para tus queries de alto throughput, estás obligando a tu base de datos a quemar valiosos ciclos de CPU volviendo a parsear exactamente la misma estructura miles de veces por segundo. La forma de detener este desperdicio y mejorar enormemente el rendimiento de tu aplicación es utilizando Prepared Statements. Cuando te conectas a Cassandra, la forma más directa de interactuar con la base de datos es pasándole un string al método session punto execute. Le pasas una query y te devuelve un result set. Por defecto, cada fila de ese resultado vuelve como una namedtuple de Python. Esto significa que puedes acceder a los valores de tus columnas usando una simple dot notation, como row punto name o row punto age. Es limpio y funciona bien para operaciones puntuales. Pero enviar un raw string es muy ineficiente para las queries que ejecutas constantemente. Podrías pensar que ya estás haciendo las cosas bien si usas parámetros posicionales. Si pasas un query string que contiene marcadores de porcentaje s junto con una secuencia de valores, el driver de Python formatea esa query de forma segura. Esto previene ataques de inyección, pero arquitectónicamente, no cambia nada en el rendimiento. Sigues enviando el query string completo a Cassandra a través de la red cada vez. Cassandra aún tiene que recibir el texto, parsear la sintaxis y calcular el query plan desde cero. Aquí está la clave. No necesitas parsear la misma estructura dos veces. Aquí es donde entra en juego el método session punto prepare. En lugar de ejecutar la query inmediatamente, le pasas tu query string al método prepare. En este string, reemplazas los valores dinámicos con signos de interrogación. El driver envía esta plantilla a Cassandra. Cassandra la parsea, la valida, calcula el execution plan más eficiente y luego genera un identificador único para este statement específico. Envía este ID de vuelta a tu aplicación Python. A partir de ese momento, cada vez que necesites ejecutar esa query, tu aplicación no enviará un string. Vincula tus variables específicas al objeto Prepared Statement y envía solo el ID único junto con los raw bytes de los valores. Piensa en un servicio de autenticación con mucho tráfico. Necesitas buscar a un usuario por su ID durante el login. La query es select asterisco from users where user id igual a signo de interrogación. Si tu servicio gestiona diez mil logins por segundo, enviar el query string completo diez mil veces desperdicia ancho de banda de red y CPU de la base de datos. Al preparar el statement una sola vez al iniciar tu aplicación, reduces significativamente el payload de red. Cassandra ve el ID, recupera instantáneamente el execution plan precalculado y obtiene los datos de inmediato. Los Prepared Statements trasladan el trabajo pesado del parseo de queries de un coste recurrente por request a un único coste de setup. Eso es todo por este episodio. ¡Hasta la próxima!
7

Paginación de consultas grandes

3m 44s

Nunca bloquees tu aplicación cargando un conjunto de datos masivo en la memoria. Descubre cómo el driver de Python pagina automáticamente los resultados de consultas grandes y cómo gestionar los fetch sizes.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 7 de 12. Ejecutas una select query sencilla en una tabla enorme y, de repente, tu aplicación falla con un error out of memory. O al menos, eso pasaría si el driver de tu base de datos intentara cargarlo todo a la vez. En cambio, el driver lo gestiona sin problemas en background. Hoy hablamos sobre Paging Large Queries. Cuando ejecutas una query usando el driver de Python para Cassandra, este no intenta meter millones de filas en la memoria de tu aplicación. Por defecto, el driver pagina automáticamente los resultados, trayendo exactamente 5000 filas cada vez. El result set que recibes actúa como un iterator estándar de Python. A medida que haces un loop por las filas, el driver se conecta de forma transparente a la base de datos para traer el siguiente lote justo antes de que te quedes sin ellas. Tu código se ve exactamente como un loop estándar, pero por debajo, el driver garantiza la seguridad de la memoria manteniendo solo una página de datos en memoria en todo momento. No tienes por qué quedarte con el valor por defecto de 5000 filas. Puedes controlar esto configurando la propiedad fetch size en tu objeto statement antes de pasarlo al método execute. Si bajas el fetch size a 100, el driver mantendrá menos datos en memoria, pero hará network round trips más frecuentes a la base de datos. No confundas este mecanismo con la paginación SQL tradicional que usa los comandos limit y offset. La offset pagination de SQL obliga a la base de datos a escanear y descartar filas antes de devolver tus datos, lo que se vuelve drásticamente más lento cuanto más profundo paginas. Cassandra usa un enfoque basado en cursores. El driver usa un marcador interno para rastrear la ubicación física exacta en la base de datos donde terminó la última lectura. El paging automático es perfecto para el procesamiento de datos en background, pero no funciona para construir aplicaciones web stateless. Imagina un endpoint web que proporciona una lista con scroll continuo de miles de audit logs a una interfaz de usuario. No puedes mantener una conexión a la base de datos y un iterator activo abiertos en tu servidor mientras esperas a que el usuario haga scroll. Necesitas una forma de detener la query, cerrar la request y reanudarla más tarde. Aquí está la clave. El driver proporciona una propiedad en el result set llamada paging state. Se trata de un byte string opaco que representa la posición exacta del cursor de tu query. Para construir una API stateless, ejecutas una query, coges una sola página de audit logs y recuperas este paging state. Conviertes los bytes a un hex string normal y lo envías a tu frontend junto con los datos. Cuando el usuario hace scroll hasta el final de la interfaz, el frontend envía ese hex string exacto de vuelta a tu servidor. Tu backend decodifica el string y lo pasa al método execute como parámetro del paging state. Cassandra reanuda la query al instante justo donde se quedó. Usar el paging state te permite desconectar el ciclo de vida de la memoria de tu aplicación de la escala masiva de las tablas de tu base de datos, permitiéndote hacer stream de datos infinitos a los clientes usando una cantidad estrictamente finita de RAM en el servidor. Eso es todo por este episodio. Gracias por escuchar, y sigue programando.
8

Consultas asíncronas de alto rendimiento

3m 55s

Maximiza el rendimiento de tu aplicación. Aprende a usar execute_async, ResponseFutures y callbacks para ejecutar peticiones concurrentes contra Cassandra.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 8 de 12. Esperar la respuesta de la base de datos antes de enviar tu siguiente request es la forma más fácil de ralentizar tu propia aplicación. Si tu script de Python se queda inactivo durante los round trips de red, estás perdiendo un rendimiento de escritura enorme. Para solucionar esto, necesitas queries async de alto throughput. Cuando usas el método execute estándar en una sesión de Cassandra, tu código se bloquea. Envía la query, espera a que la base de datos la procese, y espera a que la red devuelva la respuesta. Para un pipeline de ingesta de datos, este tiempo de inactividad es fatal para tu throughput general. Para desbloquear la velocidad real, el driver de Python de DataStax te ofrece el método execute async. Primero, tenemos que aclarar una confusión muy común. Si escuchas la palabra async en Python, probablemente pienses en el módulo asyncio de la librería estándar y en las keywords async await. Pero no es eso. El driver de Cassandra usa su propio event loop ligero ejecutándose en un background thread. Cuando llamas a execute async, estás usando el mecanismo personalizado del driver, no las funciones async nativas de Python. Cuando le pasas una query a execute async, no espera la respuesta de la base de datos. Le devuelve el control a tu programa al instante. Lo que te devuelve es un objeto llamado ResponseFuture. Este objeto es una promise. Representa una operación de base de datos que se ha enviado al clúster, pero que todavía no ha terminado. Como tu main thread ya no está esperando, necesitas una forma de saber cuándo termina realmente la query o si falla. Esto lo gestionas añadiendo callbacks directamente al ResponseFuture. Primero, defines una función de éxito que recibe el result set como argumento. Luego, defines una función de error que recibe una exception como argumento. Finalmente, vinculas ambas funciones al future usando el método add callbacks. Cuando la base de datos responde, el background thread recibe el paquete de red y dispara automáticamente la función correcta. Aquí está la clave. Este mecanismo te permite procesar en pipeline cientos de operaciones simultáneamente. Imagina un pipeline de IoT recibiendo cientos de lecturas de temperatura por segundo. Usando el método síncrono, procesas una lectura, esperas a la base de datos, y luego procesas la siguiente. Usando execute async, recorres tus lecturas entrantes en un loop sin parar. Por cada lectura, lanzas una query de insert, coges el ResponseFuture, le añades tus callbacks de éxito y error, y pasas inmediatamente a la siguiente lectura. Envías cientos de requests a la red en una fracción de segundo. El driver multiplexa estas queries a través de tus conexiones de base de datos existentes. El clúster de Cassandra las gestiona en paralelo y devuelve los resultados en stream. Tus callbacks de éxito y error se disparan a medida que llegan las respuestas, de forma totalmente independiente del orden en que las enviaste. Este enfoque aumenta drásticamente tu throughput porque solapa el tiempo de espera de la red para todas esas queries. Solo estás limitado por tu ancho de banda de red y la capacidad de la base de datos, en lugar de estar limitado por el thread blocking. Eso sí, necesitas controlar cuántas requests tienes in flight para no sobrecargar la queue del driver, pero el principio fundamental es mantener el pipeline de red lleno. La conclusión más importante aquí es que el throughput de la base de datos consiste en minimizar el tiempo de inactividad, y al usar a fondo execute async con callbacks, obligas a tu aplicación a dedicar su tiempo a enviar datos en lugar de esperar a la red. Si te ha parecido útil y quieres apoyar el programa, puedes buscar DevStoriesEU en Patreon. Eso es todo por este episodio. ¡Gracias por escuchar, y a seguir programando!
9

Lightweight Transactions

4m 01s

Implementa operaciones compare-and-set de forma segura. Aprende cómo funcionan las Lightweight Transactions (LWTs) en Cassandra y cómo inspeccionar la columna especializada applied en tus resultados de Python.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 9 de 12. En un sistema distribuido sin locks globales, dos usuarios intentan registrar exactamente el mismo nombre de usuario en el mismo milisegundo. ¿Cómo garantizas que solo uno de ellos lo consiga? El mecanismo que resuelve esto se llama Lightweight Transaction. Antes de explicar cómo implementarlas, tenemos que aclarar el nombre. Una Lightweight Transaction en Cassandra no es una transacción ACID tradicional multi-tabla. No puedes abrir un bloque de transacción, escribir en tres tablas diferentes y hacer un rollback de todo si falla un paso. Una Lightweight Transaction está estrictamente limitada a una sola partición. Es una operación condicional, esencialmente un compare and set distribuido. Para reclamar un nombre de usuario único sin race conditions, escribes una query de insert estándar, pero le añades la cláusula específica IF NOT EXISTS al final. Cassandra comprobará en el clúster si esa partition key ya existe. Si no existe, la escritura sigue adelante. Para una operación de update, usas la cláusula IF seguida del nombre de una columna y un valor esperado. Puedes indicarle a la base de datos que actualice el estado de una cuenta solo IF el estado actual coincide con un string específico. Ejecutar estas queries desde Python requiere manejar la respuesta de forma diferente a una escritura normal. Una escritura estándar en Cassandra o tiene éxito silenciosamente o lanza un error de timeout. Pero cuando añades una cláusula IF, la base de datos debe decirle a tu aplicación si la condición realmente se cumplió. El driver de Python maneja esto devolviendo un result set especializado. Cuando ejecutas una Lightweight Transaction, la primera fila de los datos devueltos contiene una columna booleana especial del sistema. El driver expone esta columna bajo el nombre applied, entre corchetes. Ejecutas tu query de insert y obtienes la primera fila del resultado. Luego, evalúas esa columna corchete applied corchete. Si estás devolviendo filas de diccionario desde el driver, accedes a la key como un string. Si el valor se evalúa como true, la condición se cumplió, y tu nuevo usuario reclamó el nombre de usuario con éxito. La operación se ha completado. Aquí está la clave. Tienes que entender exactamente qué pasa cuando esa columna corchete applied corchete se evalúa como false. Si el insert falla porque el nombre de usuario ya está en uso, Cassandra no devuelve simplemente un flag de rechazo. El driver rellena la fila de resultados con los datos reales que hicieron que tu condición fallara. Como tu condición fue rechazada, la base de datos lee la fila existente y se la devuelve a tu aplicación Python en exactamente la misma respuesta. Recibes el flag applied en false, y junto a él, recibes el estado actual del registro conflictivo. Si estabas intentando hacer un update de un saldo basándote en un saldo anterior esperado, obtienes el saldo actual real de vuelta inmediatamente. Tu código Python sabe exactamente qué datos bloquearon la transacción, eliminando por completo la necesidad de ejecutar una query select posterior para averiguar por qué se rechazó la escritura. Las Lightweight Transactions te dan un control de concurrencia estricto a nivel de partición, y el driver de Python lo hace altamente eficiente dándote el estado de bloqueo gratis siempre que una condición falla. Gracias por escuchar. Cuidaos todos.
10

Los modelos del Object Mapper

4m 13s

Evita las cadenas CQL en crudo y modela tus datos usando clases de Python. Aprende a usar cqlengine para definir tablas, especificar primary keys y sincronizar tu esquema.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 10 de 12. Escribir strings raw de CQL dentro del código de tu aplicación Python puede convertirse rápidamente en un caos imposible de mantener. Terminas con strings multilínea llenos de interpolación de variables, lo que facilita los typos y hace que refactorizar el esquema sea un dolor. Necesitas una forma de representar las tablas de tu base de datos como objetos nativos de Python. Esto es exactamente lo que te dan los modelos del Object Mapper. El driver de Python de Datastax incluye un object mapper llamado cqlengine. Te permite definir tablas de Cassandra usando clases estándar de Python. Si alguna vez has usado Django ORM o SQLAlchemy, la sintaxis te resultará muy familiar. Pero hay una diferencia importante que conviene aclarar de inmediato. En esos mappers relacionales, declaras foreign keys para vincular las tablas. Cassandra no es una base de datos relacional, así que los relational bindings simplemente no existen aquí. Un modelo en cqlengine se mapea exactamente a una tabla independiente de Cassandra. Vamos a crear un modelo Comment para una aplicación de fotos. Queremos agrupar todos los comentarios de una foto específica y ordenarlos cronológicamente. Primero, creas una clase de Python llamada Comment que hereda de la clase base Model de cqlengine. Dentro de esta clase, defines tus columnas como atributos de clase. Empezamos con el atributo photo guion bajo id. Lo asignas a un tipo de columna UUID y le pasas el argumento primary guion bajo key igual a True. Como esta es la primera primary key que declaras en la clase, cqlengine la asigna automáticamente como partition key. A continuación, necesitamos una forma de identificar de forma única y ordenar cada comentario dentro de esa partición de la foto. Defines un segundo atributo llamado comment guion bajo id. Lo asignas a un tipo de columna TimeUUID, y también le pasas primary guion bajo key igual a True. Aquí es donde la cosa se pone interesante. En cqlengine, cualquier primary key definida después de la partition key se convierte automáticamente en una clustering key. No necesitas un bloque especial de configuración de clustering. El orden literal de arriba a abajo en el que defines los atributos de columna dentro de tu clase de Python dicta la estructura de la primary key en Cassandra. Una vez definidas las primary keys, añades las columnas de datos reales. Defines un atributo body y le asignas un tipo de columna Text. Son solo datos normales, así que no se necesitan argumentos de primary key. Ahora tienes una clase de Python declarativa que describe el esquema de tu tabla. Pero la tabla aún no existe en tu base de datos. Para hacer push de este esquema a Cassandra, usas una función llamada sync guion bajo table. Le pasas tu clase del modelo Comment directamente a esta función. El mapper lee la estructura de tu clase, la traduce al statement raw de CQL correcto y lo ejecuta. Si la tabla no existe, sync guion bajo table la crea. Si la tabla ya existe, comprueba si has añadido nuevas columnas a tu clase de Python y altera la tabla para que coincida. Es importante saber que sync guion bajo table nunca hará drop de los datos ni alterará las primary keys existentes, manteniendo a salvo tu estructura de datos principal durante las actualizaciones. El verdadero poder de definir modelos de esta manera no es solo ocultar la sintaxis de la base de datos, sino fijar la definición de tu esquema directamente en la capa de aplicación, donde vive la lógica de negocio. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue construyendo!
11

Haciendo consultas con cqlengine

3m 58s

Recupera y filtra datos de forma fluida usando objetos QuerySet en el Object Mapper cqlengine. Cubrimos los operadores de filtrado, la inmutabilidad y las limitaciones en la ordenación.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 11 de 12. Filtrar registros de una base de datos con un object mapper suele parecer muy sencillo, dejándote buscar por cualquier campo que quieras. Pero en Cassandra, si intentas filtrar por una columna que no está indexada explícitamente o que no es parte de una primary key, la base de datos se negará en rotundo a ejecutar tu query. La solución es entender cómo construir requests válidas, lo que nos lleva a Hacer Queries con cqlengine. En cqlengine, cuando quieres recuperar datos de un modelo, interactúas con un QuerySet. Si tienes un modelo que representa una tabla de la base de datos, puedes acceder a los registros llamando al atributo objects seguido del método all. Esto devuelve un QuerySet que representa cada fila de esa tabla. Traerse cada fila rara vez es lo que quieres en una base de datos distribuida, así que necesitas una forma de acotar los resultados. Para restringir los datos devueltos, usas el método filter. Un punto común de confusión aquí es cómo ocurre realmente este filtrado. Los developers que vienen de otros frameworks a veces asumen que el object mapper se trae todos los datos al cliente de Python y filtra la lista en memoria. Eso es completamente incorrecto. El método filter se mapea directamente a una cláusula WHERE estricta de CQL. Esto significa que las columnas que le pasas al método filter deben cumplir con las reglas de las queries de Cassandra. Solo puedes filtrar por partition keys, clustering columns o columnas con un secondary index. Si intentas filtrar por un campo de texto estándar sin indexar, cqlengine no ocultará el error ni lo procesará localmente. Pasará la query directamente a Cassandra, y Cassandra la rechazará. Veamos un escenario concreto. Tienes un modelo Automobile y quieres encontrar todos los coches de Tesla fabricados después del año 2012. Empiezas llamando a objects punto filter. Pasas el keyword argument manufacturer asignado a la string Tesla. Para manejar la condición del año, cqlengine proporciona operadores de filtrado especiales. Los aplicas añadiendo un doble guion bajo y la abreviatura del operador directamente al nombre de la columna. Para estrictamente mayor que, usas doble guion bajo g t. Así que añades un segundo keyword argument a tu llamada a filter: year doble guion bajo g t igual a 2012. El mapper lo traduce sin problemas a una query CQL válida. Hay varios otros operadores disponibles para diferentes condiciones. Si quisieras comprobar modelos de coche específicos en lugar de un año, podrías usar el operador doble guion bajo in. Pasarías model doble guion bajo in, asignado a una lista de Python que contenga los nombres Model S y Model 3. La base de datos devolverá los registros que coincidan con cualquier valor de esa lista. Aquí está la clave. Los QuerySets son completamente inmutables. Cuando llamas al método filter en un QuerySet existente, no modifica ese objeto in place. En su lugar, devuelve un QuerySet totalmente nuevo con los filtros adicionales aplicados. Puedes crear un QuerySet base que solo filtre por el manufacturer Tesla y asignarlo a una variable. Luego, puedes usar esa única variable para generar múltiples QuerySets filtrados diferentes para distintos años o modelos, simplemente llamando a filter en la variable base de nuevo. La query base original permanece completamente inalterada. Como los QuerySets son inmutables, puedes construir programáticamente queries complejas y muy específicas paso a paso, reutilizando de forma segura las condiciones base en toda tu aplicación antes de hacer ninguna llamada de red. Gracias por pasarte. Espero que hayas aprendido algo nuevo.
12

Vector Search para IA

4m 15s

Prepara tus habilidades para el futuro con el Vector Search de Cassandra 5.0. Descubre cómo almacenar y consultar vectores de alta dimensionalidad para impulsar aplicaciones modernas de IA y machine learning.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. Apache Cassandra con Python, episodio 12 de 12. Los Large Language Models están cambiando la forma en que construimos aplicaciones, pero plantean un gran reto de almacenamiento. Cuando un modelo de inteligencia artificial necesita contexto, las queries tradicionales a bases de datos fallan porque buscan coincidencias exactas de caracteres, perdiéndose por completo la intención real detrás de un prompt. Los workloads modernos de inteligencia artificial requieren una forma fundamentalmente distinta de consultar datos. Ahí es donde entra Vector Search, introducido de forma nativa en Cassandra 5.0. Es fácil confundir Vector Search con una simple búsqueda léxica full-text. Un índice full-text estándar, como Lucene, se basa estrictamente en keywords. Si un usuario busca en tu base de datos la frase database backup, una búsqueda léxica escanea buscando esos strings exactos. Vector Search funciona de manera diferente. En lugar de keywords literales, los vectores capturan el significado semántico subyacente de los datos. Un Vector Search entiende que guardar un snapshot de datos o archivar tablas está relacionado con el mismo concepto que un database backup, incluso si las palabras no comparten ninguna letra en común. Para que esto funcione, Cassandra se basa en vector embeddings. Un embedding es un array de números en coma flotante generado por un modelo de machine learning. Estos arrays actúan como coordenadas matemáticas que representan el significado más profundo de tu texto. Almacenas estos arrays directamente en Cassandra junto a los datos estándar de tu aplicación. Cuando necesitas encontrar contenido relevante dentro de colecciones masivas de documentos, haces un Vector Search. La base de datos compara el vector de la query entrante con los vectores almacenados en tus tablas. Calcula la distancia matemática entre ellos. Las distancias más cortas indican una mayor similitud semántica. Imagina que estás construyendo un chatbot interno para un gran equipo de ingeniería. Tienes miles de páginas de documentación técnica. Un ingeniero escribe una pregunta sobre cómo desmantelar un cluster de staging. Primero, tu aplicación pasa esa pregunta a un modelo de embeddings, que traduce la frase a un array de floats. A continuación, envías ese array numérico a Cassandra como una query de Vector Search. Cassandra escanea al instante el espacio multidimensional y recupera los documentos internos cuyos embeddings están más cerca de la pregunta. Devuelve el manual correcto para retirar entornos de testing porque el significado semántico se alinea a la perfección, independientemente de que la terminología sea distinta. Esta capacidad está construida explícitamente para aplicaciones de inteligencia artificial. La mayoría de estas aplicaciones dependen de recuperar contexto altamente relevante para alimentar al modelo de lenguaje antes de que genere una respuesta. Al llevar Vector Search directamente a Cassandra 5.0, eliminas la necesidad de ejecutar una base de datos vectorial standalone por separado. Mantienes tus registros operativos principales y sus embeddings semánticos en exactamente la misma arquitectura distribuida, confiando en la alta disponibilidad y el escalado que ofrece Cassandra. Como esto concluye nuestra serie sobre Apache Cassandra, te animo a explorar la documentación oficial y a probar a generar embeddings para tus propios datos de forma práctica. Si tienes sugerencias sobre qué tecnologías deberíamos cubrir a continuación, visita devstories dot eu y cuéntanoslo. Vector Search cierra la brecha entre el lenguaje y el almacenamiento, convirtiendo la compleja intención humana en un problema de geometría que Cassandra puede resolver a escala. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue construyendo!