Louder Developer Zone
Articles, Tutorials and Tips from the community and Louder developers

Integración de Kumbia Enterprise con Apache Cassandra

Publish at Monday, April 12, 2010
By Core Developers - Louder

¿Qué es Cassandra?

Cassandra es una base de datos hibrida no-relacional liberada por Facebook y actualmente administrada y mantenida por la fundación Apache en su incubadora.

Ofrece muchas más posibilidades que una simple base de datos clave-valor como Redis, pero tiene menos facilidades de consulta que MongoDB.

Cassandra es actualmente usada por Digg, Facebook, Twitter, Reddit, Rackspace, Cisco y muchas otras grandes empresas con altos volúmenes de datos y necesidades de escalabilidad. Uno de los clusteres en producción más grandes tienen alrededor de 100TB sobre 150 maquinas.

Recientemente Cassandra empezó a ser adoptada por sitios web con gran tráfico como Twitter y Digg como un reemplazo de MySQL a mediano plazo.

¿Qué no es Cassandra (y las NoSQL)?

Cassandra y las bases de datos NoSQL en general, no son un reemplazo para las bases de datos relacionales (RBDM). Cualquier sistema en donde la prioridad sea la integridad de los datos no debe ser implementado con una base de datos NoSQL, de ninguna manera. Cassandra en este caso seguramente es gran opción para sitios con alto tráfico y necesidad de escalar en donde no de manejan datos no-criticos.

Características de Cassandra

  • Esquema Fléxible: siendo un document store, no es necesario definir que campos debe llevar cada registro ó que tipos de datos deben manejar. Es posible agregar ó quitar campos en cualquier momento lo que representa un avance muy importante en productividad.
  • Escalabilidad Real: Cassandra escala horizontalmente en todo el sentido de la palabra. Agregar más poder a cassandra es solamente colocar otra maquina, sin reiniciar nada, copiar datos ó modificar las aplicaciones.
  • Lectura/Escritura Distribuida: Es posible leer ó escribir en cualquier maquina con cassandra dentro de un cluster y obtener el mismo resultado. Todos los datos son replicados en todas las maquinas muy rápidamente.
  • Rendimiento: Cassandra es 10 veces más rápido para escribir y 9 veces más rápido que MySQL al tratar con altos volúmenes de datos (> 50GB).

¿Qué diferencia a Cassandra de los demas bases de datos NoSQL?

Cassandra está diseñada bajo un principio que va más alla de almacenar datos mediante claves-valores. Con esta base de datos es posible almacenar datos en esquemas multidimensionales (5-dimensiones) ofreciendo mayor flexibilidad para organizar y consultar los datos. A diferencia los modelos relacionales presentan tablas de 2 dimensiones, agregar otra dimensión requiere agregar otra tabla.

Integración con Kumbia Enterprise

Anteriormente ya habiamos integrado Kumbia Enterprise con MongoDB, por lo que se había agregado una nueva capa de acceso a bases de datos llamada nosql al componente Db.

En el caso de Cassandra hemos trabajado en crear una integración más completa la cuál incluye: un manejador de modelos (ODM?) para bases de datos de este tipo, un adaptador de Session, un componente de Traslate (traducción), optimización para almacenar los ACL (listas de control de acceso) y las credenciales de autenticación con Auth.

Cassie, un ODM(?) para Cassandra

El componente Cassie es un (object-document-mapper) que facilita en gran medida al trabajo de "ver" bases de datos Cassandra como las tradicionales relacionales. Cassie actua como una capa superior funcional que implementa algunos comportamientos de las bases de datos relacionales.

Con Cassie es posible:

  • Definir modelos como los utilizados en ActiveRecord
  • Crear, actualizar, Consultar y Borrar registros
  • Definir relaciones (1-1, 1-n, n-1, n-m) entre modelos de Cassandra y modelos ActiveRecord

Cassie, también extiende las capacidades de Cassandra al permitir:

  • Definir llaves primarias para controlar la duplicidad. Las claves en Cassandra tienen un scope-global, con Cassie es posible que 2 modelos diferentes tengan un mismo valor de llave primaria.
  • Definir columnas auto-numéricas en los modelos. Cassandra no tiene columnas auto-numéricas.
  • Definir indices a partir de las columnas de los modelos en cualquier momento.

Crear un Blog con Cassandra y Kumbia Enterprise

Ya hemos visto como Arin Sarkissian, el principal desarrollador de Digg ha enseñado como crear un esquema para almacenar los datos de un blog usando Cassandra.

El objetivo del siguiente tutorial es ilustrar la integración de Cassie con Cassandra como un medio transparente y familiar a como se ha venido trabajando con ActiveRecord.

Cassie almacena los datos de los modelos de una forma parecida pero a la vez un poco diferente con el fin de ofrecer un nivel de flexibilidad superior. En este tutorial se van a utilizar 2 modelos: "Posts", para almacenar los artículos y "Comments" para los comentarios.

Agregar un Keyspace a Cassandra

Los Keyspaces (espacios de nombres) se usan para agrupar familias de columnas en Cassandra. Un Keyspace podría decirse que son las "bases de datos" como se conocen en las relacionales.

Un keyspace que vaya a ser usado con Cassie debe tener la siguiente estructura:

 <Keyspace Name="Blog">
      <KeysCachedFraction>0.01</KeysCachedFraction>
      <ColumnFamily ColumnType="Super"
                    CompareWith="UTF8Type"
                    CompareSubcolumnsWith="UTF8Type"
                    Name="Data"/>
      <ColumnFamily ColumnType="Super"
                    CompareWith="UTF8Type"
                    CompareSubcolumnsWith="UTF8Type"
                    Name="Schema"/>
      <ColumnFamily CompareWith="UTF8Type" Name="Indexes"/>
    </Keyspace>

Esta configuración se agrega al archivo conf/storage-conf.xml de Cassandra. Después de agregar la configuración se requiere reiniciar el servidor de cassandra.

A diferencia de otros ejemplos que se puedan encontrar en internet, este keyspace define 3 familias de super columnas donde se almacenarán los datos, indices y meta-datos de los modelos respectivamente.

Crear la Aplicación

Creamos el esqueleto de la aplicación usando el script. La aplicación se llamará "blog". También se hubiera podido llamar "default" pero usaremos "blog" en este tutorial:

php scripts/create_application.php --name blog

Con el anterior comando se creó el esqueleto de la aplicación. Ahora se edita el archivo apps/blog/config/enviroment.ini y se configura la conexión a cassandra en el entorno de desarrollo:

[development]
database.layer = nosql
database.type = cassandra
database.host = 192.168.10.100
database.port = 9160
database.keyspace = Blog

La configuración hace referencia al keyspace destinado a almacenar los datos del blog, junto con el nodo de cassandra que utilizará la aplicación.

Cargar la libreria Thrift

Thrift es una libreria originalmente desarrollada por Facebook y mantenida actualmente por Apache, que permite el intercambio de datos entre diferentes lenguajes y que en nuestro caso proporciona el acceso de bajo nivel a Cassandra desde PHP. Utilizamos el archivo apps/blog/config/boot.ini para cargar está libreria que se incluye con el framework para cualquier petición a la aplicación:

[modules]
extensions = "Thrift.Thrift"

Definir los modelos

Como se mencionó se van a utilizar los modelos "Posts" y "Comments" en este blog, por lo que debemos crear los modelos de la siguiente forma:

//apps/blog/models/posts.php
class Posts extends CassieRecord {

}

//apps/blog/models/comments.php
class Comments extends CassieRecord {

}

De momento los modelos no requieren ninguna inicialización en especial. Tampoco se requiere definir los atributos que van a tener ya que estos pueden variar de un registro a otro.

Crear Posts

El Controlador PostsController nos va a permitir administrar los posts del blog:

<php

class PostsController extends ApplicationController {

	//Acción por defecto del controlador
	public function indexAction(){
		//Si no hay "Posts" creados ir a la acción "new"
		$numeroPostsCreados = $this->Posts->count();
		if($numeroPostsCreados==0){
			$this->routeTo("action: new");
		} else {
			//Si hay alguno ir a la acción "list"
			$this->routeTo("action: list");
		}
	}

	//Acción para crear "Posts"
	public function newAction(){

		$request = $this->getRequestInstance();

		//Ingresaron el campo titulo?
		if($request->isSetPostParam("titulo")){

			$post = new Posts();
			$post->titulo = $request->getParamPost("titulo");
			$post->contenido = $request->getParamPost("contenido");

			if($post->save()==false){
				foreach($post->getMessages() as $message){
					Flash::error($message->getMessage());
				}
			} else {
				Flash::success("Se creó el post con éxito");
				return $this->routeTo("action: list");
			}

		}

	}

	//Acción para listar los existentes
	public function listAction(){

	}

	//Acción para editar "Posts" existentes
	public function editAction($postId){
		$postId = $this->filter($postId, "int");
		if($postId>0){
			$post = $this->Posts->findFirst($postId);
			if($post!=false){

				//Si la petición es por GET mostramos los campos actuales sino
				//se actualizan según lo ingresado
				$request = ControllerRequest::getInstance();
				if($request->isGet()==true){
					Tag::displayTo("titulo", $post->titulo);
					Tag::displayTo("contenido", $post->contenido);
				} else {

					$post->titulo = $request->getParamPost("titulo");
					$post->contenido = $request->getParamPost("contenido");

					if($post->save()==false){
						foreach($post->getMessages() as $message){
							Flash::error($message->getMessage());
						}
					} else {
						Flash::success("Se actualizó el post con éxito");
						return $this->routeTo("action: list");
					}

				}
			} else {
				$this->routeTo("action: index");
			}
		} else {
			$this->routeTo("action: index");
		}
	}

	//Acción para eliminar "Posts" existentes
	public function deleteAction($postId){
		$postId = $this->filter($postId, "int");
		if($postId>0){
			$post = $this->Posts->findFirst($postId);
			if($post!=false){
				if($post->delete()==false){
					foreach($post->getMessages() as $message){
						Flash::error($message->getMessage());
					}
				} else {
					Flash::success("Se eliminó el post con éxito");
					return $this->routeTo("action: list");
				}
			} else {
				$this->routeTo("action: index");
			}
		} else {
			$this->routeTo("action: index");
		}

	}

}

Vistas de cada acción:

//apps/blog/views/posts/new.phtml
<?php View::getContent() ?>

<h1>Crear un Post</h1>

<?php echo Tag::form() ?>
	<p>
		<b>Título</b>
		<?php echo Tag::textField("titulo") ?>
	</p>
	<p>
		<b>Contenido</b>
		<?php echo Tag::textArea("contenido") ?>
	</p>
	<p>
		<?php echo Tag::submitButton("Grabar") ?>
	</p>
<?php echo Tag::endForm()?>
//apps/blog/views/posts/list.phtml
<?php View::getContent() ?>

<h2>Posts Grabados</h2>

<table>
	<tr>
		<th>Id</th>
		<th>Titulo</th>
	</tr>
	<?php foreach($Posts->find() as $post){ ?>
		<tr>
			<td><?php echo $post->id ?></td>
			<td><?php echo $post->titulo ?></td>
			<td><?php echo Tag::linkTo("posts/edit/".$post->id, "Editar") ?></td>
			<td><?php echo Tag::linkTo("posts/delete/".$post->id, "Eliminar") ?></td>
		</tr>
	<?php } ?>
</table>

//apps/blog/views/posts/edit.phtml
<?php View::getContent() ?>

<h1>Editar un Post</h1>

<?php echo Tag::form() ?>
	<p>
		<b>Título</b>
		<?php echo Tag::textField("titulo") ?>
	</p>
	<p>
		<b>Contenido</b>
		<?php echo Tag::textArea("contenido") ?>
	</p>
	<p>
		<?php echo Tag::submitButton("Grabar") ?>
		<?php echo Tag::linkTo("posts/list", "Volver") ?>
	</p>
<?php echo Tag::endForm()?>

Conclusiones

  • Cassandra es una base de datos que promete mucho y desde ya se puede ir aprovechando sus capacidades
  • Se ha realizado un importante trabajo en la integración de Kumbia Enterprise y la base de datos Cassandra
  • Cassie ofrece una capa de mapeo muy familiar a lo que se puede hacer con ActiveRecord, supliendo algunas de las falencias de Cassandra

Enlaces Externos

Tell friends about this article on social networks:



blog comments powered by Disqus

Next: Introducción a KEQL. Parte 1

Colaborate

Colaborate

We invite you to submit articles and tutorials to the Developer Zone.

Archive

  • Mayo 2009

Maybe you are interested

Added value to your Business.

Become a Solution Partner Louder Now.

Bring to the Open-Source retroactively..

Learn more about Shared Louder Labs