Cassandra jest magazynem klucz-wartość. Łączy ona rozproszone technologie Dynamo oraz model danych z Google BigTable. Podobnie ja Dynamo zakłada, iż po odpowiednio długim czasie bez nowych zmian wszystkie repliki bedą identyczne, natomiast tak jak BigTable dostarcza modele przypominające tabele.
Cassandra została otwarta przez Facebooka w 2008 roku. Aktualnie rozwijana jest przez Apache oraz współpracowników z różnych firm.
Poniższy opis dotyczy instalacji i konfiguracji na systemie linuksowym a konkretniej na Debianie. W innych systemach operacyjnych kroki te przebiegają analogicznie. Debian został wybrany ze względu na częste wykorzystywanie go jako OS dla serwera baz danych.
Thrift wykorzystywany jest do tworzenie skalowalnych, multiplatformowych usług webowych. Generuje klienty RPC w dowolnym języku (z pośród C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk, OCaml) które mogą komunikować się z tym samym serwerem RPC. Dzięki takiemu podejściu Cassandra może być dostępna w łatwy i szybki sposób z pośród wielu języków programowania.
instalacja dodatkowych pakietów na debianie
konfiguracja (bez wsparcia dla ruby):
./configure --disable-gen-rb --without-ruby make make install
do poprawnego działania z pythonem należy jeszcze ręcznie zainstalować biblioteki:
cd lib/py python setup.py install
linki:
tutorial do thrifta http://wiki.apache.org/thrift/Tutorial
najprostsza instalacja polega na rozpakowaniu tarballa z plikami cassandry i skonfigurowaniu ścieżek dostępu do logów i do danych w pliku conf/storage-conf.xml
Jeżeli nie chcemy zmieniać defaultowych ścieżek wystarczy że stworzymy poniższe katalogi tak aby odpowiadały tym zdefiniowanym:
sudo mkdir -p /var/log/cassandra sudo chown -R `whoami` /var/log/cassandra sudo mkdir -p /var/lib/cassandra sudo chown -R `whoami` /var/lib/cassandra
Uruchmienie pojedynczego węzła:
bin/cassandra -f # -f loguje na stdout
testowanie węzła poprzez konsole cli:
bin/cassandra-cli --host localhost --port 9160
Cassandra posiada model danych, który najlepiej wyobrazić sobie jako cztero lub pięcio wymiarowy hash. Podstawowe pojęcia to:
Jest to najmniejszy/niższy przyrost danych. Określane są jako trójki zawierające nazwę, wartość oraz czas. Definicja:
struct Column { 1: binary name, 2: binary value, 3: i64 timestamp, }
Wystkie wartości wypełnia klient, dlatego ważna jest synchronizacja na komputerach klienckich. Wartość timestamp może być dowolna ale konwenją jest czas w milisekundach od 1970 roku. Używa się go do rozwiązywania konfliktów. W wielu przypadkach pomija się to pole i myśli o kolumnie jako o parze nazwa/wartość.
Jest to pojemnik na kolumny (analogiczny do tabeli w relacyjnym systemie). Posiadają upożadkowaną listę kolumn, do których można odwaołać się poprzez nazwę. Definiuje się je w pliku storage-conf.xml, kórego nie można modyfikować bez restartowania Casandry.
W Casasndrze, każda ColumnFamily jest przechowywana w oddzielnym pliku, który z kolei jest posortowany w wierszu (Row) okreslonym pożądku np. po kluczu. Powiązane kolumny, do których jest wspólny dostęp powinny być w jedej rodzinie kolumn. Klucz wiersza (row key) określa jakie dane są przechowywane na maszynie. Tak więc każdy klucz może być powiązany z danymi z różnych rodzin kolumn. Jest to jednak odrębne logicznie, dlatego interfejs Thrift jest zorientowany tak aby możliwy był dostęp do jedenego ColumnFamily poprze klucz w danej chwili.
Przykład:
{ "klucz":{ "Uzytkownicy":{ "adresEmail":{"name":"adresEmail", "value":"user.email@email.com"}, "stronaWWW":{"name":"stronaWWW", "value":"http://userstrona.com"} }, "Statystyki":{ "wejscia":{"name":"wejscia", "value":"1000"} } }, "user2":{ "Uzytkownicy":{ "adresEmail":{"name":"adresEmail", "value":"user2@email.com"}, "twitter":{"name":"twitter", "value":"user2"} } } }
Klucz „klucz” identyfikuje dane w dwóch rożnych rodzinach kolumn („Uzytkownicy” i „Statystyki”), co wcale nie oznacza, że dane z tych kolumn są powiązane. Podsiadanie danych z tym samym kluczem dla róznych rodzin kolumn zalezy wyłacznie od aplikacji. Co więcej, rodzina kolumn „Uzytkownicy” posiada różne nazwy kolumn dla „klucz” oraz „user2” co jest dozwolone w Cassandrze.
Jest to pierwszy wymiar cassandrowego hasha. Są kontenerem dla column families. Są mnie j więcej tej ziarnistości co schemat lub baza w relaycjnym systemie. Stanowią punkt konfiguracyjny i zarządzający dla column families. Są rownież strukturami na których wykonuje się „batch insert”.
Są to kolumny, których wartościami są super kolumny. Oznacza to ze super kolumna jest posortowana tablicą asocjacyjną kolumn. O kolumnach i super kolumnach można myśleć jako o mapach:
{ "mccv": { "Tags": { "cassandra": { "incubator": {"incubator": "http://incubator.apache.org/cassandra/"}, "jira": {"jira": "http://issues.apache.org/jira/browse/CASSANDRA"} }, "thrift": { "jira": {"jira": "http://issues.apache.org/jira/browse/THRIFT"} } } } }
„Tags” - rodzina kolumn,
„cassandra”, „thrift” - super kolumny.
Cassandra wspiera schematy partycjionowania o stosunkowo niewielkiej ilości kodu. Dostarcza ona dwóch narzędzi:
System, który wspiera wyłącznie partycjonowanie oparte na has'ach, nie może efektywnie wywoływać dużych zapytań.
Do korzystania z Cassandry w Javie, będziemy używać klienta Thrift. Thrift pozwala oprócz Javy na pracę z wieloma innymi językami np. C++, PHP, Python. Aby móc zacząć pracę musimy najpierw skonfigurować bazę Cassandry poprzez modyfikacje pliku konfiguracyjnego storage-conf.xml znajdującego się w katalogu CASSANDRA_HOME/conf. Pomiędzy elementy <Keyspaces> i </Keyspaces> wstawiamy następujące linie:
<Keyspace Name="Geography"> </Keyspace>
Keyspace odpowiada schematowi w relacyjnej bazie danych. Dajemy mu nazwę Geography. Właśnie pomiędzy tymi znacznikami umieszczamy wszystkie pozostałe instrukcje.
<ColumnFamily CompareWith="UTF8Type" Name="Cities"/>
ColumnFamily jest strukturą w której możemy przechowywać „wiersze”, możemy utożsamiać ją z tabelą w RDBMS. Naszą „tabelę” nazywamy Cities. Atrybut CompareWith służy do sortowania danych. W Cassandrze nie mamy możliwości tak jak w relacyjnej bazie wykonywania zapytań i otrzymywania posortowanych wyników. Ustawienie atrybutu powoduje że dane zostają posortowane w momencie wkładania ich do bazy i takie w niej już pozostają. W naszym przypadku wartość „UTF8Type” powoduje że klucze kolumn będą traktowane jako stringi UTF8.
<ReplicaPlacementStrategy>org.apache.cassandra.locator.RackUnawareStrategy</ReplicaPlacementStrategy> <ReplicationFactor>1</ReplicationFactor> <EndPointSnitch>org.apache.cassandra.locator.EndPointSnitch</EndPointSnitch>
Powyższe linie są niezbędne aby móc operować i pracować z bazą Cassandry. Zostawiamy tutaj domyślne wartości.
Przykładowy program obrazujący pracę z Cassandrą zostanie wykonany w środowisku Netbeans. Aby móc pracować bez przeszkód i aby w ogóle mieć możliwość zaczęcia pracy z Cassandrą należy po stworzeniu nowego projektu dodać odpowiednie biblioteki do niego. Dokonujemy tego poprzez kliknięcie prawym przyciskiem na Libraries w drzewku z projektem. Wybieramy Add JAR/Folder i wskazujemy pliki znajdujące się w folderze CASSANDRA_HOME/lib.
Po użytych nazwach widzimy że nasz przykładowy program będzie przechowywał informacje o miastach. Tabela „Cities” zawiera odwzorowanie klucz-wartość. Kluczami będą nazwy miast np. „Kraków”, „Warszawa”, natomiast wartości będą „kolumnami”. Mówiąc kolumna mamy tutaj jednak na myśli strukturę „Column” z Cassandry która to zawiera trzy pola nazwę, wartość oraz czas. Kolumnami w naszej bazie będą „country” - kraj w którym leży dane miasto, „population” - jego ludność, „area” - jego powierzchnia.
Ok, możemy teraz już zacząć pracę z kodem.
Pierwsze co musimy to połączyć się z bazą Cassandry – łączymy się na porcie 9160 domyślnym porcie na którym nasłuchuje Cassandra, po czym przekazujemy połączenie klientowi który zajmuje się utrzymaniem komunikacji z serwerem. Poniższa funkcja wykonuje to zadanie.
private static Cassandra.Client setupConnection() throws TTransportException { try { tr = new TSocket("localhost", 9160); TProtocol proto = new TBinaryProtocol(tr); Cassandra.Client client = new Cassandra.Client(proto); tr.open(); return client; } catch (TTransportException exception) { exception.printStackTrace(); } return null; }
Zamknięcie połączenia polega na wymuszeniu wykonania wszystkich zadań które mogą znajdować się w buforze i zamknięciu Socketa:
private static void closeConnection() { try { tr.flush(); tr.close(); } catch (TTransportException exception) { exception.printStackTrace(); } }
Przyjrzymy się teraz jak będziemy dodawać i przechowywać nasze miasta w bazie.
private static void createCities(Cassandra.Client client, String key, String country, String population, String area) { try { long timestamp = System.currentTimeMillis(); Map<String, List<ColumnOrSuperColumn>> job = new HashMap<String, List<ColumnOrSuperColumn>>(); List<ColumnOrSuperColumn> columns = new ArrayList<ColumnOrSuperColumn>(); Column column = new Column("country".getBytes(KODOWANIE), country.getBytes(KODOWANIE), timestamp); ColumnOrSuperColumn columnOrSuperColumn = new ColumnOrSuperColumn(); columnOrSuperColumn.setColumn(column); columns.add(columnOrSuperColumn); column = new Column("population".getBytes(KODOWANIE), population.getBytes(KODOWANIE), timestamp); columnOrSuperColumn = new ColumnOrSuperColumn(); columnOrSuperColumn.setColumn(column); columns.add(columnOrSuperColumn); column = new Column("area".getBytes(KODOWANIE), area.getBytes(KODOWANIE), timestamp); columnOrSuperColumn = new ColumnOrSuperColumn(); columnOrSuperColumn.setColumn(column); columns.add(columnOrSuperColumn); job.put(TABELA, columns); client.batch_insert(SCHEMA, key, job, ConsistencyLevel.ALL); } catch (Exception exception) { exception.printStackTrace(); } }
Jak już wspomnieliśmy wyżej każda kolumna musi zawierać pole timestamp. Pierwszą rzeczą którą robimy jest stworzenie zmiennej przechowującej czas utworzenia. Jest ona używana do rozwiązywania konfliktów gdy mamy kilka podobnych kluczy. Dlatego nie należy nigdy ustawiać takich samych wartości tego pola; najczęściej stosuje się liczbę milisekund które upłynęły od roku 1970. W następnej linii tworzymy mapę w której będziemy umieszczać wiersze. Kluczem w naszej mapie będzie String który jest nazwą ColumnFamily czyli naszą „tabelą” zdefiniowaną wcześniej w pliku konfiguracyjnym. Wartością mapy będzie lista specjalnej struktury mogącej przechowywać Columnę lub SuperColumnę, u nas to będą jak już wcześniej wspomnieliśmy Columny czyli konstrukcja przechowująca trzy pola.
W kolejnych liniach tworzymy instacje naszych Column: country, population i area oraz wstawiamy do nich odpowiednie wartości przesłane jako argumenty funkcji, a następnie każdą Columne dodajemy do listy.
Na końcu używamy metody batch_insert do zapisania wszystkich danych jednorazowo w bazie. W przypadku gdybyśmy chcieli zapisać tylko pojedynczą Columne należy użyć metody insert. Jako parametry dla metody batch_insert wykorzystujemy klucz naszej tabeli czyli miasto do którego odnoszą się wszystkie dane zapisane w strukturze Column, nazwę schematu oraz mapę o której mówiliśmy wcześniej. Ostatnim parametrem jest tzw. ConsistencyLevel który jest wykorzystywany przy wszystkich operacjach zapisu oraz czytania z bazy i wskazuje kiedy żądanie klienta zostaje wykonane. Typ ALL mówi, że zapis powinien się odbyć przed udzieleniem odpowiedzi klientowi. Dokładne znaczenie każdego parametru jest wyjaśnione na stronie wiki Cassandry http://wiki.apache.org/cassandra/API#ConsistencyLevel.
Jeśli zapisaliśmy dane do bazy, pewnie chcielibyśmy je z niej odczytać. Poniżej znajduje się funkcja realizująca odczytanie wszystkich naszych kolumn z tabeli:
private static void selectAllCities(Cassandra.Client client) { try { KeyRange keyRange = new KeyRange(3); keyRange.setStart_key(""); keyRange.setEnd_key(""); SliceRange sliceRange = new SliceRange(); sliceRange.setStart(new byte[] {}); sliceRange.setFinish(new byte[] {}); SlicePredicate slicePredicate = new SlicePredicate(); slicePredicate.setSlice_range(sliceRange); ColumnParent columnParent = new ColumnParent(TABELA); List<KeySlice> keySlices = client.get_range_slices(SCHEMA, columnParent, slicePredicate, keyRange, ConsistencyLevel.ONE); for (KeySlice keySlice : keySlices) { printToConsole(keySlice.getKey(), keySlice.getColumns()); } } catch (Exception exception) { exception.printStackTrace(); } }
KeyRange jest używane do wyspecyfikowania jaki zakres kluczy chcemy wyciągnąć z bazy. W naszym przypadku w rzeczywistości nie definiujemy zakresu a jedynie ilość wierszy które chcemy wczytać z bazy, dlatego w konstruktorze przekazujemy liczbę 3 co odpowiada trzem wierszom.
SliceRange jest strukturą przechowującą informacje o zakresie, kolejności oraz ilości dla zapytania które zwraca wiele kolumn. Można ją utożsamiać z poleceniami LIMIT oraz ORDER BY z języka SQL. Metoda setStart ustawia kolumnę od której powinniśmy zacząć pobieranie danych. Pusta tablica byte oznacza rozpoczęcie od pierwszej kolumny. Metoda setFinish ustawia Columne na której powinniśmy skończyć pobieranie danych. Pusta tablica byte oznacza odzyskanie wszystkich Column dopóki nie uzyskamy wartości count. SliceRange posiada jeszcze jedną funkcje setCount której nie użyliśmy tutaj, a oznaczającą liczbę Column które chcemy zwrócić – coś jak LIMIT w SQL.
Następnie dzięki SlicePredicate ustawiamy dane które chcemy odzyskać, u nas jest to zdefiniowany wcześniej przy użyciu SliceRange pewien zakres kolumn.
W końcu dzięki metodzie get_range_slices wczytujemy dane i przypisujemy je do listy obiektów KeySlice. Jest to obiekt przechowujący odzyskane wiersze zawierający klucz i kolumny należące do danego wiersza.
Instrukcje wykonywane w funkcji main to:
Cassandra.Client client = setupConnection(); removeCities(client, "Mediolan"); removeCities(client, "Warszawa"); removeCities(client, "Kraków"); System.out.println("Dodaje miasta"); createCities(client, "Mediolan", "włochy", "1308735", "18377"); createCities(client, "Warszawa", "polska", "1714446", "51724"); createCities(client, "Kraków", "polska", "754854", "32700"); System.out.println("Wyciagam dane z bazy:"); selectAllCities(client); closeConnection();
Na początku ustanawiamy połączenie. Kolejne trzy instrukcje to usuniecie wierszy z bazy na wypadek gdyby już z danym kluczem znajdowały się w bazie. W kolejnych liniach dodajemy trzy miasta: „Mediolan”, „Warszawa” „Kraków”, a następnie wczytujemy i wypisujemy dopiero co włożone informacje na ekran. Otrzymany rezultat wygląda następująco:
Dodaje miasta Wyciągam dane z bazy: Key: 'Warszawa' name: 'area', value: '51724', timestamp: 1276876711965 name: 'country', value: 'polska', timestamp: 1276876711965 name: 'population', value: '1714446', timestamp: 1276876711965 Key: 'Kraków' name: 'area', value: '32700', timestamp: 1276876711967 name: 'country', value: 'polska', timestamp: 1276876711967 name: 'population', value: '754854', timestamp: 1276876711967 Key: 'Mediolan' name: 'area', value: '18377', timestamp: 1276876711924 name: 'country', value: 'włochy', timestamp: 1276876711924 name: 'population', value: '1308735', timestamp: 1276876711924
Wpis z deskryptora:
<Keyspace Name="Geography"> <ColumnFamily CompareWith="UTF8Type" Name="Cities"/> <ReplicaPlacementStrategy>org.apache.cassandra.locator.RackUnawareStrategy</ReplicaPlacementStrategy> <ReplicationFactor>1</ReplicationFactor> <EndPointSnitch>org.apache.cassandra.locator.EndPointSnitch</EndPointSnitch> </Keyspace>
package cassandracities; /** * * @author johnny */ import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.cassandra.thrift.Cassandra; import org.apache.cassandra.thrift.Column; import org.apache.cassandra.thrift.ColumnOrSuperColumn; import org.apache.cassandra.thrift.ColumnParent; import org.apache.cassandra.thrift.ColumnPath; import org.apache.cassandra.thrift.ConsistencyLevel; import org.apache.cassandra.thrift.InvalidRequestException; import org.apache.cassandra.thrift.KeyRange; import org.apache.cassandra.thrift.KeySlice; import org.apache.cassandra.thrift.NotFoundException; import org.apache.cassandra.thrift.SlicePredicate; import org.apache.cassandra.thrift.SliceRange; import org.apache.cassandra.thrift.TimedOutException; import org.apache.cassandra.thrift.UnavailableException; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; public class Main { private static final String SCHEMA = "Geography"; private static final String TABELA = "Cities"; public static final String KODOWANIE = "utf-8"; private static TTransport tr = null; public static void main(String[] args) throws TException, InvalidRequestException, UnavailableException, UnsupportedEncodingException, NotFoundException, TimedOutException { Cassandra.Client client = setupConnection(); //System.out.println("Usuwam miasta o danym kluczu, na wypadek gdyby już znajdowały się w bazie"); removeCities(client, "Mediolan"); removeCities(client, "Warszawa"); removeCities(client, "Kraków"); System.out.println("Dodaje miasta"); createCities(client, "Mediolan", "włochy", "1308735", "18377"); createCities(client, "Warszawa", "polska", "1714446", "51724"); createCities(client, "Kraków", "polska", "754854", "32700"); System.out.println("Wyciagam dane z bazy:"); selectAllCities(client); closeConnection(); } private static Cassandra.Client setupConnection() throws TTransportException { try { tr = new TSocket("localhost", 9160); TProtocol proto = new TBinaryProtocol(tr); Cassandra.Client client = new Cassandra.Client(proto); tr.open(); return client; } catch (TTransportException exception) { exception.printStackTrace(); } return null; } private static void closeConnection() { try { tr.flush(); tr.close(); } catch (TTransportException exception) { exception.printStackTrace(); } } private static void removeCities(Cassandra.Client client, String key) { try { ColumnPath columnPath = new ColumnPath(TABELA); client.remove(SCHEMA, key, columnPath, System.currentTimeMillis(), ConsistencyLevel.ALL); } catch (Exception exception) { exception.printStackTrace(); } } private static void createCities(Cassandra.Client client, String key, String country, String population, String area) { try { long timestamp = System.currentTimeMillis(); Map<String, List<ColumnOrSuperColumn>> job = new HashMap<String, List<ColumnOrSuperColumn>>(); List<ColumnOrSuperColumn> columns = new ArrayList<ColumnOrSuperColumn>(); Column column = new Column("country".getBytes(KODOWANIE), country.getBytes(KODOWANIE), timestamp); ColumnOrSuperColumn columnOrSuperColumn = new ColumnOrSuperColumn(); columnOrSuperColumn.setColumn(column); columns.add(columnOrSuperColumn); column = new Column("population".getBytes(KODOWANIE), population.getBytes(KODOWANIE), timestamp); columnOrSuperColumn = new ColumnOrSuperColumn(); columnOrSuperColumn.setColumn(column); columns.add(columnOrSuperColumn); column = new Column("area".getBytes(KODOWANIE), area.getBytes(KODOWANIE), timestamp); columnOrSuperColumn = new ColumnOrSuperColumn(); columnOrSuperColumn.setColumn(column); columns.add(columnOrSuperColumn); job.put(TABELA, columns); client.batch_insert(SCHEMA, key, job, ConsistencyLevel.ALL); } catch (Exception exception) { exception.printStackTrace(); } } private static void selectAllCities(Cassandra.Client client) { try { KeyRange keyRange = new KeyRange(3); keyRange.setStart_key(""); keyRange.setEnd_key(""); SliceRange sliceRange = new SliceRange(); sliceRange.setStart(new byte[] {}); sliceRange.setFinish(new byte[] {}); SlicePredicate slicePredicate = new SlicePredicate(); slicePredicate.setSlice_range(sliceRange); ColumnParent columnParent = new ColumnParent(TABELA); List<KeySlice> keySlices = client.get_range_slices(SCHEMA, columnParent, slicePredicate, keyRange, ConsistencyLevel.ONE); for (KeySlice keySlice : keySlices) { printToConsole(keySlice.getKey(), keySlice.getColumns()); } } catch (Exception exception) { exception.printStackTrace(); } } private static void printToConsole(String key, List<ColumnOrSuperColumn> result) { try { System.out.println("Key: '" + key + "'"); for (ColumnOrSuperColumn c : result) { if (c.getColumn() != null) { String name = new String(c.getColumn().getName(), KODOWANIE); String value = new String(c.getColumn().getValue(), KODOWANIE); long timestamp = c.getColumn().getTimestamp(); System.out.println(" name: '" + name + "', value: '" + value + "', timestamp: " + timestamp); } else { } } } catch (UnsupportedEncodingException exception) { exception.printStackTrace(); } } }
thrift -v -gen py path_to_cassandra/interface/cassandra.thrift
import Cassandra
Jest to program pythonowy wykorzystujący wygenerowaną bibliotekę z pliku Cassandra.py
./Cassandra-remote -h localhost:9160 describe_version ./Cassandra-remote -h localhost:9160 describe_keyspace Keyspace1
Została wykonana na podstawie wygenerowanego pliku Cassandra-remote.
Konfiguracja w pliku conf/storage-conf.xml
<Keyspace Name="Dictionary"> <ColumnFamily Name="Words" ColumnType="Super" CompareWith="UTF8Type" CompareSubcolumnsWith="UTF8Type" RowsCached="10000" KeysCached="50%" Comment="A column family with supercolumns, whose column and subcolumn names are UTF8 strings"/> <ReplicaPlacementStrategy>org.apache.cassandra.locator.RackUnawareStrategy</ReplicaPlacementStrategy> <!-- Number of replicas of the data --> <ReplicationFactor>1</ReplicationFactor> <EndPointSnitch>org.apache.cassandra.locator.EndPointSnitch</EndPointSnitch> </Keyspace>
#!/usr/bin/env python import sys import time import pprint from urlparse import urlparse from thrift.transport import TTransport from thrift.transport import TSocket from thrift.transport import THttpClient from thrift.protocol import TBinaryProtocol import Cassandra from ttypes import * pp = pprint.PrettyPrinter(indent = 2) host = 'localhost' port = 9160 uri = '' framed = False http = False argi = 1 if len(sys.argv)>1 and sys.argv[argi] == '-h': parts = sys.argv[argi+1].split(':') host = parts[0] port = int(parts[1]) argi += 2 if http: transport = THttpClient.THttpClient(host, port, uri) else: socket = TSocket.TSocket(host, port) if framed: transport = TTransport.TFramedTransport(socket) else: transport = TTransport.TBufferedTransport(socket) protocol = TBinaryProtocol.TBinaryProtocol(transport) client = Cassandra.Client(protocol) transport.open() class Dict: def translate(self, lang_from, word_from, lang_to): colPath = ColumnPath("Words", lang_from, lang_to) try: ret = client.get('Dictionary', word_from, colPath, ConsistencyLevel.ONE,) if ret: print "translate " + word_from + " (" + lang_from + " - " +lang_to+"): " + ret.column.value except NotFoundException: print "there is no translate for " + word_from + " (" + lang_from + " - " +lang_to+")" def getAllTranslates(self, lang_from, word_from): colParent = ColumnParent("Words", lang_from) slPred = SlicePredicate(None, SliceRange('','')) ret = client.get_slice('Dictionary', word_from, colParent, slPred, ConsistencyLevel.ONE,) print "all transalte for word " + word_from + " (" + lang_from + "):" for item in ret: print " translate " + word_from + " (" + lang_from + " - " +item.column.name+"): " + item.column.value def addWord(self, lang_from, lang_to, word_from, word_to): colPath = ColumnPath("Words", lang_from, lang_to) timestamp = int(time.time()) ret = client.insert('Dictionary', word_from, colPath, word_to, timestamp, ConsistencyLevel.ONE,) if ret is None: print "inserted word "+word_from+" ("+lang_from+")" + " - " +word_to+" ("+lang_to+")" else: print ret def removeWord(self, lang_from, lang_to, word_from): colPath = ColumnPath("Words", lang_from, lang_to) timestamp = int(time.time()) ret = client.remove('Dictionary', word_from, colPath, timestamp, ConsistencyLevel.ONE,) if ret is None: print "removed word " + word_from + " ("+lang_from+" - "+lang_to+")" dic = Dict() dic.addWord("pl","en","dom","home") dic.addWord("pl","de","dom","house") dic.addWord("pl","cz","dom","holpiczka") print dic.removeWord("pl","de","dom") print dic.translate("pl", "dom", "en") dic.translate("pl", "dom", "cz") dic.translate("pl", "dom", "de") print dic.getAllTranslates("pl","dom") transport.close()
Jak każda technologia Cassandra posiada w sobie zarówno wady jak i zalety. Oto część z nich: