martes, 13 de mayo de 2014

Fin del curso online de Datastax: "Java Development with Apache Cassandra"

Ya he terminado el curso online de Datastax "Java Development with Apache Cassandra":

He realizado el examen online hoy día 13/05/2013, habiendo contestado 53 de las 60 preguntas correctamente:
 
Podemos ver un seguimiento del curso, dividido en 7 lecciones (6 de ellas en las que se incluyen practicas), más el test final online. Entre los días 5,6 y 7 de este mes observé los videos de cada lección, tomé apuntes y me hice un resumen. Desde el día 8 hasta hoy día 13 he realizado las prácticas, y hoy el examen:


Aquí podemos ver el certificado que se consigue con este curso online:



Curso Datastax: Cassandra - Practica 6

En esta última practica se añaden vistas estadísticas y se utilizan características avanzadas de Cassandra para optimizar algunas partes de la aplicación.


Ejercicio 1 - Usando contadores para recolectar estadísticas

 
Añadimos la tabla de estadísticas:
CREATE TABLE statistics (counter_name text PRIMARY KEY, counter_value counter);
Implementamos el método StatisticsDAO.increment_counter y SatisticsDAO.decrement_counter. Para incrementar o decrementar un contador debe utilizarse una instrucción update:

 
Comprobamos que la pagina de estadísticas de la aplicación web funciona correctamente; nos dice que tenemos 2 usuarios y 5 playlist, lo comprobamos en el shell:
 
SELECT * FROM users;
username | password | playlist_names
----------+----------+-------------------------------
  astwin1 |     test |            {'test1', 'test2'}
  astwin2 |     test | {'test''3', 'test1', 'test2'}

Ejercicio 2 - Verificando logins leyendo con un mayor nivel de consistencia (quorum)

 
Una de las características de las que dispone Cassandra es que permite escoger el nivel de consistencia que se desee. Por defecto, sólo requiere que una de las replicas de los datos entre los nodos sea leída en una consulta. Se puede escoger un valor más elevado de consistencia, requiriendo que más de una de las replicas de los datos sea leída, comprobada (comprobar que todos los nodos contienen la misma información actualizada) y poder obtener el valor más reciente de un valor.


Debemos modificar el método UserDAO.getUserWithQuorum para añadir un nivel de consistencia de quorum en la consulta:

Ejercicio 3 - Usando paginación

 
Automatic paging introduced in Cassandra 2.0, allows the developer to iterate on an entire ResultSet without having to care about its size: some extra rows are fetched as the client code iterate over the results while the old ones are dropped. The amount of rows that must be retrieved can be parameterized at query time. In the Java Driver this will looks like:
Statement stmt = new SimpleStatement("SELECT * FROM images");
stmt.setFetchSize(100);
ResultSet rs = session.execute(stmt);

Modificar el método  TracksDAO.listSongsByGenre para obtener 200 entradas cada vez (paginación):

Ejercicio 4 - Mejorando el rendimiento de las consultas con una cache de registros

 
En las tablas que más se lean se puede activar una caché para almacenar la últimas consultas. Si las consultas se realizan muy a menudo a esa tabla se puede obtener ventaja, leyendo de la caché en vez tener que traer los datos otra vez del cluster.

Utilizar la herramienta nodetool y el comando info para ver que el tamaño de caché de registros es 0 (también aparecen las estadísticas de la caché):
 

Aumentamos a 50 MB la caché de registros modificando el archivo cassandra.yaml:
astwin@astwin-H87-HD3:~$ sudo gedit /etc/cassandra/cassandra.yaml
# Default value is 0, to disable row caching.
row_cache_size_in_mb: 50
Activamos la caché en alguna de las tablas. Lo hacemos en la de tracks_by_genre por ejemplo:
cqlsh> ALTER TABLE playlist.track_by_genre WITH caching='rows_only';
cqlsh> use playlist ;
cqlsh:playlist> describe KEYSPACE;
 
Reiniciamos Cassandra:
astwin@astwin-H87-HD3:~$ sudo service cassandra restart
Miramos unas cuantas veces la misma consulta en la web de canciones por género y comprobamos las estadísticas de la caché de registros (rows) para ver que sí se ha utilizado la caché (y se habrá obtenido un beneficio en rendimiento en las consultas):
Row Cache        : size 12964446 (bytes), capacity 52428800 (bytes), 27 hits, 32 requests, 0,844 recent hit rate, 0 save period in seconds 

Ejercicio 5 - Variable estática para 'PreparedStatement'

 
Uno de los beneficios de utilizar un PreparedStatement en la API de Java para realizar consultas, es que se puede instanciar el objeto una única vez y reutilizarlo en las posteriores consultas. Se nos pide crear una variable estática de este tipo para que sólamente se tenga que instanciar una única vez:

 

 

lunes, 12 de mayo de 2014

Curso Datastax: Cassandra - Practica 5

En esta práctica se pretende implementar el código para poder añadir y quitar canciones de las playlists creadas por los usuarios.

Ejercicio 1 - Añadir la tabla playlist_tracks

CREATE TABLE playlist_tracks (username TEXT, playlist_name TEXT, sequence_no TIMESTAMP, artist TEXT, track_name TEXT, genre TEXT, track_length_in_seconds INT, track_id UUID, PRIMARY KEY ((username, playlist_name), sequence_no ));

Ejercicio 2 - Método de añadir una canción al playlist

 

Ejercicio 3 - Método de eliminar una canción del playlist

 


Ejercicio 4 - Borrando una playlist entera usando un bloque atómico 

 
Tenemos que completar el método deletePlaylist:
 

Debemos crear una consulta en un bloque atómico que se encargue de borrar la playlist de las tablas users y playlist_tracks:
 

Ejercicio 5 - Comprobar desde cqlsh si todo funciona bien

 
Playlist con dos canciones desde aplicación web:
 
Comprobamos las tablas en el shell:

Borramos una cancion en app. web y las revisamos:
 







 
Borramos la playlist completa:
 









Ejercicio 6 - Utilizando DEVcenter para realizar algunas consultas
 

Creamos un indice secundario en la tabla playlist_tracks:
CREATE INDEX playlist_track_by_genre ON playlist_tracks(genre);
Ejecutamos un par de consultas:


 

 

   

   
 

Búsqueda  de canciones por duración mayor a 120 segundos. Necesitamos un nuevo indice secundario y una consulta con ALLOW FILTERING:
 


Curso Datastax: Cassandra - Practica 4

En esta sesión se pretende agregar cuentas de usuario, donde cada uno de ellos pueda añadir su propio playlist.

Ejercicio 1 - Añadir tabla users

CREATE TABLE users (username TEXT PRIMARY KEY, password TEXT);

Ejercicio 2 - Implementando el método addUser()

Debemos implementar una Ligthweight transaction para asegurarnos que sólo inscribimos a un usuario si no se encuentra en la base de datos ya su username:

Comprobamos que funciona creando un usuario (llamado astwin):
 
 

 





  

Ejercicio 3 - Añadir un Set a la tabla users para almacenar playlist personal

alter table users add playlist_names set<text>  ;

Ejercicio 4 - Código para añadir y eliminar playlists

Tenemos que modificar el código del método PlaylistDAO.createPlayList:
 
 
También el código del método PlaylistDAO.deletePlayList:
 
En el caso de insertar elementos en colecciones, como son los Set, no se pueden crear "Bound Stataments", sino que hay agregar los valores directamente en el string de la consulta CQL. Para evitar el problema que existe con las comillas en un string se cambian por dos comillas, así no existe problema al realizar una consulta mediante el API de java (por ejemplo si la variable nombre de lista es test'3, debemos cambiarla en el código por test''3). Mostramos el código del método createPlayList:

Comprobamos como funciona correctamente (incluso con nombres que contengan una comilla simple '):
 
 
Ahora implementamos el método deletePlayList:

 Comprobamos que funciona eliminando listas desde la aplicación web:

 

 

 

 


Curso Datastax - Cassandra: Práctica 3

Añadiendo funcionalidades a la aplicación web: Playlist

Se pretende añadir la capacidad de ordenar la búsqueda por artista, poder incluir las canciones favoritas seleccionando una estrella y limitar el número de filas que se muestran cuando se realiza una búsqueda por género.

Preparando la práctica

Se propone el uso de un script que borre el keyspace playlist, lo cree de nuevo con todas las tablas y las rellene, en definitiva, que nos permita resetear la base de datos. Se nos indica que añadamos otra nueva tabla track_by_id para poder realizar consultas de canciones por track_id.

Creamos archivo 'playlist.cql':
use system;
// Borramos el keyspace playlist anterior
drop keyspace playlist;

// Creando el keyspace
CREATE KEYSPACE playlist WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1 };
USE playlist;

// Creamos las tablas
create table track_by_id (track text, artist text, track_id UUID, track_length_in_seconds int, genre text,music_file text, primary key (track_id));
create table track_by_artist (track text, artist text, track_id UUID, track_length_in_seconds int, genre text,music_file text, primary key (artist, track, track_id));
create table track_by_genre (track text, artist text, track_id UUID, track_length_in_seconds int, genre text,music_file text, primary key (genre, artist, track, track_id));

create table artists_by_first_letter (first_letter text, artist text, primary key (first_letter, artist));

// Rellenamos las tablas
copy track_by_id (track_id, genre, artist, track, track_length_in_seconds, music_file) FROM '/home/astwin/Escritorio/Cassandra/Leccion 3/scripts/songs.csv' WITH DELIMITER = '|'  AND HEADER=true;
copy track_by_artist (track_id, genre, artist, track, track_length_in_seconds, music_file) FROM '/home/astwin/Escritorio/Cassandra/Leccion 3/scripts/songs.csv' WITH DELIMITER = '|'  AND HEADER=true;
copy track_by_genre (track_id, genre, artist, track, track_length_in_seconds, music_file) FROM '/home/astwin/Escritorio/Cassandra/Leccion 3/scripts/songs.csv' WITH DELIMITER = '|'  AND HEADER=true;

copy artists_by_first_letter (first_letter, artist) from '/home/astwin/Escritorio/Cassandra/Leccion 3/scripts/artists.csv' WITH DELIMITER = '|';
Para poder ejecutar un scrip desde el terminal cqlsh:
cqlsh:playlist> source '/home/astwin/Escritorio/Cassandra/Leccion 3/scripts/playlist.cql';

Ejercicio 1 - Añadiendo una clausula "ORDER BY" a un comando SELECT

Se ha añadido en la aplicación web en el apartado de artistas ordenado por letra, dos botones de flecha arriba y abajo para poder ordenar la consulta.  Cuando se pulsa uno de estos botones se pasa un parámetro booleano llamado 'desc' al método ArtistsDAO. listArtistByLetter. Si éste parámetro es true los resultados de deben presentar en orden alfabético descendente, y ascendente si es false. 
Modificamos la función añadiendo la clausula ORDER BY a la consulta:
Podemos comprobar como podemos ordenar el resultado de formas ascendente o descendente:



 




 

Ejercicio 2 - Eligiendo el número de resultados de la búsqueda

En la búsqueda por género existe una gran cantidad de canciones. Se pretende limitar el número de resultados de la consulta. Se debe modificar el método TracksDAO.listSongsBygenre():

Modificamos la función añadiendo una clausula LIMIT a la consulta:

Comprobamos que funciona en la aplicación web:


Ejercicio 3 - Canción 'Hot'

Se quiere añadir la funcionalidad de categorizar una canción como 'hot' en el playlist clickeando en la estrella que aparece en la aplicación web al lado de cada canción.
 
Añadir un campo boolean a las tablas que lo necesiten para indicar cuando una canción es 'hot' o no:
cqlsh:playlist> alter table track_by_artist add hot boolean ;
cqlsh:playlist> alter table track_by_genre add hot boolean ;
Modificar el constructor TracksDAO(Row row):

Debemos modificar el método star() para que cambie el estado del campo hot:

Comprobamos que todo funciona bien (la estrella se enciende y se actualiza en la base de datos el valor booleano):


 Ejercicio 4 -Limitar temporalmente que una canción sea 'hot'

  Añadir clausula TTL para que la categorización de hot sólo dure 30 segundos:

 

domingo, 11 de mayo de 2014

Curso Datastax - Cassandra: Practica 2

Aplicación web: Playlist

Ejercicio 1 - Creando un Keyspace: 'playlist'

Abrimos el shell de Cassandra desde el terminal:
  astwin@astwin-H87-HD3:~$ cqlsh
Creamos el keyspace llamado playlist:
CREATE KEYSPACE playlist WITH replication = {
  'class': 'SimpleStrategy',  'replication_factor': '1' };
Como trabajamos en local, el factor de réplica 1 (una sola copia de los datos introducidos) y la estrategia de replicación simple (sin múltiples 'data centers').
Utilizamos este keyspace a partir de ahora:
USE playlist ;

Ejercicio 2 - Creando y cargando datos en la tabla de artistas

Creamos tabla de artistas ordenados por la primera letra del nombre:
CREATE TABLE artists_by_first_letter (
  first_letter text,
  artist text,
  PRIMARY KEY (first_letter, artist)
);
Nos permitirá realizar consultas a través de la primera letra del nombre del artista. 
Cargamos los datos de artistas proporcionados en el archivo 'artists.csv': primera letra y nombre de artista separados por caracter ' | ':
A|ARRESTED DEVELOPMENT
A|Abe Vigoda
...
cqlsh:playlist> COPY artists_by_first_letter(first_letter , artist ) FROM '/home/astwin/Escritorio/Cassandra/Leccion 2/scripts/artists.csv' WITH DELIMITER = '|';
3605 rows imported in 1.694 seconds.
Comprobamos que los datos se han cargado mirando unos cuantos registros:

cqlsh:playlist> SELECT * FROM artists_by_first_letter LIMIT 3;
 first_letter | artist
--------------+---------------------------------
            C |                  C.W. Stoneking
            C |                            CH2K
            C | CHARLIE HUNTER WITH LEON PARKER
La consulta no proporciona los resultados en orden alfabético por primera letra (first_letter: partition key), pero sí lo realiza por nombre (artist: clustering key).
Un ejemplo de consulta por letra:
cqlsh:playlist> SELECT * FROM artists_by_first_letter WHERE first_letter='N' LIMIT 5;

Ejercicio 3 - Correr la aplicación en Eclipse y ver los artistas

Cargamos el proyecto Maven descargado desde la página del curso de Datastax en Eclipse. Lo ejecutamos (clase principal StartJetty class). Abrimos la aplicación web (local): http://localhost:8080/playlist. Abrimos el apartado: “VISIT THE SONG DATABASE”. Clickeamos en alguna letra para obtener los artistas que comienzan por esa letra (se utiliza la consulta a la tabla que creamos anteriormente):

Si intentamos obtener las canciones por artista obtendremos un error, puesto que no tenemos creada la tabla track_by_artist:

Ejercicio 4 - Crear y cargar datos en la base de datos de canciones por artista (track_by_artist)

Creamos la tabla de canciones por artista:
CREATE TABLE track_by_artist (
  artist text,
  genre text,
  music_file text,
  track text,
  track_id uuid,
  track_length_in_seconds int,
  PRIMARY KEY (artist,track_id)
);
Cargamos datos desde fichero 'songs.csv' (contiene header y separación '|'):
track_id|genre|artist|track|track_length_in_seconds|music_file
c8bbc608-07ab-4586-9ba4-bedbd829c66c|classic pop and rock|Blue Oyster Cult|Mes Dames Sarat|246|TRFCOOU128F427AEC0
b3805f45-4db8-4362-bf3d-7864cfdaa0c1|classic pop and rock|Blue Oyster Cult|Screams|189|TRNJTPB128F427AE9F
....
cqlsh:playlist> COPY track_by_artist (track_id, genre, artist, track, track_length_in_seconds, music_file) FROM '/home/astwin/Escritorio/Cassandra/Leccion 2/scripts/songs.csv' WITH DELIMITER = '|' AND HEADER=true;
59600 rows imported in 16.323 seconds.
 Ahora comprobamos que ya podemos obtener las canciones por artista en la aplicación web (por ejemplo, miramos Camilo Sesto):

Ejercicio 5 - Implementar un comando 'select'

Si vamos a la letra P y al artista “Pagan's Mind” obtenemos una excepcion CQL:
Observamos el error:

com.datastax.driver.core.exceptions.SyntaxError: line 1:59 mismatched character '<EOF>' expecting '''
 at com.datastax.driver.core.exceptions.SyntaxError.copy(SyntaxError.java:35)
 at com.datastax.driver.core.ResultSetFuture.extractCauseFromExecutionException(ResultSetFuture.java:271)
 at com.datastax.driver.core.ResultSetFuture.getUninterruptibly(ResultSetFuture.java:187)
 at com.datastax.driver.core.Session.execute(Session.java:126)
 at com.datastax.driver.core.Session.execute(Session.java:77)
 at playlist.model.TracksDAO.listSongsByArtist(TracksDAO.java:65)
Si vamos al código Java del método listSongsByArtist, vemos que se realiza una consulta utilizando la API de Cassandra:
    String queryText = "SELECT * FROM track_by_artist WHERE artist = '" + artist + "'";
    ResultSet results = getSession().execute(queryText);
La consulta se crea de forma simple, utilizando un String, concatenando la consulta con la variable artist. En este caso, al contener la variable artist una comilla simple (Pagan's Mind), la consulta creada será errónea para CQL:
SELECT * FROM track_by_artist WHERE artist ='Pagan's Mind';
Necesitamos arreglar la consulta utilizando la API de Java con una consulta preparada (PreparedStatement object y BoundStatement object):
/* 
String queryText = "SELECT * FROM track_by_artist WHERE artist = '" + artist + "'";
ResultSet results = getSession().execute(queryText);
*/

// Preparamos consulta con string ? en los campos que obtendremos de variables 
   String queryText =  "SELECT * FROM track_by_artist WHERE artist =?";
   PreparedStatement prepared = getSession().prepare(queryText);
// Añadimos las variables  
   BoundStatement bound = prepared.bind(artist);
// Podemos realizar ya la consulta CQL
   ResultSet results = getSession().execute(bound);

Comprobamos que ya no se produce el error:


Ejercicio 6 - Realizando una petición por género

Necesitamos programa el método TracksDAO.listSongsByGenre() para añadir la funcionalidad de poder mostrar todas las canciones por género.

Primero será crear una nueva tabla en Cassandra (track_by_genre) para este tipo de consulta (de-normalización: crear nuevas tablas aunque suponga duplicar el almacenamiento de datos para poder ganar en velocidad).
cqlsh:playlist> CREATE TABLE track_by_genre (track_id UUID, genre TEXT, artist TEXT, track TEXT, track_length_in_seconds INT, music_file TEXT, PRIMARY KEY (genre,track,track_id))  ;
cqlsh:playlist> COPY track_by_genre  (track_id,genre,artist,track,track_length_in_seconds,music_file) FROM '/home/astwin/Escritorio/Cassandra/Leccion 2/scripts/songs.csv' WITH DELIMITER = '|' AND HEADER=true ;
59600 rows imported in 16.579 seconds.
Implementamos el método que se nos pide con la consulta adecuada por género (ver que la tabla nueva tiene como key primaria el genero, y no el artista):

 Comprobamos que ya obtenemos resultados en el apartado de búsqueda por genero:


Ejercicio 7 - Implementar 'introducir canción'

En el último apartado de la práctica se nos pide que introduzcamos una canción en la base de datos a través de la aplicación web y utilizando el modo de depuración de Eclipse comprobemos el proceso de añadir canción en el código de Java y completemos lo que pueda faltar.

Añadimos una canción utilizando la aplicación web (he añadido la canción Avicci - Hey Brother):
Comprobamos que se llama al método add() del objeto TracksDAO para insertar una nueva canción en la base de datos. Observamos como en este método sólo se implementan los comandos de insertar la nueva canción en las tablas artists_by_first_letter y track_by_artist, pero no lo hace en track_by_genre. Debemos completar este método. Importante recordar que en Cassandra se utiliza la filosofía de la denormalización, o implementar varias tablas para diversas consultas, aunque ello suponga  una redundancia en el almacenamiento de datos, y que esto implica tener que actualizar todas las tablas relacionadas ante la introducción de nuevos datos.

Comprobamos que se ha introducido correctamente y aparece en los tres tipos de consulta en la web: