Voltar ao catálogo
Season 17 12 Episódios 43 min 2026

Apache Cassandra with Python

Edição de 2026. Uma série de podcasts técnicos que explora a arquitetura distribuída do Apache Cassandra e como interagir com ela usando o DataStax Python Driver. Aborda a modelação de dados, Execution Profiles, LWTs, queries async e o Object Mapper cqlengine.

Bases de Dados Computação Distribuída
Apache Cassandra with Python
A Reproduzir
Click play to start
0:00
0:00
1
A Visão Geral
Uma introdução ao Apache Cassandra. Saiba por que motivo as aplicações à escala global escolhem esta base de dados NoSQL distribuída e como se diferencia dos sistemas relacionais tradicionais.
3m 36s
2
Consistent Hashing e o Anel
Mergulhe na arquitetura do Cassandra. Exploramos o consistent hashing, o token ring e como os dados são particionados por vários nós sem um servidor mestre.
3m 36s
3
Modelação de Dados Orientada a Queries
Desaprenda tudo o que sabe sobre bases de dados relacionais. Saiba como a modelação orientada a queries do Cassandra exige desnormalização e a diferença crucial entre partition keys e clustering keys.
3m 00s
4
Estabelecer Ligação com Python
Comece a usar o DataStax Python Driver. Aprenda a instanciar um Cluster, ligar-se a uma Session e estabelecer comunicação com os seus nós Cassandra.
3m 37s
5
Execution Profiles
Gira cargas de trabalho complexas de forma contínua utilizando Execution Profiles. Aprenda a configurar o balanceamento de carga, timeouts e níveis de consistência por query sem poluir a configuração do seu cluster.
3m 45s
6
Prepared Statements
Aprenda a executar comandos CQL a partir de Python. Abordamos simple statements e os benefícios críticos de desempenho ao utilizar Prepared Statements para queries frequentes.
3m 01s
7
Paginação de Queries Grandes
Nunca bloqueie a sua aplicação ao carregar um conjunto de dados massivo para a memória. Descubra como o driver de Python pagina automaticamente os resultados de queries grandes e como gerir os fetch sizes.
3m 41s
8
Queries Async de Alto Rendimento
Maximize o rendimento da sua aplicação. Aprenda a usar execute_async, ResponseFutures e callbacks para executar pedidos concorrentes no Cassandra.
4m 04s
9
Lightweight Transactions
Implemente operações compare-and-set com segurança. Aprenda como funcionam as Lightweight Transactions (LWTs) no Cassandra e como inspecionar a coluna especializada applied nos seus resultados em Python.
3m 23s
10
Os Modelos do Object Mapper
Evite strings CQL em bruto e modele os seus dados usando classes Python. Aprenda a usar o cqlengine para definir tabelas, especificar primary keys e sincronizar o seu schema.
3m 59s
11
Fazer Queries com o cqlengine
Recupere e filtre dados de forma fluente usando objetos QuerySet no Object Mapper cqlengine. Abordamos operadores de filtragem, imutabilidade e limitações na ordenação.
3m 55s
12
Vector Search para IA
Prepare as suas competências para o futuro com o Vector Search do Cassandra 5.0. Descubra como armazenar e fazer queries a vetores de alta dimensão para impulsionar aplicações modernas de IA e machine learning.
3m 53s

Episódios

1

A Visão Geral

3m 36s

Uma introdução ao Apache Cassandra. Saiba por que motivo as aplicações à escala global escolhem esta base de dados NoSQL distribuída e como se diferencia dos sistemas relacionais tradicionais.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. Apache Cassandra com Python, episódio 1 de 12. As bases de dados relacionais batem numa parede quando tentas fazer deploy delas em vários continentes. Acabas por lutar contra a latência, downtime, ou uma arquitetura frágil em que um único servidor a falhar deita abaixo as tuas operações de write. Uma escala global massiva exige um paradigma de base de dados completamente diferente. Esse paradigma é o Apache Cassandra. É uma base de dados NoSQL distribuída e open-source, construída para uma escala imensa. Quando os engenheiros olham para o Cassandra pela primeira vez, muitas vezes trazem consigo a bagagem dos sistemas relacionais. Procuram o primary node que lida com os writes e as réplicas read-only que o seguem. Tens de abandonar esse modelo mental imediatamente. O Cassandra não usa um design primary-replica. Opera inteiramente numa arquitetura masterless e multi-primary. Cada nó no cluster é idêntico. Qualquer nó pode aceitar um pedido de read, e qualquer nó pode aceitar um pedido de write. Para perceberes porque é que funciona desta forma, olha para a sua história. O Cassandra foi originalmente desenvolvido combinando duas grandes descobertas de investigação. Primeiro, adotou o design de rede totalmente distribuído e masterless do Amazon Dynamo. Isto dita como os nós comunicam, se descobrem uns aos outros e replicam dados pela rede. Segundo, adotou o log-structured storage engine do Google Bigtable, que gere como os dados são fisicamente escritos no disco. O resultado é um sistema concebido especificamente para disponibilidade contínua em múltiplos datacenters. Considera uma rede social global. Tens utilizadores ativos em Tóquio, Londres e Nova Iorque em simultâneo. Se um utilizador em Londres atualizar o seu perfil, essa operação de write tem de acontecer instantaneamente. Fazer o routing desse pedido através do Atlântico para uma única base de dados central é demasiado lento. Com o Cassandra, o utilizador faz o write num nó local no datacenter de Londres. Esse nó aceita o write localmente e assume imediatamente a responsabilidade de o replicar para Tóquio e Nova Iorque em background. Aqui está o ponto-chave. Como cada nó atua como primary, não há um single point of failure. O Cassandra organiza os seus nós num anel lógico. Quando os dados entram no sistema, um hash matemático determina exatamente quais nós no anel são responsáveis por esse dado específico. Se um grande outage puser todo o datacenter de Londres offline, a rede social não vai abaixo. Tóquio e Nova Iorque continuam a aceitar reads e writes sem interrupção. Quando Londres volta a ficar online, os outros datacenters sincronizam automaticamente os dados em falta para os nós recuperados. Alcanças uma verdadeira disponibilidade global com zero downtime. Este design masterless também significa que o scaling é previsível e linear. Se precisares de mais storage ou mais capacidade de write, basta ligares outro nó ao anel. O cluster deteta automaticamente o novo hardware e redistribui os dados para balancear a carga entre as máquinas ativas. O Cassandra obriga-te a trocar o conforto das queries de bases de dados tradicionais por algo muito mais difícil de construir à escala: a garantia absoluta de que, não importa que hardware falhe, a tua base de dados permanece online e as tuas operações são bem-sucedidas. Se quiseres apoiar o programa, podes procurar por DevStoriesEU no Patreon. É tudo por este episódio. Obrigado por ouvires, e continua a construir!
2

Consistent Hashing e o Anel

3m 36s

Mergulhe na arquitetura do Cassandra. Exploramos o consistent hashing, o token ring e como os dados são particionados por vários nós sem um servidor mestre.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Apache Cassandra com Python, episódio 2 de 12. O hashing de dados ingénuo falha completamente no momento em que adicionas um novo servidor ao teu cluster de base de dados. Quando o número de servidores muda, quase todos os dados têm de ser movidos para um novo local. A solução para este caos de escalabilidade é o Consistent Hashing e o Ring. Uma forma padrão de distribuir dados por vários servidores é o modulo hashing. Se tiveres oito nodes, pegas numa partition key, como um user ID, fazes o hash e divides o resultado por oito. O resto indica-te qual node recebe o perfil de utilizador. Isto funciona perfeitamente até o teu storage ficar cheio e ligares um nono node. Agora divides por nove. Os restos mudam. Quase todos os perfis de utilizador no teu sistema passam de repente a pertencer a um servidor diferente, o que provoca uma enorme tempestade de movimento de dados capaz de arrasar o cluster. O Cassandra evita isto completamente. Usa consistent hashing para distribuir dados de forma previsível pelo cluster, sem depender de nenhum coordenador central. Em vez de um simples cálculo de modulo, o Cassandra mapeia tanto os dados como os nodes para um espaço circular fixo e contínuo chamado token ring. Por defeito, o Cassandra usa uma hash function que gera uma enorme gama de números possíveis. O menor valor de hash possível liga-se diretamente ao maior, formando um círculo fechado. A cada node físico no cluster é atribuído um número específico, ou token, algures neste ring. Quando inseres um perfil de utilizador, o Cassandra faz o hash do user ID para gerar um token. Para descobrir qual node é o dono deste perfil, o sistema localiza o token dos dados no ring e move-se no sentido horário. O primeiro node que encontra é o dono. Pensa novamente no nosso cluster de oito nodes. Se adicionarmos um nono node físico, é-lhe atribuído um único novo token no ring, ficando entre dois nodes existentes. Como a posse dos dados é determinada ao avançar no sentido horário, este novo nono node assume apenas uma fatia específica de dados do seu vizinho imediato no sentido horário. Os outros sete nodes não fazem nada. Os seus dados permanecem completamente intactos. Aqui está o ponto chave. Atribuir exatamente um token a um node físico cria problemas operacionais. É difícil balancear os dados perfeitamente, e quando adicionas um novo node, apenas um servidor vizinho é responsável por passar os dados. Esse único vizinho é massacrado sob carga pesada. Para resolver isto, o Cassandra usa virtual nodes, ou vnodes. Em vez de dar a um servidor físico uma enorme fatia contígua do ring, os vnodes cortam o ring em muitos intervalos menores. A um único node físico são atribuídos centenas de tokens diferentes, distribuídos aleatoriamente pelo ring. Quando adicionas esse nono node físico usando vnodes, ele reclama muitas pequenas fatias do ring de todos os servidores existentes ao mesmo tempo. Agora, em vez de um único vizinho fazer todo o trabalho pesado, o cluster inteiro partilha uniformemente o trabalho de fazer stream de dados para a nova máquina. O consistent hashing e os virtual nodes desacoplam a colocação de dados do número bruto de servidores físicos, o que permite que um cluster escale suavemente e opere de forma previsível, sem nenhum master central a ditar para onde os dados devem ir. É tudo por agora. Até à próxima!
3

Modelação de Dados Orientada a Queries

3m 00s

Desaprenda tudo o que sabe sobre bases de dados relacionais. Saiba como a modelação orientada a queries do Cassandra exige desnormalização e a diferença crucial entre partition keys e clustering keys.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. Apache Cassandra com Python, episódio 3 de 12. Numa base de dados relacional, primeiro crias as tuas tabelas, defines os teus relacionamentos e, a seguir, escreves as tuas queries. No Cassandra, se não souberes as tuas queries exatas antes de começares, a tua base de dados vai falhar. Este é o princípio fundamental do Query Driven Data Modeling. Muitos developers chegam ao Cassandra com fortes hábitos relacionais. Naturalmente, procuras formas de normalizar os teus dados, configurar foreign keys e evitar a duplicação. Tens de abandonar essa mentalidade por completo. O Cassandra não suporta joins. Se tentares normalizar os teus dados em várias tabelas, vais acabar por fazer joins no código da tua aplicação, o que destrói os benefícios de performance pelos quais escolheste o Cassandra em primeiro lugar. O Cassandra exige uma modelação de dados query-driven. Começas por mapear as perguntas exatas que a tua aplicação precisa de fazer à base de dados. Neste modelo, uma query normalmente corresponde a uma tabela. Se precisares de aceder aos mesmos dados de três formas diferentes, crias três tabelas diferentes com os mesmos dados. Isto leva-nos à desnormalização. Duplicar dados não é um erro aqui; é a estratégia fundamental. O espaço em disco é barato, mas reads distribuídos numa rede são caros. Ao escrever os dados juntos no formato exato da tua read query, o Cassandra consegue recuperá-los numa única operação, sem pesquisar em todo o cluster. Para que isto funcione, tens de entender a primary key. Não é apenas um identificador único. Controla exatamente onde e como os teus dados são armazenados no disco. A primary key tem duas partes distintas: a partition key e a clustering key. A partition key dita qual node físico do teu cluster guarda os dados. Todas as rows que partilham a mesma partition key são armazenadas juntas no mesmo node. A clustering key determina a ordenação dessas rows no disco dentro dessa partição específica. Considera o cenário de publicação de revistas da documentação. Supõe que a tua aplicação precisa de fazer o fetch de todas as revistas lançadas por uma editora específica. A tua partition key tem de ser o nome da editora. Quando a query chega, o Cassandra faz o hash do nome da editora, identifica o node exato que contém os dados dessa editora e vai diretamente até ele. Para organizar os resultados de forma natural, podes usar a data de publicação como a tua clustering key. Agora, não só todas as revistas dessa editora estão agrupadas num único node, como também estão armazenadas fisicamente em ordem cronológica. A base de dados simplesmente faz stream dos dados pré-ordenados de volta. Aqui está o ponto-chave. Estás a trocar complexidade de writes por uma velocidade massiva de reads. Escreves os mesmos dados em várias tabelas para satisfazer diferentes views da aplicação, mas quando um utilizador pede esses dados, eles são devolvidos em milissegundos porque a base de dados faz zero computação para os montar. Obrigado por ouvirem. Fiquem bem, pessoal.
4

Estabelecer Ligação com Python

3m 37s

Comece a usar o DataStax Python Driver. Aprenda a instanciar um Cluster, ligar-se a uma Session e estabelecer comunicação com os seus nós Cassandra.

Download
Olá, daqui é o Alex do DEV STORIES DOT EU. Apache Cassandra com Python, episódio 4 de 12. Ao ligares-te a uma base de dados tradicional, apontas a tua aplicação para um único endereço de host. Ao ligares-te a uma base de dados distribuída, podes assumir que tens de rastrear manualmente dezenas de endereços de servidor nos teus ficheiros de configuração. Mas não precisas, porque o driver da tua base de dados atua, na verdade, como um router inteligente. Hoje, vamos ver como fazer a ligação com Python. Imagina um microservice em Python a arrancar. Ele precisa de estabelecer comunicação com um cluster Cassandra local de três nodes. Para começar, importas a classe Cluster do módulo cassandra dot cluster. Inicializas esta classe passando-lhe uma lista de endereços IP, conhecidos como contact points. Se os teus nodes usarem um port não standard, também podes especificar um argumento port aqui; caso contrário, o default é 9042. Os developers muitas vezes confundem este passo com a criação de uma connection string standard de um único DSN, onde tens de listar explicitamente com o que queres falar. Com o Cassandra, não precisas de listar cada node da tua infraestrutura. Se tiveres um cluster enorme de trinta nodes, passar apenas dois ou três endereços IP como contact points é perfeitamente suficiente. Aqui está o ponto chave. Quando o driver de Python arranca, ele liga-se a um desses contact points para fazer o bootstrap. Ele faz queries às tabelas de sistema para fazer o download da topologia atual do cluster. Ao fazer isto, descobre automaticamente os endereços IP dos restantes nodes. O driver mantém este mapa de rede dinamicamente em background. Se fizeres scale out e adicionares nodes mais tarde, o driver deteta a alteração e ajusta o seu routing automaticamente, sem exigir o restart da aplicação. Assim que instanciares o teu objeto Cluster com esses contact points iniciais, chamas o seu método connect. Esta ação devolve um objeto Session. A Session gere o verdadeiro connection pooling para os nodes que acabou de descobrir. Ao chamar o connect, podes opcionalmente passar um nome de keyspace. Um keyspace funciona como um namespace para os teus dados. Se for fornecido, o driver define-o como default para todas as operações futuras nessa Session. Como a Session gere connection pools complexos nos bastidores, foi desenhada para ser long-lived e thread-safe. Normalmente, crias uma Session no startup da aplicação e reutilizas a mesma. Agora, a segunda parte disto é ligares-te a um managed cloud service como o DataStax Astra. O Astra opera de forma diferente e não expõe endereços IP raw para os contact points. Em vez disso, fazes o download de um secure connect bundle. Este é um ficheiro zip que contém os certificados necessários e os detalhes da ligação mutual TLS. No teu código Python, ignoras a lista de endereços IP. Em vez disso, forneces um dictionary de configuração cloud ao objeto Cluster. Este dictionary contém uma key chamada secure connect bundle, que aponta para o file path local do teu ficheiro zip. Combinas isto com um objeto authentication provider em plaintext configurado com o teu client ID e secret. Chamar o connect gera então um objeto Session standard, a funcionar exatamente como no setup do cluster local. A principal conclusão é que, independentemente de passares alguns endereços IP locais ou um cloud secure bundle, o driver de Python pega no teu entry point inicial, mapeia a rede da base de dados distribuída e abstrai completamente a lógica de routing do código da tua aplicação. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!
5

Execution Profiles

3m 45s

Gira cargas de trabalho complexas de forma contínua utilizando Execution Profiles. Aprenda a configurar o balanceamento de carga, timeouts e níveis de consistência por query sem poluir a configuração do seu cluster.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Apache Cassandra com Python, episódio 5 de 12. À medida que a tua aplicação cresce, aplicar um timeout ou consistency level único a todas as tuas queries de base de dados torna-se um enorme bottleneck operacional. Acabas por falhar background jobs prematuramente ou por bloquear a tua user interface enquanto esperas por uma read pesada. Os Execution Profiles são o mecanismo que resolve esta tensão. Muitos developers confundem a configuração de execução com o setup legado ao nível do cluster. Em paradigmas mais antigos, definias parâmetros globais diretamente no objeto cluster, o que significava que todas as queries partilhavam exatamente o mesmo timeout. Os Execution Profiles substituem esse padrão rígido. Eles permitem-te manter várias configurações distintas em simultâneo dentro de uma única session ativa. Considera uma aplicação web multi-tenant. A tua interface de front-end requer um timeout estrito de um segundo para garantir que as páginas web se mantêm rápidas. Entretanto, as tuas tarefas de reporting em background precisam de trinta segundos para agregar grandes partições em segurança. Um Execution Profile é um bundle independente e nomeado de configurações de request, feito à medida precisamente para estes diferentes workloads. Para construir isto, começas por criar instâncias de um objeto Execution Profile. Para a tarefa de reporting, instancias um perfil e defines o parâmetro de request timeout para trinta segundos. Podes ir mais longe e associar uma load balancing policy específica a este objeto, talvez encaminhando essas reads analíticas pesadas exclusivamente para um data center de analytics dedicado. A seguir, crias um segundo objeto de perfil distinto para a tua user interface, atribuindo-lhe um timeout de um segundo e uma load balancing policy local. Também podes agrupar retry policies ou consistency levels distintos nestes perfis, dependendo do que a query exigir. Aqui está o ponto-chave. Registas estes perfis uma única vez, durante o setup inicial do teu cluster, em vez de os construíres durante a própria execução da query. Ao instanciar o cluster, passas um dictionary para o argumento execution profiles. Este dictionary mapeia nomes de string simples para os objetos de perfil que acabaste de criar. O driver gere estas configurações nos bastidores. Existe sempre um perfil de fallback built-in, que podes substituir mapeando uma configuração para uma constante específica chamada execution profile default. Se executares uma query sem nomear explicitamente um perfil, o driver aplica automaticamente estas configurações de default. Quando precisas realmente de correr uma query, usas os métodos execute ou execute async padrão da session. Juntamente com a tua query string ou prepared statement, passas um argumento de execution profile usando o nome de string simples que registaste anteriormente. O driver interceta este nome, recupera as configurações agrupadas ligadas a ele e aplica-as a esse request específico. A tua query da user interface é estritamente limitada a um segundo, e o background job corre confortavelmente durante trinta segundos. Ambas as queries correm em simultâneo, multiplexadas sobre exatamente a mesma session e exatamente o mesmo connection pool. Mover a tua configuração para Execution Profiles desacopla o comportamento da tua query da tua ligação física à base de dados. Isto permite que uma única aplicação molde o seu tráfego de base de dados dinamicamente com base nas necessidades específicas de cada função, sem nunca precisar de estabelecer sessions separadas. Obrigado por estares aí. Espero que tenhas aprendido algo novo.
6

Prepared Statements

3m 01s

Aprenda a executar comandos CQL a partir de Python. Abordamos simple statements e os benefícios críticos de desempenho ao utilizar Prepared Statements para queries frequentes.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Apache Cassandra com Python, episódio 6 de 12. Se estás a usar formatação de string simples para as tuas queries de alto débito, estás a forçar a tua base de dados a queimar ciclos preciosos de CPU para fazer o re-parsing da exata mesma estrutura milhares de vezes por segundo. A forma de parar este desperdício, e melhorar drasticamente a performance da tua aplicação, é usando Prepared Statements. Quando te ligas ao Cassandra, a forma mais direta de interagir com a base de dados é passando uma string para o método session dot execute. Passas-lhe uma query, e ele devolve um result set. Por omissão, cada row nesse resultado volta como um namedtuple de Python. Isto significa que podes aceder aos valores das tuas colunas usando a notação de ponto simples, como row dot name ou row dot age. É limpo, e funciona bem para operações pontuais. Mas enviar uma raw string é altamente ineficiente para queries que corres constantemente. Podes achar que já estás a fazer as coisas bem se usares parâmetros posicionais. Se passares uma query string contendo marcadores percent-s juntamente com uma sequência de valores, o driver de Python formata essa query de forma segura. Isto previne ataques de injeção, mas, arquiteturalmente, não muda nada na performance. Continuas a enviar a query string completa pela rede para o Cassandra todas as vezes. O Cassandra ainda tem de receber o texto, fazer o parsing da sintaxe, e calcular o query plan do zero. Aqui está o ponto chave. Não precisas de fazer o parsing da mesma estrutura duas vezes. É aqui que entra o método session dot prepare. Em vez de executares a query imediatamente, passas a tua query string para o método prepare. Nesta string, substituis os valores dinâmicos por pontos de interrogação. O driver envia este template para o Cassandra. O Cassandra faz o parsing, valida-o, calcula o execution plan mais eficiente, e depois gera um identificador único para este statement específico. Ele envia este ID de volta para a tua aplicação Python. A partir desse momento, sempre que precisares de correr essa query, a tua aplicação não envia uma string. Ela faz o bind das tuas variáveis específicas ao objeto do prepared statement e envia apenas o ID único juntamente com os raw bytes dos valores. Pensa num serviço de autenticação de alto tráfego. Precisas de procurar um utilizador pelo seu ID durante o login. A query é select star from users where user id equals question mark. Se o teu serviço processa dez mil logins por segundo, enviar a query string completa dez mil vezes desperdiça largura de banda da rede e CPU da base de dados. Ao preparares o statement uma vez quando a tua aplicação arranca, reduzes significativamente o payload de rede. O Cassandra vê o ID, puxa instantaneamente o execution plan pré-calculado, e obtém os dados imediatamente. Os prepared statements transferem o trabalho pesado do parsing da query de uma taxa recorrente por request para um custo único de setup. É tudo por agora. Até à próxima!
7

Paginação de Queries Grandes

3m 41s

Nunca bloqueie a sua aplicação ao carregar um conjunto de dados massivo para a memória. Descubra como o driver de Python pagina automaticamente os resultados de queries grandes e como gerir os fetch sizes.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Apache Cassandra com Python, episódio 7 de 12. Executas uma select query simples numa tabela enorme e, de repente, a tua aplicação crasha com um erro out-of-memory. Ou pelo menos, crasharia se o driver da tua base de dados tentasse carregar tudo de uma vez. Em vez disso, o driver lida com isto de forma elegante em background. Hoje, vamos falar sobre paginação de queries grandes. Quando executas uma query usando o driver de Python do Cassandra, ele não tenta puxar milhões de linhas para a memória da tua aplicação. Por defeito, o driver pagina automaticamente os resultados, indo buscar exatamente 5000 linhas de cada vez. O result set que te é devolvido funciona como um iterator padrão de Python. À medida que fazes um loop pelas linhas, o driver acede à base de dados de forma transparente para ir buscar o próximo batch mesmo antes de ficares sem dados. O teu código parece exatamente um loop normal, mas, por baixo dos panos, o driver garante a segurança de memória mantendo apenas uma única página de dados em memória a qualquer momento. Não estás preso ao valor por defeito de 5000 linhas. Podes controlar isto definindo a propriedade fetch size no teu objeto statement antes de o passares para o método execute. Se baixares o fetch size para 100, o driver vai manter menos dados em memória, mas fará round trips de rede mais frequentes à base de dados. Não confundas este mecanismo com a paginação SQL tradicional que usa os comandos limit e offset. A paginação SQL com offset força a base de dados a fazer scan e a descartar linhas antes de devolver os teus dados, o que fica drasticamente mais lento quanto mais avanças na paginação. O Cassandra usa uma abordagem baseada em cursor. O driver usa um marcador interno para rastrear a localização física exata na base de dados onde a última leitura terminou. A paginação automática é perfeita para processamento de dados em background, mas não funciona para construir aplicações web stateless. Imagina um endpoint web que fornece uma lista de scroll contínuo com milhares de audit logs para uma interface de utilizador. Não podes manter uma ligação à base de dados e um iterator ativo abertos no teu servidor enquanto esperas que o utilizador faça scroll. Precisas de uma forma de parar a query, fechar o request e retomá-lo mais tarde. Aqui está o ponto chave. O driver fornece uma propriedade no result set chamada paging state. Esta é uma byte string opaca que representa a posição exata do cursor da tua query. Para construíres uma API stateless, executas uma query, pegas numa única página de audit logs e recuperas este paging state. Convertes os bytes numa hex string simples e envias para o teu frontend juntamente com os dados. Quando o utilizador faz scroll até ao fim da interface, o frontend envia essa mesma hex string de volta para o teu servidor. O teu backend descodifica a string e passa-a para o método execute como o parâmetro de paging state. O Cassandra retoma instantaneamente a query exatamente onde parou. Usar o paging state permite-te desvincular o tempo de vida em memória da tua aplicação da escala massiva das tabelas da tua base de dados, permitindo-te fazer stream de dados infinitos para os clientes usando uma quantidade estritamente finita de RAM do servidor. É tudo por este episódio. Obrigado por ouvires, e continua a programar!
8

Queries Async de Alto Rendimento

4m 04s

Maximize o rendimento da sua aplicação. Aprenda a usar execute_async, ResponseFutures e callbacks para executar pedidos concorrentes no Cassandra.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Apache Cassandra com Python, episódio 8 de 12. Esperar pela resposta da base de dados antes de enviares o teu próximo request é a maneira mais fácil de limitar o desempenho da tua própria aplicação. Se o teu script Python fica inativo durante os round trips de rede, estás a desperdiçar um enorme desempenho de escrita. Para corrigir isto, precisas de queries async de alto throughput. Quando usas o método execute standard numa sessão de Cassandra, o teu código bloqueia. Ele envia a query, espera que a base de dados a processe, e espera que a rede traga a resposta de volta. Para uma pipeline de ingestão de dados, este tempo de inatividade é fatal para o teu throughput geral. Para desbloquear a verdadeira velocidade, o driver de Python da DataStax oferece o método execute async. Primeiro, precisamos de esclarecer uma confusão comum. Se ouvires a palavra async em Python, provavelmente pensas no módulo asyncio da standard library e nas keywords async e await. Não se trata disso. O driver de Cassandra depende do seu próprio event loop leve a correr numa background thread. Quando chamas o execute async, estás a usar o mecanismo custom do driver, e não as features assíncronas built-in do Python. Quando passas uma query ao execute async, ele não espera por uma resposta da base de dados. Ele devolve o controlo ao teu programa instantaneamente. O que ele devolve é um objeto chamado ResponseFuture. Este objeto é uma promise. Ele representa uma operação na base de dados que foi enviada para o cluster, mas que ainda não terminou. Como a tua main thread já não está à espera, precisas de uma forma de saber quando a query realmente termina ou se falha. Geres isto anexando funções de callback diretamente ao ResponseFuture. Primeiro, defines uma função de sucesso que recebe o result set como argumento. A seguir, defines uma função de erro que recebe uma exception como argumento. Finalmente, ligas ambas as funções ao future usando um método add callbacks. Quando a base de dados responde, a background thread recebe o pacote de rede e dispara automaticamente a função correta. Aqui está o ponto chave. Este mecanismo permite-te fazer pipeline de centenas de operações em simultâneo. Considera uma pipeline de IoT a receber centenas de leituras de temperatura por segundo. Usando o método síncrono, processas uma leitura, esperas pela base de dados e, a seguir, processas a próxima. Usando o execute async, fazes um loop pelas tuas leituras recebidas sem parar. Para cada leitura, disparas uma insert query, agarras no ResponseFuture, anexas os teus callbacks de sucesso e erro, e passas imediatamente para a leitura seguinte. Envias centenas de requests para a rede numa fração de segundo. O driver multiplexa estas queries sobre as tuas ligações existentes à base de dados. O cluster de Cassandra processa-as em paralelo e faz stream dos resultados de volta. Os teus callbacks de sucesso e erro disparam à medida que as respostas chegam, de forma completamente independente da ordem em que os enviaste. Esta abordagem aumenta drasticamente o teu throughput porque sobrepõe o tempo de espera da rede para todas essas queries. Ficas limitado apenas pela bandwidth da tua rede e pela capacidade da base de dados, em vez de estares limitado pelo thread blocking. Precisas de controlar quantos requests tens in flight para não sobrecarregares a queue do driver, mas o princípio fundamental é manter a pipeline de rede cheia. A principal conclusão aqui é que o throughput da base de dados tem a ver com minimizar o tempo de inatividade, e ao utilizares fortemente o execute async com callbacks anexados, forças a tua aplicação a passar o tempo a enviar dados em vez de estar à espera da rede. Se achaste isto útil e queres apoiar o programa, podes procurar por DevStoriesEU no Patreon. É tudo por este episódio. Obrigado por ouvires, e continua a programar!
9

Lightweight Transactions

3m 23s

Implemente operações compare-and-set com segurança. Aprenda como funcionam as Lightweight Transactions (LWTs) no Cassandra e como inspecionar a coluna especializada applied nos seus resultados em Python.

Download
Olá, daqui é o Alex da DEV STORIES DOT EU. Apache Cassandra com Python, episódio 9 de 12. Num sistema distribuído sem locks globais, dois utilizadores tentam registar exatamente o mesmo username no mesmo milissegundo. Como é que garantes que apenas um deles o consegue? O mecanismo que resolve isto chama-se Lightweight Transaction. Antes de explicar como as implementar, precisamos de esclarecer o nome. Uma Lightweight Transaction no Cassandra não é uma transação ACID tradicional multi-table. Não podes abrir um bloco de transação, fazer writes para três tabelas diferentes e fazer rollback a tudo se um passo falhar. Uma Lightweight Transaction tem um scope estritamente limitado a uma única partition. É uma operação condicional, essencialmente um compare and set distribuído. Para garantires um username único sem race conditions, escreves uma insert query standard, mas adicionas a cláusula específica IF NOT EXISTS no fim. O Cassandra vai verificar o cluster para ver se essa partition key já existe. Se não existir, o write avança. Para uma operação de update, usas a cláusula IF seguida do nome de uma coluna e de um valor esperado. Podes instruir a base de dados a fazer update ao status de uma conta apenas IF o status atual corresponder a uma string específica. Executar estas queries a partir de Python exige tratar a resposta de forma diferente de um write normal. Um write standard no Cassandra ou tem sucesso silenciosamente ou lança um erro de timeout. Mas quando adicionas uma cláusula IF, a base de dados tem de dizer à tua aplicação se a condição foi realmente cumprida. O driver de Python lida com isto retornando um result set especializado. Quando executas uma Lightweight Transaction, a primeira row dos dados retornados contém uma coluna de sistema boolean especial. O driver expõe esta coluna com o nome applied, entre parênteses retos. Executas a tua insert query e vais buscar a primeira row do resultado. Depois, avalias essa coluna parêntese reto applied parêntese reto. Se estiveres a retornar dictionary rows do driver, acedes à key como uma string. Se o valor for avaliado como true, a condição foi cumprida, e o teu novo utilizador garantiu o username com sucesso. A operação está concluída. Aqui está o ponto chave. Precisas de perceber exatamente o que acontece quando essa coluna parêntese reto applied parêntese reto é avaliada como false. Se o insert falhar porque o username já está ocupado, o Cassandra não retorna apenas uma simples flag de rejeição. O driver preenche a result row com os dados reais que fizeram a tua condição falhar. Como a tua condição foi rejeitada, a base de dados lê a row existente e devolve-a à tua aplicação Python exatamente na mesma resposta. Recebes a flag applied a false e, juntamente com ela, recebes o estado atual do record em conflito. Se estavas a tentar fazer update a um saldo com base num saldo anterior esperado, recebes o saldo atual real de volta imediatamente. O teu código Python sabe exatamente que dados bloquearam a transação, eliminando por completo a necessidade de correr uma select query a seguir para descobrir por que motivo o write foi rejeitado. As Lightweight Transactions dão-te um concurrency control rigoroso ao nível da partition, e o driver de Python torna isto altamente eficiente ao entregar-te o blocking state de graça sempre que uma condição falha. Obrigado por ouvirem. Fiquem bem, pessoal.
10

Os Modelos do Object Mapper

3m 59s

Evite strings CQL em bruto e modele os seus dados usando classes Python. Aprenda a usar o cqlengine para definir tabelas, especificar primary keys e sincronizar o seu schema.

Download
Olá, daqui é o Alex do DEV STORIES DOT EU. Apache Cassandra com Python, episódio 10 de 12. Escrever strings CQL raw dentro do código da tua aplicação Python pode rapidamente tornar-se numa confusão impossível de manter. Acabas com strings multi-line cheias de interpolação de variáveis, o que facilita os erros de digitação e torna o refactoring do schema doloroso. Precisas de uma forma de representar as tuas tabelas de base de dados como objetos nativos de Python. É exatamente isso que os Object Mapper Models oferecem. O driver de Python da Datastax inclui um object mapper chamado cqlengine. Ele permite-te definir tabelas de Cassandra usando classes standard de Python. Se já usaste o Django ORM ou o SQLAlchemy, a sintaxe vai parecer-te muito familiar. Mas há uma grande diferença a esclarecer desde já. Nesses mappers relacionais, declaras foreign keys para ligar as tabelas entre si. O Cassandra não é uma base de dados relacional, por isso os bindings relacionais simplesmente não existem aqui. Um model no cqlengine mapeia exatamente para uma tabela standalone de Cassandra. Vamos criar um model Comment para uma aplicação de fotos. Queremos agrupar todos os comentários de uma foto específica e queremos que fiquem ordenados cronologicamente. Primeiro, crias uma classe de Python chamada Comment que herda da base class Model do cqlengine. Dentro desta classe, defines as tuas colunas como atributos de classe. Começamos com o atributo photo underscore id. Atribuis-lhe um tipo de coluna UUID e passas o argumento primary underscore key equals True. Como esta é a primeira primary key que declaras na classe, o cqlengine atribui-a automaticamente como partition key. A seguir, precisamos de uma forma de identificar de forma única e ordenar cada comentário dentro dessa partition da foto. Defines um segundo atributo chamado comment underscore id. Atribuis-lhe um tipo de coluna TimeUUID, e também passas primary underscore key equals True. É aqui que a coisa fica interessante. No cqlengine, qualquer primary key definida depois da partition key torna-se automaticamente numa clustering key. Não precisas de um bloco especial de configuração de clustering. A ordem literal de cima para baixo na qual defines os atributos das colunas dentro da tua classe de Python dita a estrutura da primary key no Cassandra. Depois de as primary keys estarem definidas, adicionas as colunas de dados reais. Defines um atributo body e atribuis-lhe um tipo de coluna Text. São apenas dados normais, por isso não são necessários argumentos de primary key. Agora tens uma classe declarativa de Python que descreve o schema da tua tabela. Mas a tabela ainda não existe na tua base de dados. Para fazeres push deste schema para o Cassandra, usas uma função chamada sync underscore table. Passas a tua classe do model Comment diretamente para esta função. O mapper lê a estrutura da tua classe, traduz isso para a raw CQL statement correta, e executa-a. Se a tabela não existir, o sync underscore table cria-a. Se a tabela já existir, ele verifica se adicionaste novas colunas à tua classe de Python e altera a tabela para corresponder. É importante saber que o sync underscore table nunca vai fazer drop de dados nem alterar as primary keys existentes, mantendo a tua estrutura de dados core segura durante os updates. O verdadeiro poder de definir models desta forma não é apenas esconder a sintaxe da base de dados, mas sim fixar a definição do teu schema diretamente na application layer, onde vive a business logic. É tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!
11

Fazer Queries com o cqlengine

3m 55s

Recupere e filtre dados de forma fluente usando objetos QuerySet no Object Mapper cqlengine. Abordamos operadores de filtragem, imutabilidade e limitações na ordenação.

Download
Olá, daqui é o Alex do DEV STORIES DOT EU. Apache Cassandra com Python, episódio 11 de 12. Filtrar registos de uma base de dados com um object mapper normalmente parece não custar nada, permitindo-te pesquisar por qualquer campo que queiras. Mas no Cassandra, se tentares filtrar por uma coluna que não esteja explicitamente indexada ou que não faça parte de uma primary key, a base de dados vai simplesmente recusar-se a correr a tua query. A solução é perceber como construir requests válidos, o que nos leva a Fazer Queries com cqlengine. No cqlengine, quando queres ir buscar dados a um model, interages com um QuerySet. Se tiveres um model a representar uma tabela da base de dados, podes aceder aos registos chamando o atributo objects seguido do método all. Isto devolve um QuerySet que representa cada linha dessa tabela. Puxar todas as linhas raramente é o que queres numa base de dados distribuída, por isso precisas de uma forma de restringir os resultados. Para restringir os dados devolvidos, usas o método filter. Um ponto comum de confusão aqui é como esta filtragem realmente acontece. Developers que vêm de outras frameworks às vezes assumem que o object mapper puxa todos os dados para o cliente Python e filtra a lista em memória. Isso está completamente incorreto. O método filter mapeia diretamente para uma cláusula WHERE estrita de CQL. Isto significa que as colunas que passas para o método filter têm de estar em conformidade com as regras de query do Cassandra. Só podes filtrar por partition keys, clustering columns, ou colunas com um secondary index. Se tentares filtrar por um campo de texto standard, não indexado, o cqlengine não vai esconder o erro nem processá-lo localmente. Ele vai passar a query diretamente para o Cassandra, e o Cassandra vai rejeitá-la. Vamos ver um cenário concreto. Tens um model Automobile e queres encontrar todos os carros da Tesla fabricados depois do ano 2012. Começas por chamar objects ponto filter. Passas o keyword argument manufacturer definido para a string Tesla. Para lidar com a condição do ano, o cqlengine fornece operadores de filtragem especiais. Aplicas isto adicionando um double underscore e a abreviatura do operador diretamente ao nome da coluna. Para estritamente maior que, usas double underscore g t. Então adicionas um segundo keyword argument à tua chamada do filter: year double underscore g t igual a 2012. O mapper traduz isto perfeitamente para uma query CQL válida. Existem vários outros operadores disponíveis para diferentes condições. Se quisesses verificar models de carros específicos em vez de um ano, podias usar o operador double underscore in. Passarias model double underscore in, definido para uma lista Python com os nomes Model S e Model 3. A base de dados vai devolver registos que correspondam a qualquer valor nessa lista. Aqui está o ponto chave. Os QuerySets são completamente imutáveis. Quando chamas o método filter num QuerySet existente, ele não modifica esse objeto in place. Em vez disso, devolve um QuerySet completamente novo com os filtros adicionais aplicados. Podes criar um QuerySet base que filtra apenas pelo manufacturer Tesla e atribuí-lo a uma variável. Depois, podes usar essa única variável para gerar vários QuerySets filtrados diferentes para anos ou models diferentes, apenas por chamar filter na variável base novamente. A query base original permanece completamente inalterada. Como os QuerySets são imutáveis, podes construir programaticamente queries altamente específicas e complexas, passo a passo, reutilizando com segurança as condições base em toda a tua aplicação antes sequer de qualquer network call ser feita. Obrigado por estares aí. Espero que tenhas aprendido algo novo.
12

Vector Search para IA

3m 53s

Prepare as suas competências para o futuro com o Vector Search do Cassandra 5.0. Descubra como armazenar e fazer queries a vetores de alta dimensão para impulsionar aplicações modernas de IA e machine learning.

Download
Olá, daqui fala o Alex da DEV STORIES DOT EU. Apache Cassandra com Python, episódio 12 de 12. Os Large Language Models estão a mudar a forma como construímos aplicações, mas trazem um grande desafio de armazenamento. Quando um modelo de inteligência artificial precisa de contexto, as queries tradicionais a bases de dados falham porque procuram correspondências exatas de caracteres, perdendo completamente a intenção real por trás de um prompt. Os workloads modernos de inteligência artificial exigem uma forma fundamentalmente diferente de fazer queries aos dados. É aí que entra a Vector Search, introduzida nativamente no Cassandra 5.0. É fácil confundir a vector search com uma simples pesquisa lexical full-text. Um index full-text standard, como o Lucene, é estritamente baseado em palavras-chave. Se um utilizador pesquisar na tua base de dados pela frase database backup, uma pesquisa lexical vai procurar por essas strings exatas. A vector search opera de forma diferente. Em vez de palavras-chave literais, os vetores capturam o significado semântico subjacente dos dados. Uma vector search entende que guardar um data snapshot ou arquivar tabelas está relacionado exatamente com o mesmo conceito de um database backup, mesmo que as palavras não partilhem nenhuma letra em comum. Para que isto funcione, o Cassandra utiliza vector embeddings. Um embedding é um array de números de floating-point gerado por um modelo de machine learning. Estes arrays atuam como coordenadas matemáticas que representam o significado mais profundo do teu texto. Guardas estes arrays diretamente no Cassandra, juntamente com os dados standard da tua aplicação. Quando precisas de encontrar conteúdo relevante em grandes coleções de documentos, fazes uma vector search. A base de dados compara o vetor da query recebida com os vetores guardados nas tuas tabelas. Calcula a distância matemática entre eles. Distâncias menores indicam maior similaridade semântica. Considera a criação de um chatbot interno para uma grande equipa de engenharia. Tens milhares de páginas de documentação técnica. Um engenheiro escreve uma pergunta sobre como desativar um staging cluster. Primeiro, a tua aplicação passa essa pergunta para um modelo de embedding, que traduz a frase num array de floats. De seguida, envias esse array numérico para o Cassandra como uma query de vector search. O Cassandra examina instantaneamente o espaço multidimensional e recupera os documentos internos cujos embeddings estão mais próximos da pergunta. Devolve o manual correto para desativar ambientes de teste porque o significado semântico alinha-se perfeitamente, independentemente da terminologia diferente. Esta funcionalidade foi criada especificamente para aplicações de inteligência artificial. A maioria destas aplicações depende da recuperação de contexto altamente relevante para alimentar o language model antes que este gere uma resposta. Ao integrar a vector search diretamente no Cassandra 5.0, eliminas a necessidade de correr uma vector database separada e independente. Manténs os teus registos operacionais principais e os seus embeddings semânticos exatamente na mesma arquitetura distribuída, contando com a alta disponibilidade e scaling que o Cassandra oferece. Como isto conclui a nossa série sobre Apache Cassandra, encorajo-te a explorares a documentação oficial e a experimentares gerar embeddings para os teus próprios dados na prática. Se tiveres sugestões sobre que tecnologias devemos abordar a seguir, visita devstories dot eu e avisa-nos. A vector search preenche a lacuna entre linguagem e armazenamento, transformando a complexa intenção humana num problema de geometria que o Cassandra pode resolver à escala. Isto é tudo por este episódio. Obrigado por ouvires, e continua a desenvolver!