Înapoi la catalog
Season 17 12 Episoade 45 min 2026

Apache Cassandra with Python

Ediția 2026. O serie de podcasturi tehnice care explorează arhitectura distribuită a Apache Cassandra și modul de interacțiune cu aceasta folosind DataStax Python Driver. Acoperă modelarea datelor, Execution Profiles, LWTs, interogări asincrone și cqlengine Object Mapper.

Baze de date Calcul distribuit
Apache Cassandra with Python
Se redă acum
Click play to start
0:00
0:00
1
Imaginea de ansamblu
O introducere în Apache Cassandra. Află de ce aplicațiile la scară globală aleg această bază de date NoSQL distribuită și cum diferă de sistemele relaționale tradiționale.
4m 05s
2
Consistent Hashing și Inelul
Pătrunde în arhitectura Cassandra. Explorăm Consistent Hashing, inelul de tokenuri (token ring) și modul în care datele sunt partiționate pe mai multe noduri fără un server master.
3m 54s
3
Modelarea datelor orientată pe interogări
Uită tot ce știi despre bazele de date relaționale. Află cum modelarea orientată pe interogări din Cassandra necesită denormalizare și diferența crucială dintre partition keys și clustering keys.
3m 15s
4
Conectarea cu Python
Începe să folosești DataStax Python Driver. Învață cum să instanțiezi un Cluster, să te conectezi la o Session și să stabilești comunicarea cu nodurile tale Cassandra.
3m 53s
5
Execution Profiles
Gestionează fără probleme sarcinile de lucru complexe folosind Execution Profiles. Învață cum să configurezi load balancing, timeout-uri și niveluri de consistență (consistency levels) per interogare, fără a polua configurarea clusterului.
3m 44s
6
Prepared Statements
Învață cum să execuți comenzi CQL din Python. Acoperim instrucțiunile simple și beneficiile critice de performanță ale utilizării Prepared Statements pentru interogările frecvente.
3m 10s
7
Paginarea interogărilor mari
Nu-ți bloca niciodată aplicația încărcând un set masiv de date în memorie. Descoperă cum driverul Python paginează automat rezultatele interogărilor mari și cum să gestionezi dimensiunile de preluare (fetch sizes).
3m 19s
8
Interogări asincrone cu throughput ridicat
Maximizează throughput-ul aplicației tale. Învață cum să folosești execute_async, ResponseFutures și funcții callback pentru a rula cereri concurente către Cassandra.
4m 13s
9
Lightweight Transactions
Implementează în siguranță operațiunile compare-and-set. Învață cum funcționează Lightweight Transactions (LWTs) în Cassandra și cum să inspectezi coloana specializată applied în rezultatele tale din Python.
3m 48s
10
Modelele Object Mapper
Evită șirurile brute CQL și modelează-ți datele folosind clase Python. Învață cum să folosești cqlengine pentru a defini tabele, a specifica primary keys și a-ți sincroniza schema.
4m 03s
11
Efectuarea interogărilor cu cqlengine
Extrage și filtrează datele fluent folosind obiecte QuerySet în cqlengine Object Mapper. Acoperim operatorii de filtrare, imuabilitatea și limitările privind ordonarea.
4m 25s
12
Vector Search pentru AI
Pregătește-ți abilitățile pentru viitor cu Vector Search din Cassandra 5.0. Descoperă cum să stochezi și să interoghezi vectori multidimensionali pentru a susține aplicațiile moderne de AI și machine learning.
3m 38s

Episoade

1

Imaginea de ansamblu

4m 05s

O introducere în Apache Cassandra. Află de ce aplicațiile la scară globală aleg această bază de date NoSQL distribuită și cum diferă de sistemele relaționale tradiționale.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 1 din 12. Bazele de date relaționale se lovesc de un zid atunci când încerci să le deployezi pe mai multe continente. Ajungi să te lupți cu latența, cu downtime-ul sau cu o arhitectură fragilă în care un singur server care pică îți pune la pământ operațiunile de write. Scalarea la nivel global masiv necesită o paradigmă de baze de date complet diferită. Această paradigmă este Apache Cassandra. Este o bază de date NoSQL distribuită, open-source, construită pentru o scalare imensă. Când inginerii se uită pentru prima dată la Cassandra, vin adesea cu bagajul din sistemele relaționale. Caută nodul primary care gestionează write-urile și replicile read-only care îl urmează. Trebuie să renunți imediat la acest model mental. Cassandra nu folosește un design primary-replica. Funcționează în întregime pe o arhitectură masterless, multi-primary. Fiecare nod din cluster este identic. Orice nod poate accepta un request de read și orice nod poate accepta un request de write. Pentru a înțelege de ce funcționează așa, uită-te la istoricul său. Cassandra a fost dezvoltată inițial prin combinarea a două inovații majore de cercetare. În primul rând, a preluat designul de rețea complet distribuit, masterless, de la Amazon Dynamo. Acesta dictează modul în care nodurile comunică, se descoperă reciproc și replică datele în rețea. În al doilea rând, a adoptat storage engine-ul log-structured de la Google Bigtable, care gestionează modul în care datele sunt scrise fizic pe disc. Rezultatul este un sistem proiectat special pentru disponibilitate continuă în mai multe datacenter-uri. Gândește-te la o rețea socială globală. Ai utilizatori activi simultan în Tokyo, Londra și New York. Dacă un utilizator din Londra își actualizează profilul, acea operațiune de write trebuie să se întâmple instantaneu. Rutarea acelui request peste Atlantic către o singură bază de date centrală este prea lentă. Cu Cassandra, utilizatorul face write către un nod local din datacenter-ul din Londra. Nodul respectiv acceptă write-ul local și își asumă imediat responsabilitatea de a-l replica în Tokyo și New York în background. Iată ideea cheie. Pentru că fiecare nod acționează ca un primary, nu există un single point of failure. Cassandra își aranjează nodurile într-un inel logic. Când datele intră în sistem, un hash matematic determină exact care noduri din inel dețin acea anumită porțiune de date. Dacă un outage major scoate offline întregul datacenter din Londra, rețeaua socială nu pică. Tokyo și New York continuă să accepte read-uri și write-uri fără întrerupere. Când Londra revine online, celelalte datacenter-uri sincronizează automat datele lipsă pe nodurile recuperate. Obții o disponibilitate globală reală, cu zero downtime. Acest design masterless înseamnă, de asemenea, că scalarea este predictibilă și liniară. Dacă ai nevoie de mai mult storage sau de mai multă capacitate de write, pur și simplu adaugi încă un nod în inel. Clusterul detectează automat noul hardware și redistribuie datele pentru a echilibra load-ul pe mașinile active. Cassandra te obligă să dai confortul query-urilor tradiționale de baze de date pe ceva mult mai greu de construit la scară largă: garanția absolută că, indiferent ce hardware pică, baza ta de date rămâne online, iar operațiunile tale reușesc. Dacă vrei să susții podcastul, poți căuta DevStoriesEU pe Patreon. Asta e tot pentru acest episod. Mulțumesc că ai ascultat și continuă să construiești!
2

Consistent Hashing și Inelul

3m 54s

Pătrunde în arhitectura Cassandra. Explorăm Consistent Hashing, inelul de tokenuri (token ring) și modul în care datele sunt partiționate pe mai multe noduri fără un server master.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 2 din 12. Hashing-ul naiv al datelor se prăbușește complet în momentul în care adaugi un server nou în clusterul bazei de date. Când numărul de servere se schimbă, aproape fiecare bucată de date trebuie mutată într-o locație nouă. Soluția la acest haos de scalare este Consistent Hashing și The Ring. O metodă standard de a distribui datele pe mai multe servere este modulo hashing. Dacă ai opt noduri, iei un partition key, cum ar fi un user ID, îi faci hash și împarți rezultatul la opt. Restul îți spune care nod primește profilul de utilizator. Asta funcționează perfect până când ți se umple storage-ul și conectezi un al nouălea nod. Acum împarți la nouă. Resturile se schimbă. Aproape fiecare profil de utilizator din sistemul tău aparține brusc de un alt server, provocând o furtună masivă de mutare a datelor, care îți pune clusterul la pământ. Cassandra evită complet chestia asta. Folosește Consistent Hashing pentru a distribui predictibil datele în cluster, fără să se bazeze pe vreun coordonator central. În loc de un simplu calcul modulo, Cassandra mapează atât datele, cât și nodurile într-un spațiu circular fix și continuu, numit token ring. By default, Cassandra folosește o funcție de hash care generează un range masiv de numere posibile. Cea mai mică valoare de hash posibilă se conectează direct înapoi la cea mai mare, formând un cerc închis. Fiecărui nod fizic din cluster i se atribuie un număr specific, sau un token, undeva pe acest ring. Când inserezi un profil de utilizator, Cassandra face hash la user ID pentru a produce un token. Pentru a afla care nod deține acest profil, sistemul localizează tokenul de date pe ring și se mișcă în sensul acelor de ceasornic. Primul nod pe care îl întâlnește este ownerul. Gândește-te înapoi la clusterul nostru cu opt noduri. Dacă adăugăm un al nouălea nod fizic, acestuia i se atribuie un singur token nou pe ring, picând între două noduri existente. Pentru că data ownership-ul este determinat mergând în sensul acelor de ceasornic, acest al nouălea nod nou preia doar o anumită felie de date de la vecinul său imediat din sensul acelor de ceasornic. Celelalte șapte noduri nu fac nimic. Datele lor rămân complet neatinse. Aici e ideea cheie. Atribuirea unui singur token unui singur nod fizic creează probleme operaționale. Este dificil să echilibrezi perfect datele, iar atunci când adaugi un nod nou, un singur server vecin este responsabil să predea datele. Acel singur vecin este copleșit de load. Pentru a repara asta, Cassandra folosește virtual nodes, sau vnodes. În loc să îi dea unui server fizic o felie masivă și continuă din ring, vnodes taie ringul în mai multe range-uri mai mici. Unui singur nod fizic i se atribuie sute de tokenuri diferite, distribuite random pe tot ringul. Când adaugi acel al nouălea nod fizic folosind vnodes, el preia multe felii mici din ring de la toate serverele existente, în același timp. Acum, în loc ca un singur vecin să facă tot greul, întregul cluster împarte în mod egal munca de streaming de date către noua mașină. Consistent Hashing și virtual nodes decuplează plasarea datelor de numărul brut de servere fizice, permițând unui cluster să facă scale up fără probleme și să funcționeze predictibil, fără ca vreun master central să dicteze unde ar trebui să meargă datele. Asta e tot pentru acest episod. Ne auzim data viitoare!
3

Modelarea datelor orientată pe interogări

3m 15s

Uită tot ce știi despre bazele de date relaționale. Află cum modelarea orientată pe interogări din Cassandra necesită denormalizare și diferența crucială dintre partition keys și clustering keys.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 3 din 12. Într-o bază de date relațională, mai întâi construiești tabelele, definești relațiile și apoi scrii query-urile. În Cassandra, dacă nu știi exact care sunt query-urile înainte să începi, baza ta de date va eșua. Acesta este principiul de bază din Query Driven Data Modeling. Mulți developeri vin la Cassandra cu obiceiuri relaționale puternice. În mod natural, cauți metode să îți normalizezi datele, să setezi foreign keys și să eviți duplicarea. Trebuie să renunți complet la mentalitatea asta. Cassandra nu suportă joins. Dacă încerci să normalizezi datele în mai multe tabele, vei ajunge să faci joins în codul aplicației tale, ceea ce distruge fix beneficiile de performanță pentru care ai ales Cassandra de la bun început. Cassandra necesită o modelare a datelor query-driven. Începi prin a mapa întrebările exacte pe care aplicația ta trebuie să le pună bazei de date. În acest model, un query este de obicei egal cu un tabel. Dacă trebuie să accesezi aceleași date în trei moduri diferite, creezi trei tabele diferite care conțin aceleași date. Asta ne aduce la denormalizare. Duplicarea datelor nu este o greșeală aici; este strategia fundamentală. Spațiul pe disk este ieftin, dar read-urile distribuite într-o rețea sunt scumpe. Scriind datele împreună, în forma exactă a read query-ului tău, Cassandra le poate recupera într-o singură operațiune, fără să caute în întregul cluster. Ca asta să funcționeze, trebuie să înțelegi primary key-ul. Nu este doar un identificator unic. Controlează exact unde și cum sunt stocate datele tale pe disk. Primary key-ul are două părți distincte: partition key-ul și clustering key-ul. Partition key-ul dictează ce nod fizic din clusterul tău deține datele. Toate rândurile care împart același partition key sunt stocate împreună pe același nod. Clustering key-ul determină ordinea de sortare a acelor rânduri pe disk, în cadrul acelei partiții specifice. Ia scenariul cu publicarea revistelor din documentație. Să presupunem că aplicația ta trebuie să aducă toate revistele lansate de un anumit publisher. Partition key-ul tău trebuie să fie numele publisher-ului. Când ajunge query-ul, Cassandra face hash pe numele publisher-ului, identifică nodul exact care deține datele acelui publisher și merge direct la el. Ca să organizezi rezultatele natural, ai putea folosi data publicării drept clustering key. Acum, nu doar că toate revistele pentru acel publisher sunt grupate pe un singur nod, dar sunt stocate fizic în ordine cronologică. Baza de date pur și simplu face stream înapoi cu datele pre-sortate. Iată ideea cheie. Dai complexitatea de write pe o viteză masivă de read. Scrii aceleași date în mai multe tabele ca să satisfaci diferite view-uri ale aplicației, dar când un user cere datele respective, ele se întorc în milisecunde, pentru că baza de date face zero calcule ca să le asambleze. Mersi că m-ați ascultat. Aveți grijă de voi, tuturor!
4

Conectarea cu Python

3m 53s

Începe să folosești DataStax Python Driver. Învață cum să instanțiezi un Cluster, să te conectezi la o Session și să stabilești comunicarea cu nodurile tale Cassandra.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 4 din 12. Te conectezi la o bază de date tradițională și îți direcționezi aplicația către o singură adresă de host. Te conectezi la o bază de date distribuită și ai putea presupune că trebuie să gestionezi manual zeci de adrese de server în fișierele tale de configurare. Nu e cazul, pentru că driverul bazei de date acționează de fapt ca un smart router. Astăzi, ne uităm la conectarea cu Python. Imaginează-ți un microservice Python care pornește. Trebuie să stabilească comunicarea cu un cluster Cassandra local cu trei noduri. Pentru început, imporți clasa Cluster din modulul cassandra dot cluster. Inițializezi această clasă pasându-i o listă de adrese IP cunoscute sub numele de contact points. Dacă nodurile tale folosesc un port non-standard, poți specifica aici și un argument port; altfel, valoarea default este 9042. Developerii confundă adesea acest pas cu construirea unui connection string standard cu un singur DSN, unde trebuie să listezi explicit exact cu ce vrei să comunici. Cu Cassandra, nu trebuie să listezi fiecare nod din infrastructura ta. Dacă ai un cluster masiv de treizeci de noduri, să pasezi doar două sau trei adrese IP ca contact points este perfect în regulă. Iată ideea cheie. Când driverul Python pornește, contactează unul dintre acele contact points pentru a face bootstrap. Face un query pe tabelele de sistem pentru a descărca topologia curentă a clusterului. Făcând asta, descoperă automat adresele IP ale celorlalte noduri. Driverul menține dinamic această hartă a rețelei în background. Dacă faci scale out și adaugi noduri mai târziu, driverul detectează schimbarea și își ajustează automat routing-ul, fără să necesite un restart al aplicației. Odată ce instanțiezi obiectul Cluster cu acele contact points inițiale, apelezi metoda sa connect. Această acțiune returnează un obiect Session. Session gestionează partea de connection pooling către nodurile pe care tocmai le-a descoperit. Când apelezi connect, poți pasa opțional un nume de keyspace. Un keyspace funcționează ca un namespace pentru datele tale. Dacă este furnizat, driverul îl setează ca default pentru toate operațiunile viitoare pe acel Session. Pentru că Session gestionează connection pools complexe în spate, este conceput să fie long-lived și thread-safe. În mod normal, creezi un singur Session la startup-ul aplicației și îl refolosești. Acum, a doua parte a discuției este conectarea la un serviciu cloud managed, cum ar fi DataStax Astra. Astra funcționează diferit și nu expune adrese IP raw pentru contact points. În schimb, descarci un secure connect bundle. Acesta este un fișier zip care conține certificatele necesare și detaliile de conexiune mutual TLS. În codul tău Python, sari peste lista de adrese IP. În schimb, furnizezi un dictionary de configurare cloud obiectului Cluster. Acest dictionary conține o cheie numită secure connect bundle, care indică path-ul local al fișierului tău zip. Combini asta cu un obiect plaintext authentication provider configurat cu client ID-ul și secret-ul tău. Apelarea metodei connect produce apoi un obiect Session standard, care funcționează exact ca setup-ul unui cluster local. Ideea de bază este că, indiferent dacă pasezi câteva adrese IP locale sau un secure connect bundle de cloud, driverul Python preia entry point-ul tău inițial, mapează rețeaua bazei de date distribuite și abstractizează complet logica de routing din codul aplicației tale. Asta e tot pentru acest episod. Mulțumesc că ai ascultat și continuă să construiești!
5

Execution Profiles

3m 44s

Gestionează fără probleme sarcinile de lucru complexe folosind Execution Profiles. Învață cum să configurezi load balancing, timeout-uri și niveluri de consistență (consistency levels) per interogare, fără a polua configurarea clusterului.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 5 din 12. Pe măsură ce aplicația ta crește, aplicarea unui timeout sau a unui consistency level universal pentru toate query-urile bazei de date devine un bottleneck operațional masiv. Ajungi să îți pice prematur background jobs sau să blochezi user interface-ul în timp ce aștepți un heavy read. Execution Profiles sunt mecanismul care rezolvă această tensiune. Mulți developeri confundă execution configuration cu setup-ul legacy la nivel de cluster. În paradigmele mai vechi, defineai parametrii globali direct pe obiectul cluster, ceea ce înseamnă că fiecare query avea exact același timeout. Execution profiles înlocuiesc acest pattern rigid. Acestea îți permit să menții simultan mai multe configurații discrete într-un singur session activ. Ia în considerare o aplicație web multi-tenant. Interfața ta de front-end necesită un timeout strict de o secundă pentru a te asigura că paginile web rămân rapide. Între timp, task-urile tale de reporting din background au nevoie de treizeci de secunde pentru a agrega în siguranță partiții mari. Un execution profile este un bundle independent, cu nume, de request settings, adaptat precis pentru aceste workloads diferite. Pentru a construi asta, începi prin a crea instanțe ale unui obiect execution profile. Pentru task-ul de reporting, instanțiezi un profil și setezi parametrul de request timeout la treizeci de secunde. Poți merge mai departe și poți atașa o load balancing policy specifică acestui obiect, eventual rutând acele heavy analytical reads exclusiv către un data center dedicat de analytics. Apoi, creezi un al doilea obiect de profil distinct pentru user interface-ul tău, atribuindu-i un timeout de o secundă și o local load balancing policy. De asemenea, poți include retry policies distincte sau consistency levels în aceste profiluri, în funcție de ceea ce cere query-ul. Iată insight-ul cheie. Înregistrezi aceste profiluri o singură dată, în timpul setup-ului inițial al cluster-ului, în loc să le construiești în timpul execuției query-ului în sine. La instanțierea cluster-ului, pasezi un dictionary argumentului execution profiles. Acest dictionary mapează nume simple de tip string la obiectele de profil pe care tocmai le-ai creat. Driver-ul gestionează aceste configurații under the hood. Există întotdeauna un fallback profile built-in, căruia îi poți face override mapând o configurație la o constantă specifică numită execution profile default. Dacă execuți un query fără a denumi explicit un profil, driver-ul aplică automat aceste default settings. Când trebuie să rulezi efectiv un query, folosești metodele standard execute sau execute async ale session-ului. Pe lângă query string-ul sau prepared statement-ul tău, pasezi un argument execution profile folosind numele simplu de tip string pe care l-ai înregistrat anterior. Driver-ul interceptează acest nume, preia setările din bundle asociate cu el și le aplică acelui request specific. Query-ul de user interface este limitat strict la o secundă, iar background job-ul rulează confortabil timp de treizeci de secunde. Ambele query-uri rulează simultan, multiplexate peste exact același session și exact același connection pool. Mutarea configurației tale în execution profiles decuplează comportamentul query-ului de conexiunea fizică la baza de date. Asta permite unei singure aplicații să își modeleze dinamic traficul bazei de date, în funcție de nevoile specifice ale fiecărei funcții, fără a fi vreodată nevoie să stabilească sessions separate. Mersi că m-ai ascultat. Sper că ai învățat ceva nou.
6

Prepared Statements

3m 10s

Învață cum să execuți comenzi CQL din Python. Acoperim instrucțiunile simple și beneficiile critice de performanță ale utilizării Prepared Statements pentru interogările frecvente.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 6 din 12. Dacă folosești o formatare simplă de string-uri pentru query-urile tale cu throughput mare, îți forțezi baza de date să ardă cicluri prețioase de CPU, re-parsând exact aceeași structură de mii de ori pe secundă. Modul prin care poți opri această risipă și poți îmbunătăți considerabil performanța aplicației tale este folosind Prepared Statements. Când te conectezi la Cassandra, cel mai direct mod de a interacționa cu baza de date este să pasezi un string către metoda session dot execute. Îi dai un query, iar ea îți returnează un result set. By default, fiecare rând din acel rezultat este returnat ca un namedtuple de Python. Asta înseamnă că poți accesa valorile coloanelor folosind dot notation simplu, cum ar fi row dot name sau row dot age. Este clean și funcționează bine pentru operațiuni one-off. Dar trimiterea unui raw string este extrem de ineficientă pentru query-urile pe care le rulezi constant. S-ar putea să crezi că deja faci lucrurile corect dacă folosești parametri poziționali. Dacă pasezi un query string care conține markeri percent-s împreună cu o secvență de valori, driver-ul de Python formatează acel query în siguranță. Asta previne atacurile de tip injection, dar arhitectural, nu schimbă nimic la performanță. Tu tot trimiți query string-ul complet prin rețea către Cassandra de fiecare dată. Cassandra tot trebuie să primească textul, să parseze sintaxa și să calculeze query plan-ul de la zero. Aici este ideea cheie. Nu trebuie să parsezi aceeași structură de două ori. Aici intervine metoda session dot prepare. În loc să execuți query-ul imediat, pasezi query string-ul tău către metoda prepare. În acest string, înlocuiești valorile dinamice cu semne de întrebare. Driver-ul trimite acest template către Cassandra. Cassandra îl parsează, îl validează, calculează cel mai eficient execution plan, și apoi generează un identificator unic pentru acest statement specific. Trimite acest ID înapoi către aplicația ta de Python. Din acel moment, ori de câte ori trebuie să rulezi acel query, aplicația ta nu mai trimite un string. Face bind la variabilele tale specifice pe obiectul de tip prepared statement și trimite doar ID-ul unic împreună cu raw bytes ai valorilor. Gândește-te la un serviciu de autentificare cu trafic intens. Trebuie să cauți un utilizator după ID-ul său în timpul login-ului. Query-ul este select star from users where user id equals question mark. Dacă serviciul tău gestionează zece mii de login-uri pe secundă, trimiterea întregului query string de zece mii de ori irosește network bandwidth și CPU-ul bazei de date. Făcând prepare la statement o singură dată când pornește aplicația, reduci semnificativ network payload-ul. Cassandra vede ID-ul, extrage instantaneu execution plan-ul precalculat și preia datele imediat. Prepared statements mută greul de pe query parsing de la o taxă recurentă per request, la un singur cost de setup one-time. Asta e tot pentru acest episod. Ne auzim data viitoare!
7

Paginarea interogărilor mari

3m 19s

Nu-ți bloca niciodată aplicația încărcând un set masiv de date în memorie. Descoperă cum driverul Python paginează automat rezultatele interogărilor mari și cum să gestionezi dimensiunile de preluare (fetch sizes).

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 7 din 12. Execuți un simplu select query pe un tabel masiv și, dintr-o dată, aplicația ta crapă cu o eroare de out-of-memory. Sau cel puțin, așa s-ar întâmpla, dacă driverul bazei de date ar încerca să încarce totul deodată. În schimb, driverul gestionează asta elegant în background. Astăzi vorbim despre paginarea query-urilor mari. Când execuți un query folosind driverul de Python pentru Cassandra, acesta nu încearcă să tragă milioane de rânduri în memoria aplicației tale. By default, driverul paginează automat rezultatele, aducând exact 5000 de rânduri odată. Acel result set care îți este returnat se comportă ca un iterator standard de Python. Pe măsură ce iterezi prin rânduri, driverul contactează transparent baza de date pentru a face fetch la următorul batch, fix înainte să rămâi fără date. Codul tău arată exact ca un loop standard, dar în spate, driverul garantează memory safety ținând o singură pagină de date în memorie la un moment dat. Nu ești limitat la valoarea default de 5000 de rânduri. Poți controla asta setând proprietatea fetch size pe obiectul statement, înainte să-l pasezi metodei execute. Dacă reduci fetch size-ul la 100, driverul va ține mai puține date în memorie, dar va face mai multe network round trips către baza de date. Nu confunda acest mecanism cu paginarea SQL tradițională care folosește comenzile limit și offset. Paginarea SQL cu offset forțează baza de date să scaneze și să arunce rânduri înainte să-ți returneze datele, lucru care devine drastic mai lent cu cât mergi mai adânc în paginare. Cassandra folosește o abordare bazată pe cursor. Driverul folosește un marker intern pentru a urmări locația fizică exactă din baza de date unde s-a terminat ultima citire. Paginarea automată este perfectă pentru procesarea datelor în background, dar nu funcționează pentru construirea de aplicații web stateless. Gândește-te la un web endpoint care oferă o listă cu scroll continuu de mii de audit logs către un user interface. Nu poți ține o conexiune la baza de date și un iterator activ deschise pe server în timp ce aștepți ca userul să facă scroll. Ai nevoie de o modalitate prin care să oprești query-ul, să închizi request-ul și să-l reiei mai târziu. Aici e ideea de bază. Driverul oferă o proprietate pe result set numită paging state. Acesta este un byte string opac care reprezintă poziția exactă a cursorului pentru query-ul tău. Pentru a construi un API stateless, execuți un query, iei o singură pagină de audit logs și extragi acest paging state. Convertești acei bytes într-un plain hex string și îl trimiți către frontend împreună cu datele. Când userul face scroll până la finalul interfeței, frontend-ul trimite exact acel hex string înapoi către server. Backend-ul tău decodează string-ul și îl pasează în metoda execute ca parametru de paging state. Cassandra reia instantaneu query-ul exact de unde a rămas. Folosirea paging state-ului îți permite să deconectezi durata de viață a memoriei aplicației tale de la scara masivă a tabelelor din baza de date, permițându-ți să faci stream la date infinite către clienți folosind o cantitate strict finită de RAM pe server. Asta e tot pentru acest episod. Mulțumesc pentru audiție și continuă să construiești!
8

Interogări asincrone cu throughput ridicat

4m 13s

Maximizează throughput-ul aplicației tale. Învață cum să folosești execute_async, ResponseFutures și funcții callback pentru a rula cereri concurente către Cassandra.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 8 din 12. Așteptarea unui răspuns de la baza de date înainte de a trimite următorul request este cea mai simplă metodă de a face throttle propriei aplicații. Dacă scriptul tău Python stă degeaba în timpul acelor network round trips, pierzi o performanță masivă de scriere. Pentru a repara asta, ai nevoie de query-uri async cu throughput mare. Când folosești metoda standard execute pe o sesiune Cassandra, execuția codului tău se blochează. Trimite query-ul, așteaptă ca baza de date să îl proceseze și așteaptă ca rețeaua să aducă răspunsul înapoi. Pentru un pipeline de data ingestion, acest idle time este fatal pentru throughput-ul tău general. Pentru a debloca viteza reală, driverul de Python DataStax îți oferă metoda execute async. Mai întâi, trebuie să clarificăm o confuzie frecventă. Dacă auzi cuvântul async în Python, probabil te gândești la modulul asyncio din standard library și la keyword-urile async await. Aici nu este vorba despre asta. Driverul Cassandra se bazează pe propriul său event loop lightweight, care rulează într-un background thread. Când apelezi execute async, folosești mecanismul custom al driverului, nu funcționalitățile asincrone built-in din Python. Când pasezi un query către execute async, acesta nu așteaptă un răspuns de la baza de date. Returnează controlul programului tău instantaneu. Ceea ce îți dă înapoi este un obiect numit ResponseFuture. Acest obiect este un promise. Reprezintă o operațiune de database care a fost trimisă către cluster, dar care nu s-a terminat încă. Pentru că main thread-ul tău nu mai așteaptă, ai nevoie de o modalitate să știi când query-ul se finalizează efectiv sau dacă dă fail. Gestionezi asta atașând funcții de tip callback direct la ResponseFuture. Mai întâi, definești o funcție de success care ia result set-ul ca argument. Apoi, definești o funcție de error care ia o excepție ca argument. În cele din urmă, legi ambele funcții de future folosind o metodă add callbacks. Când baza de date răspunde, background thread-ul primește pachetul de rețea și declanșează automat funcția corectă. Aici este ideea de bază. Acest mecanism îți permite să faci pipeline la sute de operațiuni simultan. Gândește-te la un pipeline IoT care primește sute de citiri de temperatură pe secundă. Folosind metoda sincronă, procesezi o citire, aștepți după baza de date, și apoi o procesezi pe următoarea. Folosind execute async, faci loop prin citirile primite fără să te oprești. Pentru fiecare citire, lansezi un insert query, preiei ResponseFuture-ul, atașezi callback-urile de success și error, și treci imediat la următoarea citire. Împingi sute de request-uri în rețea într-o fracțiune de secundă. Driverul multiplexează aceste query-uri peste conexiunile tale existente la baza de date. Clusterul Cassandra le procesează în paralel și face stream la rezultate înapoi. Callback-urile tale de success și error se declanșează pe măsură ce sosesc răspunsurile, complet independent de ordinea în care le-ai trimis. Această abordare îți crește drastic throughput-ul, deoarece suprapune timpul de așteptare în rețea pentru toate acele query-uri. Ești limitat doar de lățimea de bandă a rețelei și de capacitatea bazei de date, în loc să fii limitat de thread blocking. Trebuie să ții evidența numărului de request-uri pe care le ai in flight, ca să nu copleșești coada driverului, dar principiul de bază este să menții acel network pipeline plin. Cea mai importantă concluzie de aici este că throughput-ul bazei de date înseamnă minimizarea acelui idle time, iar prin utilizarea intensă a lui execute async cu callback-uri atașate, îți forțezi aplicația să își petreacă timpul împingând date în loc să aștepte după rețea. Dacă ți s-a părut util și vrei să susții emisiunea, poți căuta DevStoriesEU pe Patreon. Asta e tot pentru acest episod. Mersi că m-ai ascultat, și continuă să construiești!
9

Lightweight Transactions

3m 48s

Implementează în siguranță operațiunile compare-and-set. Învață cum funcționează Lightweight Transactions (LWTs) în Cassandra și cum să inspectezi coloana specializată applied în rezultatele tale din Python.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 9 din 12. Într-un sistem distribuit fără global locks, doi utilizatori încearcă să înregistreze exact același username în exact aceeași milisecundă. Cum garantezi că doar unul dintre ei îl primește? Mecanismul care rezolvă asta se numește Lightweight Transaction. Înainte să explic cum se implementează, trebuie să clarificăm numele. Un Lightweight Transaction în Cassandra nu este o tranzacție ACID tradițională multi-table. Nu poți să deschizi un transaction block, să scrii în trei tabele diferite și să dai rollback la tot dacă un pas eșuează. Un Lightweight Transaction este strict limitat la o singură partiție. Este o operațiune condiționată, în esență un compare and set distribuit. Ca să rezervi un username unic fără race conditions, scrii un insert query standard, dar adaugi clauza specifică IF NOT EXISTS la final. Cassandra va verifica clusterul ca să vadă dacă acel partition key există deja. Dacă lipsește, scrierea se execută. Pentru o operațiune de update, folosești clauza IF urmată de numele unei coloane și o valoare așteptată. Poți instrui baza de date să facă update la statusul unui cont doar IF statusul curent se potrivește cu un anumit string. Executarea acestor queries din Python necesită gestionarea răspunsului diferit față de o scriere normală. O scriere standard în Cassandra fie reușește silențios, fie aruncă o eroare de timeout. Dar când adaugi o clauză IF, baza de date trebuie să spună aplicației tale dacă acea condiție a fost într-adevăr îndeplinită. Driverul de Python gestionează asta returnând un result set specializat. Când execuți un Lightweight Transaction, primul row din datele returnate conține o coloană de sistem booleană specială. Driverul expune această coloană sub numele applied, între paranteze pătrate. Execuți acel insert query și dai fetch la primul row din rezultat. Apoi evaluezi acea coloană bracket applied bracket. Dacă returnezi dictionary rows din driver, accesezi cheia ca un string. Dacă valoarea este evaluată ca true, condiția a fost îndeplinită, iar noul tău utilizator a rezervat cu succes acel username. Operațiunea este completă. Aici e partea esențială. Trebuie să înțelegi exact ce se întâmplă când acea coloană bracket applied bracket este evaluată ca false. Dacă insert-ul eșuează pentru că acel username este deja luat, Cassandra nu returnează doar un simplu rejection flag. Driverul populează acel result row cu datele reale care au făcut ca acea condiție să eșueze. Pentru că a fost respinsă condiția ta, baza de date citește acel row existent și îl dă înapoi aplicației tale Python în exact același response. Primești acel applied flag pe false, și alături de el, primești starea curentă a înregistrării conflictuale. Dacă încercai să faci update la un sold pe baza unui sold anterior așteptat, primești imediat înapoi soldul curent real. Codul tău Python știe exact ce date au blocat tranzacția, eliminând complet nevoia de a rula un select query ulterior ca să afli de ce a fost respinsă scrierea. Lightweight Transactions îți oferă un concurrency control strict la nivel de partiție, iar driverul de Python face totul extrem de eficient dându-ți gratuit acel blocking state ori de câte ori o condiție eșuează. Mulțumesc pentru ascultare. Aveți grijă de voi, tuturor.
10

Modelele Object Mapper

4m 03s

Evită șirurile brute CQL și modelează-ți datele folosind clase Python. Învață cum să folosești cqlengine pentru a defini tabele, a specifica primary keys și a-ți sincroniza schema.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 10 din 12. Scrierea de string-uri raw CQL direct în codul aplicației tale Python poate deveni rapid un mess imposibil de menținut. Ajungi să ai string-uri multi-line pline de variable interpolation, ceea ce face ca typo-urile să apară ușor, iar refactorizarea de schema să fie dureroasă. Ai nevoie de o modalitate de a-ți reprezenta tabelele din baza de date ca obiecte Python native. Exact asta oferă The Object Mapper Models. Driver-ul Datastax pentru Python include un object mapper numit cqlengine. Acesta îți permite să definești tabele Cassandra folosind clase Python standard. Dacă ai folosit vreodată Django ORM sau SQLAlchemy, sintaxa îți va părea foarte familiară. Dar există o diferență majoră pe care trebuie să o clarificăm de la bun început. În acele relational mappers, declari foreign keys pentru a lega tabelele între ele. Cassandra nu este o bază de date relațională, așa că legăturile relaționale pur și simplu nu există aici. Un model în cqlengine se mapează exact la o singură tabelă Cassandra independentă. Hai să construim un model Comment pentru o aplicație foto. Vrem să grupăm toate comentariile pentru o anumită fotografie și vrem să fie ordonate cronologic. Mai întâi, creezi o clasă Python numită Comment, care moștenește din clasa de bază Model din cqlengine. În interiorul acestei clase, îți definești coloanele ca atribute de clasă. Începem cu atributul photo underscore id. Îl atribui unui tip de coloană UUID și îi pasezi argumentul primary underscore key egal cu True. Pentru că acesta este primul primary key pe care îl declari în clasă, cqlengine îl atribuie automat ca partition key. Apoi, avem nevoie de o modalitate de a identifica și ordona în mod unic fiecare comentariu din cadrul acelei partiții a fotografiei. Definești un al doilea atribut numit comment underscore id. Pe acesta îl atribui unui tip de coloană TimeUUID și pasezi, de asemenea, primary underscore key egal cu True. Aici devine interesant. În cqlengine, orice primary key definit după partition key devine automat un clustering key. Nu ai nevoie de un bloc special de configurare pentru clustering. Ordinea literală, de sus în jos, în care definești atributele coloanelor în interiorul clasei tale Python dictează structura de primary key în Cassandra. După ce ai setat acele primary keys, adaugi coloanele de date propriu-zise. Definești un atribut body și îi atribui un tip de coloană Text. Sunt doar date obișnuite, deci nu sunt necesare argumente de primary key. Acum ai o clasă Python declarativă care descrie schema tabelei tale. Dar tabela nu există încă în baza ta de date. Pentru a face push la această schema în Cassandra, folosești o funcție numită sync underscore table. Pasezi clasa modelului Comment direct în această funcție. Mapper-ul citește structura clasei, o traduce în statement-ul raw CQL corect și o execută. Dacă tabela nu există, sync underscore table o creează. Dacă tabela există deja, verifică dacă ai adăugat coloane noi în clasa Python și modifică tabela pentru a se potrivi. Este important să știi că sync underscore table nu va da niciodată drop la date și nu va modifica acele primary keys existente, păstrându-ți structura de bază a datelor în siguranță în timpul update-urilor. Adevărata putere a definirii modelelor în acest fel nu constă doar în ascunderea sintaxei bazei de date, ci în blocarea definiției de schema direct în application layer, acolo unde trăiește business logic-ul. Asta e tot pentru acest episod. Mersi că m-ai ascultat și continuă să construiești!
11

Efectuarea interogărilor cu cqlengine

4m 25s

Extrage și filtrează datele fluent folosind obiecte QuerySet în cqlengine Object Mapper. Acoperim operatorii de filtrare, imuabilitatea și limitările privind ordonarea.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 11 din 12. Filtrarea înregistrărilor din baza de date cu un object mapper pare de obicei fără efort, lăsându-te să cauți după orice câmp vrei. Dar în Cassandra, dacă încerci să filtrezi pe o coloană care nu este indexată explicit sau nu face parte dintr-o primary key, baza de date va refuza categoric să ruleze query-ul. Soluția este să înțelegi cum să construiești request-uri valide, ceea ce ne aduce la Crearea de Query-uri cu cqlengine. În cqlengine, atunci când vrei să extragi date dintr-un model, interacționezi cu un QuerySet. Dacă ai un model care reprezintă un tabel din baza de date, poți accesa înregistrările apelând atributul objects urmat de metoda all. Asta returnează un QuerySet care reprezintă fiecare rând din acel tabel. Extragerea fiecărui rând este rareori ceea ce vrei într-o bază de date distribuită, așa că ai nevoie de o modalitate de a restrânge rezultatele. Pentru a restricționa datele returnate, folosești metoda filter. Un punct comun de confuzie aici este cum se întâmplă de fapt această filtrare. Developerii care vin din alte framework-uri presupun uneori că object mapper-ul trage toate datele în clientul Python și filtrează lista in memory. Asta este complet greșit. Metoda filter se mapează direct la o clauză CQL WHERE strictă. Asta înseamnă că acele coloane pe care le pasezi metodei filter trebuie să respecte regulile de query din Cassandra. Poți filtra doar pe partition keys, clustering columns sau coloane cu un secondary index. Dacă încerci să filtrezi pe un câmp text standard, neindexat, cqlengine nu va ascunde eroarea și nu o va procesa local. Va trimite query-ul direct către Cassandra, iar Cassandra îl va respinge. Să ne uităm la un scenariu concret. Ai un model Automobile și vrei să găsești toate mașinile făcute de Tesla, fabricate după anul 2012. Începi prin a apela objects dot filter. Pasezi keyword argument-ul manufacturer setat la string-ul Tesla. Pentru a gestiona condiția year, cqlengine oferă operatori speciali de filtrare. Îi aplici adăugând un double underscore și o abreviere a operatorului direct la numele coloanei. Pentru strict mai mare decât, folosești double underscore g t. Așa că adaugi un al doilea keyword argument la apelul tău filter: year double underscore g t equals 2012. Mapper-ul traduce asta fără probleme într-un query CQL valid. Există și alți câțiva operatori disponibili pentru diferite condiții. Dacă ai vrea să verifici anumite modele de mașini în loc de un an, ai putea folosi operatorul double underscore in. Ai pasa model double underscore in, setat la o listă Python care conține numele Model S și Model 3. Baza de date va returna înregistrările care se potrivesc cu orice valoare din acea listă. Iată ideea de bază. QuerySet-urile sunt complet imuabile. Când apelezi metoda filter pe un QuerySet existent, nu modifică acel obiect in place. În schimb, returnează un QuerySet complet nou cu filtrele adiționale aplicate. Poți crea un QuerySet de bază care filtrează doar după manufacturer-ul Tesla și să-l atribui unei variabile. Apoi, poți folosi acea singură variabilă pentru a genera mai multe QuerySet-uri filtrate diferit pentru ani sau modele diferite, doar apelând din nou filter pe variabila de bază. Query-ul de bază original rămâne complet neschimbat. Pentru că QuerySet-urile sunt imuabile, poți construi programatic query-uri complexe și foarte specifice pas cu pas, refolosind în siguranță condițiile de bază în toată aplicația ta înainte să se facă vreun network call. Mersi că ai stat cu mine. Sper că ai învățat ceva nou.
12

Vector Search pentru AI

3m 38s

Pregătește-ți abilitățile pentru viitor cu Vector Search din Cassandra 5.0. Descoperă cum să stochezi și să interoghezi vectori multidimensionali pentru a susține aplicațiile moderne de AI și machine learning.

Descarcă
Salut, sunt Alex de la DEV STORIES DOT EU. Apache Cassandra cu Python, episodul 12 din 12. Large Language Models schimbă modul în care construim aplicații, dar aduc o provocare majoră de storage. Atunci când un model de inteligență artificială are nevoie de context, query-urile tradiționale pe baza de date eșuează deoarece caută match-uri exacte de caractere, ratând complet intenția reală din spatele unui prompt. Workload-urile moderne de inteligență artificială necesită un mod fundamental diferit de a face query pe date. Aici intervine Vector Search, introdus nativ în Cassandra 5.0. E ușor să confunzi vector search cu un simplu full-text lexical search. Un full-text index standard, cum ar fi Lucene, este bazat strict pe keyword-uri. Dacă un utilizator caută în baza ta de date expresia database backup, un lexical search scanează exact acele string-uri. Vector search funcționează diferit. În loc de keyword-uri literale, vectorii capturează sensul semantic din spatele datelor. Un vector search înțelege că salvarea unui data snapshot sau arhivarea tabelelor se referă la exact același concept ca un database backup, chiar dacă cuvintele au zero litere în comun. Pentru ca asta să funcționeze, Cassandra se bazează pe vector embeddings. Un embedding este un array de numere floating-point generat de un model de machine learning. Aceste array-uri acționează drept coordonate matematice care reprezintă sensul mai profund al textului tău. Stochezi aceste array-uri direct în Cassandra, alături de datele standard ale aplicației tale. Când trebuie să găsești conținut relevant în colecții masive de documente, faci un vector search. Baza de date compară vectorul query-ului primit cu vectorii stocați în tabelele tale. Calculează distanța matematică dintre ei. Distanțele mai scurte indică o similaritate semantică mai puternică. Imaginează-ți că dezvolți un chatbot intern pentru o echipă mare de ingineri. Ai mii de pagini de documentație tehnică. Un inginer tastează o întrebare în care vrea să afle cum să facă decommission la un staging cluster. Mai întâi, aplicația ta pasează acea întrebare unui embedding model, care traduce propoziția într-un array de float-uri. Apoi, trimiți acel array numeric către Cassandra ca un vector search query. Cassandra scanează instantaneu spațiul multidimensional și preia documentele interne ale căror embedding-uri se află cel mai aproape de întrebare. Returnează manualul corect pentru scoaterea din uz a mediilor de testing, deoarece sensul semantic se aliniază perfect, indiferent de terminologia diferită. Această capacitate este construită explicit pentru aplicații de inteligență artificială. Majoritatea acestor aplicații se bazează pe extragerea unui context extrem de relevant pentru a-l trimite înapoi către language model înainte ca acesta să genereze un răspuns. Aducând vector search direct în Cassandra 5.0, elimini nevoia de a rula un vector database separat, standalone. Îți păstrezi înregistrările operaționale principale și embedding-urile lor semantice în exact aceeași arhitectură distribuită, bazându-te pe high availability-ul și scaling-ul pe care le oferă Cassandra. Pentru că asta încheie seria noastră despre Apache Cassandra, te încurajez să explorezi documentația oficială și să încerci să generezi embedding-uri pentru propriile tale date, hands-on. Dacă ai sugestii despre ce tehnologii ar trebui să acoperim în continuare, vizitează devstories dot eu și spune-ne. Vector search reduce decalajul dintre limbaj și storage, transformând intenția umană complexă într-o problemă de geometrie pe care Cassandra o poate rezolva at scale. Asta e tot pentru acest episod. Îți mulțumesc că ne-ai ascultat și continuă să construiești!