Torna al catalogo
Season 17 12 Episodi 44 min 2026

Apache Cassandra with Python

Edizione 2026. Una serie di podcast tecnici che esplora l'architettura distribuita di Apache Cassandra e come interagirvi utilizzando il DataStax Python Driver. Copre il data modeling, gli Execution Profiles, le LWTs, le query async e il cqlengine Object Mapper.

Database Calcolo Distribuito
Apache Cassandra with Python
In Riproduzione
Click play to start
0:00
0:00
1
La visione d'insieme
Un'introduzione ad Apache Cassandra. Scopri perché le applicazioni su scala globale scelgono questo database NoSQL distribuito e in cosa differisce dai sistemi relazionali tradizionali.
3m 41s
2
Consistent Hashing e il Ring
Immergiti nell'architettura di Cassandra. Esploriamo il consistent hashing, il token ring e come i dati vengono partizionati su più nodi senza un server master.
3m 57s
3
Data Modeling Query-Driven
Dimentica tutto ciò che sai sui database relazionali. Scopri come il modeling query-driven di Cassandra richieda la denormalizzazione e la differenza cruciale tra partition keys e clustering keys.
3m 03s
4
Connettersi con Python
Inizia a usare il DataStax Python Driver. Impara a istanziare un Cluster, connetterti a una Session e stabilire la comunicazione con i tuoi nodi Cassandra.
3m 49s
5
Execution Profiles
Gestisci carichi di lavoro complessi senza problemi utilizzando gli Execution Profiles. Scopri come configurare load balancing, timeout e consistency levels per singola query senza inquinare la configurazione del tuo cluster.
3m 49s
6
Prepared Statements
Scopri come eseguire comandi CQL da Python. Trattiamo i simple statements e i vantaggi critici in termini di prestazioni derivanti dall'uso dei Prepared Statements per le query frequenti.
3m 04s
7
Paginazione di query di grandi dimensioni
Non far mai crashare la tua app caricando un set di dati enorme in memoria. Scopri come il driver Python impagina automaticamente i risultati delle query di grandi dimensioni e come gestire le fetch sizes.
3m 36s
8
Query async ad alto throughput
Massimizza il throughput della tua applicazione. Scopri come utilizzare execute_async, ResponseFutures e le callback per eseguire richieste concorrenti su Cassandra.
4m 14s
9
Lightweight Transactions
Implementa le operazioni compare-and-set in modo sicuro. Scopri come funzionano le Lightweight Transactions (LWTs) in Cassandra e come ispezionare la colonna speciale applied nei tuoi risultati Python.
3m 46s
10
I modelli dell'Object Mapper
Evita le stringhe CQL grezze e modella i tuoi dati usando classi Python. Scopri come utilizzare cqlengine per definire tabelle, specificare le primary keys e sincronizzare il tuo schema.
3m 54s
11
Fare query con cqlengine
Recupera e filtra i dati in modo fluido utilizzando gli oggetti QuerySet nel cqlengine Object Mapper. Trattiamo gli operatori di filtraggio, l'immutabilità e le limitazioni sull'ordinamento.
3m 59s
12
Vector Search per l'AI
Prepara le tue competenze per il futuro con la Vector Search di Cassandra 5.0. Scopri come archiviare e interrogare vettori ad alta dimensionalità per alimentare le moderne applicazioni di AI e machine learning.
3m 45s

Episodi

1

La visione d'insieme

3m 41s

Un'introduzione ad Apache Cassandra. Scopri perché le applicazioni su scala globale scelgono questo database NoSQL distribuito e in cosa differisce dai sistemi relazionali tradizionali.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 1 di 12. I database relazionali si scontrano con un muro quando provi a farne il deploy su più continenti. Finisci per combattere contro latenza, downtime, o un'architettura fragile in cui un singolo server guasto butta giù le tue operazioni di scrittura. Una scala globale massiccia richiede un paradigma di database completamente diverso. Quel paradigma è Apache Cassandra. È un database NoSQL distribuito e open-source, costruito per una scalabilità immensa. Quando gli ingegneri guardano Cassandra per la prima volta, spesso si portano dietro il bagaglio dei sistemi relazionali. Cercano il nodo primary che gestisce le scritture e le repliche read-only che lo seguono. Devi abbandonare immediatamente questo modello mentale. Cassandra non usa un design primary-replica. Opera interamente su un'architettura masterless e multi-primary. Ogni singolo nodo nel cluster è identico. Qualsiasi nodo può accettare una richiesta di lettura, e qualsiasi nodo può accettare una richiesta di scrittura. Per capire perché funziona in questo modo, guarda alla sua storia. Cassandra è stato originariamente sviluppato combinando due grandi innovazioni di ricerca. Primo, ha preso il design di rete completamente distribuito e masterless da Amazon Dynamo. Questo stabilisce come i nodi comunicano, si scoprono a vicenda e replicano i dati attraverso la rete. Secondo, ha adottato lo storage engine log-structured da Google Bigtable, che gestisce come i dati vengono fisicamente scritti su disco. Il risultato è un sistema ingegnerizzato specificamente per una disponibilità continua su più datacenter. Pensa a un social network globale. Hai utenti attivi simultaneamente a Tokyo, Londra e New York. Se un utente a Londra aggiorna il suo profilo, quell'operazione di scrittura deve avvenire all'istante. Fare il routing di quella richiesta attraverso l'Atlantico verso un singolo database centrale è troppo lento. Con Cassandra, l'utente scrive su un nodo locale nel datacenter di Londra. Quel nodo accetta la scrittura localmente e si prende subito la responsabilità di replicarla a Tokyo e New York in background. Ecco il punto chiave. Dato che ogni nodo agisce da primary, non c'è nessun single point of failure. Cassandra dispone i suoi nodi in un anello logico. Quando i dati entrano nel sistema, un hash matematico determina esattamente quali nodi nell'anello possiedono quello specifico pezzo di dato. Se un grave outage porta offline l'intero datacenter di Londra, il social network non va down. Tokyo e New York continuano ad accettare letture e scritture senza interruzioni. Quando Londra torna online, gli altri datacenter sincronizzano automaticamente i dati mancanti sui nodi recuperati. Ottieni una vera disponibilità globale con zero downtime. Questo design masterless significa anche che lo scaling è prevedibile e lineare. Se hai bisogno di più storage o più capacità di scrittura, ti basta inserire un altro nodo nell'anello. Il cluster rileva automaticamente il nuovo hardware e ridistribuisce i dati per bilanciare il carico tra le macchine attive. Cassandra ti costringe a scambiare la comodità delle query dei database tradizionali per qualcosa di molto più difficile da costruire su larga scala: la garanzia assoluta che, non importa quale hardware fallisca, il tuo database resta online e le tue operazioni vanno a buon fine. Se vuoi aiutare a supportare lo show, puoi cercare DevStoriesEU su Patreon. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
2

Consistent Hashing e il Ring

3m 57s

Immergiti nell'architettura di Cassandra. Esploriamo il consistent hashing, il token ring e come i dati vengono partizionati su più nodi senza un server master.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 2 di 12. L'hashing naive dei dati crolla completamente nel momento in cui aggiungi un nuovo server al cluster del tuo database. Quando il numero di server cambia, quasi ogni dato deve spostarsi in una nuova posizione. La soluzione a questo caos di scaling è il Consistent Hashing e il Ring. Un metodo standard per distribuire i dati su più server è il modulo hashing. Se hai otto nodi, prendi una partition key come uno user ID, ne calcoli l'hash e dividi il risultato per otto. Il resto ti dice a quale nodo va il profilo utente. Questo funziona perfettamente finché il tuo storage non si riempie e aggiungi un nono nodo. Ora dividi per nove. I resti cambiano. Quasi ogni profilo utente nel tuo sistema si ritrova improvvisamente su un server diverso, causando un'enorme tempesta di spostamenti di dati che mette in ginocchio il cluster. Cassandra evita tutto questo completamente. Usa il Consistent Hashing per distribuire i dati in modo prevedibile all'interno del cluster, senza affidarsi ad alcun coordinatore centrale. Invece di un semplice calcolo con modulo, Cassandra mappa sia i dati che i nodi in uno spazio circolare fisso e continuo chiamato token ring. Di default, Cassandra usa una funzione di hash che genera un range enorme di numeri possibili. Il valore di hash più basso possibile si ricollega direttamente a quello più alto, formando un cerchio chiuso. A ogni nodo fisico nel cluster viene assegnato un numero specifico, o token, in un punto qualsiasi di questo ring. Quando inserisci un profilo utente, Cassandra calcola l'hash dello user ID per produrre un token. Per scoprire quale nodo possiede questo profilo, il sistema individua il token del dato sul ring e si muove in senso orario. Il primo nodo che incontra è il proprietario. Ripensa al nostro cluster a otto nodi. Se aggiungiamo un nono nodo fisico, gli viene assegnato un singolo nuovo token sul ring, posizionandosi tra due nodi esistenti. Dato che la proprietà dei dati viene determinata muovendosi in senso orario, questo nuovo nono nodo prende in carico solo una porzione specifica di dati dal suo vicino immediato in senso orario. Gli altri sette nodi non fanno nulla. I loro dati rimangono completamente intatti. Ecco il punto chiave. Assegnare esattamente un token a un nodo fisico crea problemi operativi. È difficile bilanciare i dati perfettamente, e quando aggiungi un nuovo nodo, solo un server vicino è responsabile di passargli i dati. Quel singolo vicino viene martellato da un carico pesante. Per risolvere questo problema, Cassandra usa i virtual node, o vnode. Invece di dare a un server fisico un'unica enorme fetta contigua del ring, i vnode tagliano il ring in molti range più piccoli. A un singolo nodo fisico vengono assegnate centinaia di token diversi, distribuiti casualmente lungo il ring. Quando aggiungi quel nono nodo fisico usando i vnode, questo reclama molte piccole fette del ring da tutti i server esistenti in una volta sola. Ora, invece di avere un solo vicino che fa tutto il lavoro pesante, l'intero cluster condivide equamente il lavoro di streaming dei dati verso la nuova macchina. Il Consistent Hashing e i virtual node disaccoppiano il posizionamento dei dati dal numero grezzo di server fisici, permettendo a un cluster di scalare in modo fluido e di operare in modo prevedibile senza alcun master centrale che detti dove debbano andare i dati. Per questo episodio è tutto. Alla prossima!
3

Data Modeling Query-Driven

3m 03s

Dimentica tutto ciò che sai sui database relazionali. Scopri come il modeling query-driven di Cassandra richieda la denormalizzazione e la differenza cruciale tra partition keys e clustering keys.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 3 di 12. In un database relazionale, prima crei le tue tabelle, definisci le relazioni e poi scrivi le tue query. In Cassandra, se non conosci le tue query esatte prima di iniziare, il tuo database fallirà. Questo è il principio fondamentale del Query Driven Data Modeling. Molti sviluppatori si avvicinano a Cassandra con forti abitudini relazionali. Naturalmente cerchi modi per normalizzare i tuoi dati, impostare delle foreign key ed evitare le duplicazioni. Devi abbandonare completamente questa mentalità. Cassandra non supporta le join. Se provi a normalizzare i tuoi dati su più tabelle, finirai per eseguire delle join nel codice della tua applicazione, distruggendo i vantaggi in termini di performance per cui avevi scelto Cassandra fin dall'inizio. Cassandra richiede il Query Driven Data Modeling. Inizi mappando le domande esatte che la tua applicazione deve fare al database. In questo modello, in genere una query equivale a una tabella. Se hai bisogno di accedere agli stessi dati in tre modi diversi, crei tre tabelle diverse che contengono gli stessi dati. Questo ci porta alla denormalizzazione. Duplicare i dati qui non è un errore; è la strategia fondamentale. Lo spazio su disco è economico, ma le letture distribuite su una rete sono costose. Scrivendo i dati insieme nella forma esatta della tua query di lettura, Cassandra può recuperarli in una singola operazione senza dover cercare nell'intero cluster. Per far funzionare tutto questo, devi capire la primary key. Non è solo un identificatore univoco. Controlla esattamente dove e come i tuoi dati vengono memorizzati su disco. La primary key ha due parti distinte: la partition key e la clustering key. La partition key determina quale nodo fisico nel tuo cluster contiene i dati. Tutte le righe che condividono la stessa partition key vengono memorizzate insieme sullo stesso nodo. La clustering key determina l'ordinamento di quelle righe sul disco all'interno di quella specifica partizione. Prendi lo scenario della pubblicazione di riviste dalla documentazione. Supponi che la tua applicazione debba recuperare tutte le riviste pubblicate da uno specifico editore. La tua partition key deve essere il nome dell'editore. Quando arriva la query, Cassandra fa l'hashing del nome dell'editore, identifica il nodo esatto che contiene i dati di quell'editore e ci va direttamente. Per organizzare i risultati in modo naturale, potresti usare la data di pubblicazione come tua clustering key. Ora, non solo tutte le riviste di quell'editore sono raggruppate su un unico nodo, ma sono fisicamente memorizzate in ordine cronologico. Il database restituisce semplicemente i dati pre-ordinati in streaming. Ecco il punto chiave. Stai scambiando la complessità di scrittura con un'enorme velocità di lettura. Scrivi gli stessi dati in più tabelle per soddisfare diverse view dell'applicazione, ma quando un utente richiede quei dati, vengono restituiti in millisecondi perché il database fa zero calcoli per assemblarli. Grazie per l'ascolto. Statemi bene, tutti.
4

Connettersi con Python

3m 49s

Inizia a usare il DataStax Python Driver. Impara a istanziare un Cluster, connetterti a una Session e stabilire la comunicazione con i tuoi nodi Cassandra.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 4 di 12. Quando ti connetti a un database tradizionale, punti la tua applicazione verso un singolo indirizzo host. Quando ti connetti a un database distribuito, potresti pensare di dover tracciare manualmente decine di indirizzi server nei tuoi file di configurazione. Non è così, perché il driver del tuo database in realtà agisce da smart router. Oggi vediamo come connetterci con Python. Immagina un microservice Python in fase di avvio. Deve stabilire una comunicazione con un cluster Cassandra locale a tre nodi. Per iniziare, importi la classe Cluster dal modulo cassandra punto cluster. Inizializzi questa classe passandole una lista di indirizzi IP, noti come contact points. Se i tuoi nodi usano una porta non standard, puoi anche specificare un argomento port qui; altrimenti, il default è 9042. Spesso gli sviluppatori confondono questo passaggio con la creazione di una connection string standard a singolo DSN, dove devi elencare esplicitamente l'esatto nodo con cui vuoi parlare. Con Cassandra, non hai bisogno di elencare ogni singolo nodo della tua infrastruttura. Se hai un enorme cluster da trenta nodi, passare solo due o tre indirizzi IP come contact points va benissimo. Ecco il punto chiave. Quando il driver Python si avvia, contatta uno di questi contact points per fare il bootstrap. Interroga le tabelle di sistema per scaricare la topologia attuale del cluster. Facendo così, scopre automaticamente gli indirizzi IP del resto dei nodi. Il driver mantiene dinamicamente questa mappa di rete in background. Se in seguito fai scale out e aggiungi nodi, il driver rileva il cambiamento e adatta il suo routing automaticamente, senza richiedere un riavvio dell'applicazione. Una volta istanziato il tuo oggetto Cluster con quei contact points iniziali, chiami il suo metodo connect. Questa azione restituisce un oggetto Session. La Session gestisce l'effettivo connection pooling verso i nodi che ha appena scoperto. Quando chiami connect, puoi opzionalmente passare il nome di un keyspace. Un keyspace funziona da namespace per i tuoi dati. Se fornito, il driver lo imposta come default per tutte le operazioni future su quella Session. Dato che la Session gestisce connection pools complessi dietro le quinte, è progettata per essere long-lived e thread-safe. In genere, crei una singola Session all'avvio dell'applicazione e la riutilizzi. Ora, la seconda parte riguarda la connessione a un servizio cloud gestito come DataStax Astra. Astra funziona in modo diverso e non espone indirizzi IP raw per i contact points. Invece, scarichi un secure connect bundle. Si tratta di un file zip contenente i certificati necessari e i dettagli per la connessione mutual TLS. Nel tuo codice Python, salti la lista di indirizzi IP. Invece, passi un dizionario di configurazione cloud all'oggetto Cluster. Questo dizionario contiene una chiave chiamata secure connect bundle, che punta al file path locale del tuo file zip. Combini questo con un oggetto plaintext authentication provider configurato con il tuo client ID e secret. Chiamare connect restituisce quindi un oggetto Session standard, che funziona esattamente come il setup del cluster locale. Il concetto fondamentale è che, sia che tu passi alcuni indirizzi IP locali o un secure connect bundle per il cloud, il driver Python prende il tuo entry point iniziale, mappa la rete del database distribuito e astrae completamente la logica di routing dal codice della tua applicazione. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
5

Execution Profiles

3m 49s

Gestisci carichi di lavoro complessi senza problemi utilizzando gli Execution Profiles. Scopri come configurare load balancing, timeout e consistency levels per singola query senza inquinare la configurazione del tuo cluster.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 5 di 12. Man mano che la tua applicazione cresce, applicare un timeout o un consistency level universale a tutte le tue query al database diventa un enorme collo di bottiglia operativo. Finisci per far fallire prematuramente i background job o per bloccare la tua user interface in attesa di una heavy read. Gli Execution Profile sono il meccanismo che risolve questa tensione. Molti sviluppatori confondono la execution configuration con il setup legacy a livello di cluster. Nei vecchi paradigmi, definivi i parametri globali direttamente sull'oggetto cluster, il che significava che ogni query condivideva l'esatto stesso timeout. Gli Execution Profile sostituiscono questo pattern rigido. Ti permettono di mantenere simultaneamente più configurazioni distinte all'interno di una singola session attiva. Considera un'applicazione web multi-tenant. La tua interfaccia front-end richiede un timeout rigoroso di un secondo per garantire che le pagine web restino reattive. Nel frattempo, i tuoi task di reporting in background hanno bisogno di trenta secondi per aggregare in modo sicuro partizioni di grandi dimensioni. Un Execution Profile è un bundle autonomo e con un nome di impostazioni di richiesta, creato su misura proprio per questi diversi workload. Per creare tutto questo, inizi creando delle istanze di un oggetto Execution Profile. Per il task di reporting, istanzi un profilo e imposti il parametro request timeout a trenta secondi. Puoi spingerti oltre e associare a questo oggetto una specifica load balancing policy, magari instradando quelle heavy read analitiche esclusivamente verso un data center dedicato all'analytics. Poi, crei un secondo oggetto profilo distinto per la tua user interface, assegnandogli un timeout di un secondo e una load balancing policy locale. Puoi anche includere diverse retry policy o consistency level in questi profili, a seconda di cosa richiede la query. Ecco il punto chiave. Registri questi profili una sola volta, durante il setup iniziale del cluster, anziché costruirli durante la query execution vera e propria. Quando istanzi il cluster, passi un dictionary all'argomento execution profiles. Questo dictionary mappa dei semplici nomi string agli oggetti profilo che hai appena creato. Il driver gestisce queste configurazioni dietro le quinte. C'è sempre un profilo di fallback built-in, che puoi sovrascrivere mappando una configurazione a una costante specifica chiamata execution profile default. Se esegui una query senza nominare esplicitamente un profilo, il driver applica automaticamente queste impostazioni di default. Quando hai effettivamente bisogno di lanciare una query, usi i metodi standard della session execute o execute async. Insieme alla tua query string o al prepared statement, passi un argomento execution profile usando il semplice nome string che hai registrato in precedenza. Il driver intercetta questo nome, recupera le impostazioni del bundle ad esso collegate e le applica a quella specifica richiesta. La query della tua user interface è rigorosamente limitata a un secondo, e il background job gira comodamente per trenta secondi. Entrambe le query vengono eseguite contemporaneamente, in multiplexing sull'esatta stessa session e sull'esatto stesso connection pool. Spostare la tua configurazione negli Execution Profile disaccoppia il comportamento della tua query dalla tua connessione fisica al database. Questo permette a una singola applicazione di modellare dinamicamente il suo traffico database in base alle esigenze specifiche di ciascuna funzione, senza mai dover stabilire session separate. Grazie per avermi fatto compagnia. Spero tu abbia imparato qualcosa di nuovo.
6

Prepared Statements

3m 04s

Scopri come eseguire comandi CQL da Python. Trattiamo i simple statements e i vantaggi critici in termini di prestazioni derivanti dall'uso dei Prepared Statements per le query frequenti.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 6 di 12. Se utilizzi il semplice string formatting per le tue query ad alto throughput, stai costringendo il tuo database a bruciare preziosi cicli di CPU per fare il re-parsing dell'esatta stessa struttura migliaia di volte al secondo. Il modo per fermare questo spreco, e migliorare enormemente le performance della tua applicazione, è utilizzare i Prepared Statement. Quando ti connetti a Cassandra, il modo più diretto per interagire con il database è passare una string al metodo session dot execute. Gli passi una query e ti restituisce un result set. Di default, ogni riga in quel risultato torna indietro come una namedtuple di Python. Questo significa che puoi accedere ai valori delle tue colonne usando la semplice dot notation, come row dot name o row dot age. È pulito e funziona bene per operazioni una tantum. Ma inviare una raw string è altamente inefficiente per le query che esegui costantemente. Potresti pensare di star già facendo le cose nel modo giusto se utilizzi i positional parameters. Se passi una query string che contiene dei marcatori percent-s insieme a una sequenza di valori, il driver Python formatta quella query in modo sicuro. Questo previene gli injection attacks, ma a livello architetturale non cambia nulla per le performance. Stai comunque inviando l'intera query string sul network a Cassandra ogni singola volta. Cassandra deve comunque ricevere il testo, fare il parsing della sintassi e calcolare il query plan da zero. Ecco il punto chiave. Non hai bisogno di fare il parsing della stessa struttura due volte. È qui che entra in gioco il metodo session dot prepare. Invece di eseguire la query immediatamente, passi la tua query string al metodo prepare. In questa string, sostituisci i valori dinamici con dei punti interrogativi. Il driver invia questo template a Cassandra. Cassandra ne fa il parsing, lo convalida, calcola l'execution plan più efficiente e poi genera un identificatore univoco per questo specifico statement. Rimanda questo ID alla tua applicazione Python. Da quel momento in poi, ogni volta che hai bisogno di eseguire quella query, la tua applicazione non invia una string. Fa il bind delle tue variabili specifiche all'oggetto prepared statement e invia solo l'ID univoco insieme ai raw bytes dei valori. Pensa a un servizio di autenticazione ad alto traffico. Devi cercare un utente tramite il suo ID durante il login. La query è select star from users where user id equals question mark. Se il tuo servizio gestisce diecimila login al secondo, inviare l'intera query string diecimila volte spreca network bandwidth e CPU del database. Preparando lo statement una sola volta all'avvio della tua applicazione, riduci significativamente il network payload. Cassandra vede l'ID, tira fuori all'istante l'execution plan precalcolato e recupera i dati immediatamente. I prepared statement spostano il lavoro pesante del query parsing da una tassa ricorrente per ogni richiesta a un singolo costo di setup una tantum. Per questo episodio è tutto. Alla prossima!
7

Paginazione di query di grandi dimensioni

3m 36s

Non far mai crashare la tua app caricando un set di dati enorme in memoria. Scopri come il driver Python impagina automaticamente i risultati delle query di grandi dimensioni e come gestire le fetch sizes.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 7 di 12. Esegui una semplice select query su una tabella enorme, e all'improvviso la tua applicazione va in crash con un errore di out-of-memory. O almeno, così sarebbe se il driver del database provasse a caricare tutto in una volta. Invece, il driver gestisce la cosa in modo elegante in background. Oggi parliamo del paging di query di grandi dimensioni. Quando esegui una query usando il driver Python di Cassandra, questo non prova a caricare milioni di righe nella memoria della tua applicazione. Di default, il driver fa il paging automatico dei risultati, recuperando esattamente 5000 righe alla volta. Il result set che ti viene restituito si comporta come uno standard iterator di Python. Mentre fai un loop sulle righe, il driver contatta il database in modo trasparente per recuperare il batch successivo, un attimo prima che tu le finisca. Il tuo codice sembra esattamente un loop standard, ma sotto il cofano, il driver garantisce la memory safety tenendo in memoria solo una singola pagina di dati in qualsiasi momento. Non sei bloccato sul default di 5000 righe. Puoi controllarlo impostando la proprietà fetch size sul tuo oggetto statement prima di passarlo al metodo execute. Se abbassi la fetch size a 100, il driver terrà meno dati in memoria, ma farà dei network round trip più frequenti verso il database. Non confondere questo meccanismo con la tradizionale SQL pagination che usa i comandi limit e offset. La offset pagination di SQL forza il database a scansionare e scartare le righe prima di restituire i tuoi dati, il che diventa drasticamente più lento man mano che vai avanti con le pagine. Cassandra usa un approccio basato su cursor. Il driver usa un marker interno per tracciare l'esatta posizione fisica nel database in cui è finita l'ultima lettura. L'automatic paging è perfetto per il data processing in background, ma non funziona per costruire applicazioni web stateless. Immagina un web endpoint che fornisce una lista a scorrimento continuo di migliaia di audit log a una user interface. Non puoi tenere aperti sul tuo server una connessione al database e un iterator attivo mentre aspetti che l'utente faccia scroll. Ti serve un modo per fermare la query, chiudere la request e riprenderla più tardi. Ecco l'intuizione chiave. Il driver fornisce una proprietà sul result set chiamata paging state. Questa è una byte string opaca che rappresenta l'esatta posizione del cursor della tua query. Per costruire un'API stateless, esegui una query, prendi una singola pagina di audit log e recuperi questo paging state. Converti i byte in una semplice hex string e la mandi al tuo frontend insieme ai dati. Quando l'utente fa scroll fino in fondo all'interfaccia, il frontend rimanda esattamente quella hex string al tuo server. Il tuo backend decodifica la stringa e la passa al metodo execute come parametro paging state. Cassandra riprende all'istante la query proprio da dove si era interrotta. Usare il paging state ti permette di scollegare il ciclo di vita della memoria della tua applicazione dalle dimensioni enormi delle tabelle del tuo database, permettendoti di fare streaming di dati infiniti ai client usando una quantità di RAM del server strettamente finita. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
8

Query async ad alto throughput

4m 14s

Massimizza il throughput della tua applicazione. Scopri come utilizzare execute_async, ResponseFutures e le callback per eseguire richieste concorrenti su Cassandra.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 8 di 12. Aspettare una risposta dal database prima di inviare la tua richiesta successiva è il modo più semplice per limitare le prestazioni della tua applicazione. Se il tuo script Python rimane inattivo durante i round trip di rete, stai rinunciando a enormi performance di scrittura. Per risolvere questo problema, hai bisogno di query async ad alto throughput. Quando usi il metodo execute standard su una sessione Cassandra, il tuo codice si blocca. Invia la query, aspetta che il database la elabori e aspetta che la rete riporti indietro la risposta. Per una pipeline di data ingestion, questo tempo di inattività è fatale per il tuo throughput complessivo. Per sbloccare la vera velocità, il driver Python di DataStax fornisce il metodo execute async. Innanzitutto, dobbiamo chiarire un equivoco comune. Se senti la parola async in Python, probabilmente pensi al modulo asyncio della standard library e alle keyword async await. Ma non è questo il caso. Il driver Cassandra si basa su un suo event loop leggero in esecuzione in un background thread. Quando chiami execute async, stai usando il meccanismo custom del driver, non le funzionalità async integrate di Python. Quando passi una query a execute async, non aspetta una risposta dal database. Restituisce istantaneamente il controllo al tuo programma. Quello che ti restituisce è un oggetto chiamato ResponseFuture. Questo oggetto è una promise. Rappresenta un'operazione sul database che è stata inviata al cluster ma non è ancora finita. Dato che il tuo main thread non è più in attesa, ti serve un modo per sapere quando la query viene effettivamente completata o se fallisce. Gestisci questa cosa attaccando delle funzioni di callback direttamente al ResponseFuture. Per prima cosa, definisci una funzione di successo che prende il result set come argomento. Poi, definisci una funzione di errore che prende un'exception come argomento. Infine, colleghi entrambe queste funzioni alla future usando un metodo add callbacks. Quando il database risponde, il background thread riceve il pacchetto di rete e richiama automaticamente la funzione corretta. Ecco il punto chiave. Questo meccanismo ti permette di mettere in pipeline centinaia di operazioni simultaneamente. Pensa a una pipeline IoT che riceve centinaia di letture di temperatura al secondo. Usando il metodo sincrono, elabori una lettura, aspetti il database e poi elabori la successiva. Usando execute async, cicli attraverso le tue letture in arrivo senza fermarti. Per ogni lettura, lanci una query di insert, prendi il ResponseFuture, attacchi le tue callback di successo e di errore, e passi immediatamente alla lettura successiva. Spingi centinaia di richieste sulla rete in una frazione di secondo. Il driver multiplexa queste query sulle tue connessioni al database esistenti. Il cluster Cassandra le gestisce in parallelo e ti restituisce i risultati in stream. Le tue callback di successo e di errore scattano man mano che arrivano le risposte, in modo completamente indipendente dall'ordine in cui le hai inviate. Questo approccio aumenta drasticamente il tuo throughput perché sovrappone il tempo di attesa della rete per tutte quelle query. Sei limitato solo dalla tua bandwidth di rete e dalla capacità del database, invece di essere limitato dal thread blocking. Devi tenere traccia di quante richieste hai in volo per non sovraccaricare la coda del driver, ma il principio di base è mantenere la pipeline di rete piena. Il concetto più importante da portarsi a casa qui è che il throughput del database consiste nel minimizzare il tempo di inattività, e utilizzando pesantemente execute async con le callback attaccate, costringi la tua applicazione a passare il suo tempo a spingere dati invece di aspettare la rete. Se hai trovato utile tutto questo e vuoi supportare lo show, puoi cercare DevStoriesEU su Patreon. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
9

Lightweight Transactions

3m 46s

Implementa le operazioni compare-and-set in modo sicuro. Scopri come funzionano le Lightweight Transactions (LWTs) in Cassandra e come ispezionare la colonna speciale applied nei tuoi risultati Python.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 9 di 12. In un sistema distribuito senza lock globali, due utenti provano a registrare l'esatto stesso username nello stesso identico millisecondo. Come fai a garantire che solo uno dei due riesca effettivamente a ottenerlo? Il meccanismo che risolve questo problema si chiama Lightweight Transaction. Prima di spiegare come implementarle, dobbiamo chiarire il nome. Una Lightweight Transaction in Cassandra non è una tradizionale transazione ACID multi-tabella. Non puoi aprire un blocco di transazione, scrivere su tre tabelle diverse e fare un rollback di tutto se un passaggio fallisce. Una Lightweight Transaction ha uno scope strettamente limitato a una singola partition. È un'operazione condizionale, essenzialmente un compare and set distribuito. Per registrare un username univoco senza race condition, scrivi una query di insert standard, ma aggiungi la clausola specifica IF NOT EXISTS alla fine. Cassandra controllerà nel cluster se quella partition key esiste già. Se è assente, la scrittura va a buon fine. Per un'operazione di update, usi la clausola IF seguita dal nome di una colonna e da un valore atteso. Puoi istruire il database per aggiornare lo stato di un account solo se lo stato corrente corrisponde a una specifica string. Eseguire queste query da Python richiede di gestire la response in modo diverso rispetto a una normale scrittura. Una scrittura standard in Cassandra o ha successo silenziosamente o lancia un errore di timeout. Ma quando aggiungi una clausola IF, il database deve dire alla tua applicazione se la condizione è stata effettivamente soddisfatta. Il driver Python gestisce questa cosa restituendo un result set specializzato. Quando esegui una Lightweight Transaction, la prima row dei dati restituiti contiene una speciale colonna di sistema boolean. Il driver espone questa colonna con il nome applied, racchiuso tra parentesi quadre. Esegui la tua query di insert e fai il fetch della prima row dal risultato. A quel punto valuti quella colonna parentesi quadra applied parentesi quadra. Se stai restituendo delle dictionary row dal driver, accedi alla chiave come string. Se il valore è true, la condizione è stata soddisfatta e il tuo nuovo utente ha ottenuto con successo l'username. L'operazione è completa. Ecco il punto chiave. Devi capire esattamente cosa succede quando quella colonna parentesi quadra applied parentesi quadra restituisce false. Se l'insert fallisce perché l'username è già preso, Cassandra non si limita a restituire un semplice flag di rifiuto. Il driver popola la result row con i dati effettivi che hanno fatto fallire la tua condizione. Dato che la tua condizione è stata rifiutata, il database legge la row esistente e la restituisce alla tua applicazione Python nell'esatta stessa response. Ricevi il flag applied a false e, insieme ad esso, ricevi lo stato corrente del record in conflitto. Se stavi provando a fare l'update di un saldo basandoti su un saldo precedente atteso, ottieni indietro immediatamente il saldo corrente effettivo. Il tuo codice Python sa esattamente quali dati hanno bloccato la transazione, eliminando del tutto la necessità di eseguire una query di select successiva per scoprire perché la scrittura è stata rifiutata. Le Lightweight Transaction ti offrono un controllo rigoroso della concurrency a livello di partition, e il driver Python lo rende estremamente efficiente restituendoti gratis lo stato bloccante ogni volta che una condizione fallisce. Grazie per l'ascolto. Statemi bene, ciao a tutti.
10

I modelli dell'Object Mapper

3m 54s

Evita le stringhe CQL grezze e modella i tuoi dati usando classi Python. Scopri come utilizzare cqlengine per definire tabelle, specificare le primary keys e sincronizzare il tuo schema.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 10 di 12. Scrivere stringhe CQL raw all'interno del codice della tua applicazione Python può diventare rapidamente un disastro ingestibile. Ti ritrovi con stringhe multilinea piene di interpolazione di variabili, che facilitano i typo e rendono doloroso il refactoring dello schema. Hai bisogno di un modo per rappresentare le tabelle del tuo database come oggetti Python nativi. Questo è esattamente ciò che offrono i model dell'Object Mapper. Il driver Python di Datastax include un object mapper chiamato cqlengine. Ti permette di definire le tabelle Cassandra usando classi Python standard. Se hai mai usato l'ORM di Django o SQLAlchemy, la sintassi ti sembrerà molto familiare. Ma c'è una differenza fondamentale da chiarire subito. In quei mapper relazionali, dichiari delle foreign key per collegare le tabelle tra loro. Cassandra non è un database relazionale, quindi i binding relazionali qui semplicemente non esistono. Un model in cqlengine mappa esattamente una singola tabella Cassandra standalone. Creiamo un model Comment per un'applicazione fotografica. Vogliamo raggruppare tutti i commenti di una foto specifica e li vogliamo ordinati cronologicamente. Per prima cosa, crei una classe Python chiamata Comment che eredita dalla classe base Model di cqlengine. All'interno di questa classe, definisci le tue colonne come attributi di classe. Iniziamo con l'attributo photo underscore id. Lo assegni a un column type UUID e passi l'argomento primary underscore key uguale a True. Dato che questa è la primissima primary key che dichiari nella classe, cqlengine la assegna automaticamente come partition key. Successivamente, ci serve un modo per identificare e ordinare in modo univoco ogni commento all'interno di quella partition della foto. Definisci un secondo attributo chiamato comment underscore id. Lo assegni a un column type TimeUUID, e passi anche qui primary underscore key uguale a True. Ed è qui che la cosa si fa interessante. In cqlengine, qualsiasi primary key definita dopo la partition key diventa automaticamente una clustering key. Non hai bisogno di un blocco di configurazione speciale per il clustering. L'ordine letterale, dall'alto verso il basso, in cui definisci gli attributi delle colonne all'interno della tua classe Python detta la struttura della primary key in Cassandra. Dopo aver impostato le primary key, aggiungi le colonne di dati vere e proprie. Definisci un attributo body e gli assegni un column type Text. Si tratta di dati normali, quindi non servono argomenti per la primary key. Ora hai una classe Python dichiarativa che descrive lo schema della tua tabella. Ma la tabella non esiste ancora nel tuo database. Per pushare questo schema su Cassandra, usi una funzione chiamata sync underscore table. Passi la classe del tuo model Comment direttamente in questa funzione. Il mapper legge la struttura della tua classe, la traduce nel corretto statement CQL raw e lo esegue. Se la tabella non esiste, sync underscore table la crea. Se la tabella esiste già, controlla se hai aggiunto nuove colonne alla tua classe Python e altera la tabella per allinearla. È importante sapere che sync underscore table non farà mai il drop dei dati né altererà le primary key esistenti, mantenendo al sicuro la tua struttura dati principale durante gli update. Il vero potere di definire i model in questo modo non è solo nascondere la sintassi del database, ma bloccare la definizione del tuo schema direttamente nell'application layer, dove risiede la business logic. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
11

Fare query con cqlengine

3m 59s

Recupera e filtra i dati in modo fluido utilizzando gli oggetti QuerySet nel cqlengine Object Mapper. Trattiamo gli operatori di filtraggio, l'immutabilità e le limitazioni sull'ordinamento.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 11 di 12. Filtrare i record di un database con un object mapper di solito sembra facilissimo, e ti permette di cercare per qualsiasi campo tu voglia. Ma in Cassandra, se provi a filtrare su una colonna che non è esplicitamente indicizzata o che non fa parte di una primary key, il database si rifiuterà categoricamente di eseguire la tua query. La soluzione sta nel capire come costruire richieste valide, il che ci porta a Fare Query con cqlengine. In cqlengine, quando vuoi recuperare dati da un model, interagisci con un QuerySet. Se hai un model che rappresenta una tabella del database, puoi accedere ai record chiamando l'attributo objects seguito dal metodo all. Questo restituisce un QuerySet che rappresenta ogni riga di quella tabella. Recuperare ogni riga è raramente quello che vuoi in un database distribuito, quindi ti serve un modo per restringere i risultati. Per limitare i dati restituiti, usi il metodo filter. Un punto di confusione molto comune qui è come avviene effettivamente questo filtering. Gli sviluppatori che arrivano da altri framework a volte danno per scontato che l'object mapper tiri giù tutti i dati nel client Python e filtri la lista in memoria. Questo è completamente sbagliato. Il metodo filter mappa direttamente su una rigorosa clausola WHERE di CQL. Questo significa che le colonne che passi al metodo filter devono rispettare le regole delle query di Cassandra. Puoi filtrare solo su partition key, clustering column o colonne con un secondary index. Se provi a filtrare su un campo di testo standard non indicizzato, cqlengine non nasconderà l'errore né lo elaborerà localmente. Passerà la query direttamente a Cassandra, e Cassandra la rifiuterà. Vediamo uno scenario concreto. Hai un model Automobile e vuoi trovare tutte le auto prodotte da Tesla dopo l'anno 2012. Inizi chiamando objects punto filter. Passi il keyword argument manufacturer impostato sulla string Tesla. Per gestire la condizione sull'anno, cqlengine fornisce degli operatori di filtering speciali. Li applichi aggiungendo un doppio underscore e l'abbreviazione dell'operatore direttamente al nome della colonna. Per lo strettamente maggiore, usi doppio underscore g t. Quindi aggiungi un secondo keyword argument alla tua chiamata filter: year doppio underscore g t uguale 2012. Il mapper traduce tutto questo senza problemi in una query CQL valida. Ci sono diversi altri operatori disponibili per condizioni diverse. Se volessi cercare dei model di auto specifici invece di un anno, potresti usare l'operatore doppio underscore in. Passeresti model doppio underscore in, impostato su una lista Python contenente i nomi Model S e Model 3. Il database restituirà i record che corrispondono a qualsiasi valore in quella lista. Ecco il punto chiave. I QuerySet sono completamente immutabili. Quando chiami il metodo filter su un QuerySet esistente, non modifica quell'oggetto in place. Invece, restituisce un QuerySet nuovo di zecca con i filtri aggiuntivi applicati. Puoi creare un QuerySet di base che filtra solo per il manufacturer Tesla e assegnarlo a una variabile. Poi, puoi usare quella singola variabile per generare diversi QuerySet filtrati per anni o model differenti, semplicemente chiamando di nuovo filter sulla variabile di base. La query di base originale rimane completamente invariata. Dato che i QuerySet sono immutabili, puoi costruire programmaticamente query complesse e altamente specifiche passo dopo passo, riutilizzando in modo sicuro le condizioni di base in tutta la tua applicazione prima ancora che venga fatta qualsiasi chiamata di rete. Grazie per avermi fatto compagnia. Spero tu abbia imparato qualcosa di nuovo.
12

Vector Search per l'AI

3m 45s

Prepara le tue competenze per il futuro con la Vector Search di Cassandra 5.0. Scopri come archiviare e interrogare vettori ad alta dimensionalità per alimentare le moderne applicazioni di AI e machine learning.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Apache Cassandra con Python, episodio 12 di 12. I Large Language Models stanno cambiando il modo in cui sviluppiamo le applicazioni, ma portano con sé una grande sfida per lo storage. Quando un modello di intelligenza artificiale ha bisogno di contesto, le query sui database tradizionali falliscono perché cercano corrispondenze esatte tra i caratteri, perdendo completamente il vero intento dietro a un prompt. I moderni workload di intelligenza artificiale richiedono un modo fondamentalmente diverso di fare query sui dati. È qui che entra in gioco la Vector Search, introdotta nativamente in Cassandra 5.0. È facile confondere la Vector Search con una semplice ricerca lessicale full-text. Un index full-text standard, come Lucene, è strettamente basato sulle keyword. Se un utente cerca nel tuo database la frase database backup, una ricerca lessicale scansiona esattamente quelle stringhe. La Vector Search funziona in modo diverso. Invece delle keyword letterali, i vettori catturano il significato semantico alla base dei dati. Una Vector Search capisce che salvare un data snapshot o archiviare delle table si riferisce esattamente allo stesso concetto di un database backup, anche se le parole non hanno nemmeno una lettera in comune. Per far funzionare tutto questo, Cassandra si basa sui vector embedding. Un embedding è un array di float generato da un modello di machine learning. Questi array funzionano come coordinate matematiche che rappresentano il significato più profondo del tuo testo. Puoi salvare questi array direttamente in Cassandra, insieme ai dati standard della tua applicazione. Quando hai bisogno di trovare contenuti rilevanti all'interno di enormi collezioni di documenti, esegui una Vector Search. Il database confronta il vettore della query in entrata con i vettori salvati nelle tue table. Calcola la distanza matematica tra di loro. Distanze più brevi indicano una maggiore similarità semantica. Immagina di creare un chatbot interno per un grande team di engineering. Hai migliaia di pagine di documentazione tecnica. Un ingegnere digita una domanda chiedendo come decommissionare uno staging cluster. Per prima cosa, la tua applicazione passa quella domanda a un modello di embedding, che traduce la frase in un array di float. Successivamente, invii quell'array numerico a Cassandra come query di Vector Search. Cassandra scansiona all'istante lo spazio multidimensionale e recupera i documenti interni i cui embedding si trovano più vicini alla domanda. Restituisce il manuale corretto per dismettere gli ambienti di testing, perché il significato semantico si allinea perfettamente, a prescindere dalla terminologia diversa. Questa funzionalità è pensata esplicitamente per le applicazioni di intelligenza artificiale. La maggior parte di queste applicazioni si basa sul recupero di un contesto altamente rilevante da passare al language model prima che generi una risposta. Portando la Vector Search direttamente in Cassandra 5.0, elimini la necessità di far girare un vector database separato e standalone. Mantieni i tuoi record operativi primari e i loro embedding semantici esattamente nella stessa architettura distribuita, affidandoti all'alta disponibilità e allo scaling che offre Cassandra. Dato che questo conclude la nostra serie su Apache Cassandra, ti incoraggio a esplorare la documentazione ufficiale e a provare hands-on a generare embedding per i tuoi dati. Se hai suggerimenti su quali tecnologie dovremmo trattare in futuro, visita devstories dot eu e faccelo sapere. La Vector Search colma il divario tra linguaggio e storage, trasformando il complesso intento umano in un problema di geometria che Cassandra può risolvere su larga scala. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!