Modelos experimentales de persistencia para aplicaciones web (I): congelación de objetos con Object Freezer

Hoy comienzo una serie de artículos dedicados a exponer diferentes patrones y arquitecturas de software para un problema tradicional: el almacenamiento de datos. Hacer consultas SQL a una base de datos relacional es una de las soluciones más extendidas, pero no es la única. Hay alternativas como las que te voy a presentar en este artículo y los siguientes.

Disclaimer

Las ideas que voy a presentar son experimentales. Son conceptos, con su discusón y su implementación (casi siempre parcial), pero no son estándares ampliamente probados y usados en el mundo real. El objetivo de estos artículos es reflexionar, discutir, probar y ver que algunas soluciones pueden ser útiles en ciertos escenarios, y otras quizá no valgan para nada. :-P

La congelación de objetos

La primera idea que voy a presentar es un concepto de Sebastian Bergmann. Los datos que se almacenan deben ser objetos de PHP. Se trata de “congelar” los objetos de una forma similar a como lo hacen las funciones de serialización. La diferencia es que el método de congelación entiende las relaciones entre objetos (es decir, que un atributo de un objeto sea, a su vez, otro objeto), de tal forma que si el objeto A tiene una referencia a B y el objeto C tiene una referencia a B también, al congelar los objetos A y C la referencia a B será un puntero al objeto congelado B, en lugar de congelar B dentro de A y B dentro de C, duplicando los datos, como hace serialize().

Pero ¿qué quiere decir congelar objetos? Pues sencillamente convertirlos en arrays para poder guardarlos en CouchDB, una base de datos orientada a documentos en la que los datos se almacenan en JSON.

Para comprender el funcionamiento de Object Freezer, nada mejor que una presentación de su autor: Cool Objects Sleep on the Couch.

¿Qué problemas soluciona Object Freezer?

  1. La definición y uso de esquemas relacionales. Ya no son necesarias las tablas, claves primarias, foreign keys, etc.
  2. El mapeo objeto-relacional. No hay que definir la conversión de tablas a objetos y viceversa.
  3. El almacenamiento sencillo de atributos vectoriales: es una forma fácil y automática de guardar arrays en la base de datos.
  4. ¡Tipado dinámico! En PHP se pueden añadir nuevos atributos a un objeto ya existente en tiempo de ejecución. No sería posible guardar este tipo de objetos en esquemas relacionales fijos, pero con Object Freezer es posible.

Además, tiene la ventaja de que, al guardar los objetos en CouchDB se maneja el versionado de los objetos, ya que CouchDB controla los cambios de todos sus registros.

Dame algo de código

//Una clase cualquiera
class Coche {
    private $color = 'rojo';
} 

//Una instancia de una clase cualquiera
$ferrari = new Coche();

require_once 'Object/Freezer/Storage/CouchDB.php'; 

//Instancia el freezer indicando los datos de conexión a CouchDB
$storage = new Object_Freezer_Storage_CouchDB('test', new Object_Freezer, TRUE, 'localhost', 5984); 

//Congela el objeto y lo almacena en CouchDB, devolviendo el ID auto-generado
$id = $storage->store($ferrari);

Así de sencillo. CouchDB asigna un ID único a cada nuevo registro. Para leer un registro:

//Devuelve el objeto de tipo Coche con $id
$objeto = $storage->fetch($id);

¿Es adecuado Object Freezer para mi proyecto?

La decisión de almacenar datos sin un esquema fijo es tan importante como la de usar una base de datos relacional o una NoSQL. ¿Es NoSQL bueno? No vamos a discutirlo aquí, pero sí citaré un tweet que leí una vez:

Usar NoSQL es como ir sin calzoncillos. Es cómodo, pero peligroso.

No obstante, hay una cuestión a tener en cuenta en la decisión: si usamos Object Freezer es cierto que la base de datos será schema-less, pero no es cierto que nuestros datos no estén estructurados, ya que ¡usamos objetos! Sería más correcto decir que los datos no están estructurados en la base de datos, pero sí en el código.

Si hemos hecho un buen diseño de las clases y sus relaciones, Object Freezer puede ser una solución rápida y fácil para guardar esos objetos en una base de datos muy interesante como CouchDB.

Sin embargo, no creo que sea buena idea para proyectos…

  • con fuerte necesidad de integridad referencial, o
  • que no estructuran toda la información con objetos, o
  • grandes, o
  • si crees que el modelo almacenamiento de datos cambiará, o
  • si no sabes usar las vistas de CouchDB para obtener listas de datos filtradas, indizadas, etc.

Creo que con esto es suficiente introducción. Puedes consultar la presentación que he citado arriba y el blog de Sebastian Bergmann para tener más información sobre Object Freezer, que por cierto es software libre :-)

Esto es todo por hoy. Ahora te toca a ti: ¿qué te parece el concepto de “congelar los objetos”? ¿Usarías Object Freezer? ¿Usarías CouchDB? ¿Conoces soluciones similares para otros lenguajes de programación?

Migrando el blog a WordPress

Hace algunos días el blog dejó de funcionar, probablemente por una actualización de los servidores de mi proveedor, 1&1, que invalidaba las reglas de Apache rewrite que escribí. Sí, son cosas que apestan de los servidores compartidos, pero lo he aprovechado para “matar moscas a cañonazos” y migrar todo el blog a WordPress.

El antiguo blog funcionaba con un software PHP escrito por mí siguiendo el patrón Multivista y usaba plantillas Smarty. Mis razones para migrar a WordPress:

  1. Compatibilidad con los servidores: el .htaccess de WordPress es más tolerante que el mío a los cambios caprichosos de 1&1.
  2. Tener un software más seguro, más probado y más potente.
  3. Los experimentos que haga con el blog (sobre todo de cosas relacionadas con web semántica) los escribiré como plug-ins de WordPress y así otras personas los podrán probar.
  4. El back-end de WordPress es infinitamente mejor que el mío.
  5. Utilizar la plantilla Twenty Eleven como base para el diseño, por su calidad de código HTML5 y CSS.
  6. Seguir aprendiendo WordPress.
  7. Rendimiento: WP-Cache y WP Super Cache me ahorrarán un trabajo que me daba pereza empezar.

Este ha sido, a grandes rasgos, el procedimiento que he seguido para la migración:

Uno: migrando los artículos

Esto no ha sido difícil, ya que he escrito un sencillo plug-in que se conecta a la BD antigua, recorre todos los artículos (con sus tags) y los introduce en WordPress con la función wp_insert_post().

Dos: migrar los comentarios

Cuando ya tenía todo listo me di cuenta de que había olvidado migrar los comentarios… así que los migré no programando un plug-in (como hice con los artículos) sino exportando los comentarios de la antigua base de datos a CSV, adaptando las columnas a la tabla wp_comments e importándolo a esta con phpMyAdmin.

Tres: adaptando el theme

Me he basado en Twenty Eleven, el theme por defecto de WordPress 3.2.1, que utiliza HTML5, soporta widgets, traducción y muchas otras cosas. La labor de theming, cuando ya tienes el diseño maquetado (el antiguo blog) es más laborioso que difícil, así que en un par de horas estuve despachado. También he aprovechado para hacer algunos cambios en el diseño, intentando que quede más limpio y simple, y cambiando los enlaces para compartir los posts en redes sociales.

Cuatro: organizando el contenido

Unos minutos de taxonomías, tipos de entradas, categorías, etc para tenerlo todo bien ordenadito.

Cinco: desplegando

Con el contenido migrado y organizado y el theme adaptado, ya puedo instalar en el servidor una nueva instancia de WordPress, subir el theme e importar el contenido con la herramienta Importar/Exportar de WordPress. El resto de tareas las hago directamente en el servidor de producción.

Seis: instalar plug-ins y retoques finales

Con el blog ya montado y funcionando sobre WordPress, completo la sección de contacto con Contact Form 7, enlazo el RSS con Feedburner, inserto el código de Google Analytics en el theme, añado el plug-in para resaltado de código fuente WP-Syntax y reviso que no hay enlaces rotos en los artículos con Broken Link Checker.

Siete: redirecciones 301 para no perder posicionamiento en buscadores

Con el blog en WordPress, las antiguas URL de los artículos se pierden, así que para evitar que los buscadores reindexen todo el contenido de nuevo y pierda el posicionamiento de mis artículos, he configurado redirecciones para cada uno de los artículos. La cabecera 301 “moved permanently” le indica a los buscadores que la página ha sido movida permanentemente a otra dirección, pero que sigue siendo la misma página.

Las redirecciones las he puesto en un script que es llamado desde la plantilla 404.php del theme, de forma que cuando se solicita una URL antigua se produce un error 404 controlado por WordPress, pero antes de que se lance la cabecerar 404 y página de error, mi script comprueba si esa URL es de las antiguas y redirige, con una cabecera 301, a la URL correcta.

Ajustando la cache de consultas de MySQL para un mejor rendimiento de Drupal

¡Hola de nuevo! Después de un tiempo de inactividad voy a retomar el blog, aunque no prometo publicar muy asiduamente :-P

Continuando el hilo post anterior (mayo de 2010, ¡la de cosas que han pasado desde entonces!), vamos a ver cómo aumentar el rendimiento de un sitio Drupal, en este caso a través de la cache de consultas de MySQL.

En /admin/reports/status/sql, Drupal muestra algunas estadísticas interesantes del servidor MySQL, que nos dan pistas sobre lo bien o mal optimizado que tenemos el sistema: número de consultas cacheadas, ordenaciones sin índices, etc.

En la explicación de alguno de los indicadores se indica “debe ser cero”. Estos indicadores son Select_full_join, Select_range_check y Sort_scan. En términos generales, si estos valores son altos se debería modificar el esquema de la BD añadiendo índices, aunque si estamos hablando de Drupal esta situación no debería darse, ya que el esquema tiene índices correctamente establecidos.

Otro indicador interesante es Qcache_lowmem_prunes. Indica “el número de veces que MySQL tuvo que retirar consultas del caché porque se quedó sin memoria”. En mi caso, este indicador mostraba el alarmante valor de 349102 (¡desde que se arrancó el servidor!).

Para arreglar esta situación vamos a aumentar el tamaño asignado a la caché de consultas. Lo haremos desde el fichero de configuración de MySQL (my.cnf):

query_cache_limit = 1M query_cache_size = 32M

El primer parámetro es el tamaño máximo de las consultas “cacheables”. Lo usaremos para evitar cachear consultas lentas o muy grandes, que suelen ser infrecuentes (y por tanto no tiene mucho sentido cachearlas).

El segundo parámetro es la cantidad de memoria que queremos asignar a la caché. Cuanta más memoria RAM tenga nuestro servidor, mayor valor podremos darle. En mi caso el servidor tiene 512MB, de los cuales alrededor de 200 están ocupados por el sistema y los servicios (Apache, el propio MySQL, memcache y los servicios de correo). Así que puedo aumentar el tamaño del cache hasta 50MB.

Después de reiniciar el servidor, lleva 95 días funcionando y arroja un valor de 3451 purgas. Muchas menos que con la anterior configuración.

Esta es una medida sencilla que ayuda a optimizar la cache de consultas de MySQL, un sistema que impacta en el rendimiento global del sistema, sobre todo si no tienes un sistema de caché fuera de MySQL.

Mejor rendimiento de Drupal con GraphicsMagick

Un pequeño (y espero útil) apunte antes de que penséis que he abandonado completamente el blog :-(

Se trata de un consejo para mejorar el rendimiento de nuestro sitio Drupal. El sistema utiliza por defecto la biblioteca GD para manipular imágenes (por ejemplo, generar miniaturas). El módulo ImageAPI inserta una capa de abstracción en Drupal que permite elegir qué biblioteca gráfica usar, GD o ImageMagick. Es mejor utilizar ImageMagick, ya que es una biblioteca independiente de PHP y, por ello obtendremos mejor rendimiento total en Drupal, y además es más estable (si falla GD puede caer todo el motor de PHP, sin embargo, si falla ImageMagick no caerá, ya que es un binario diferente).

Pero aún mejor: existe un proyecto llamado GraphicsMagick, que es un fork de ImageMagick y que es más estable y, sobre todo, con mejor rendimiento, ya que consume menos memoria y es más rápida. Y lo que es mejor, tiene la misma API que ImageMagick (el comando

convert), con lo cual podemos engañar a ImageAPI diciéndole que use ImageMagick cuando en realidad tenemos instalado GraphicsMagick.

Para lograr esto no tienes más que instalar GraphicsMagick y el módulo ImageAPI de Drupal, en dos sencillos pasos. Supongamos que trabajamos con un servidor Debian/Ubuntu (en otros sistemas operativos no debería ser muy diferente):

  1. Instalamos GraphicsMagick:
    apt-get install graphicsmagick-imagemagick-compatPodemos probar que GraphicsMagick funciona escribiendo convert en la consola:

isra@salon:~$ convert
GraphicsMagick 1.3.5 2009-01-26 Q8 http://www.GraphicsMagick.org/
Copyright (C) 2002-2009 GraphicsMagick Group.
Additional copyrights and licenses apply to this software.
See http://www.GraphicsMagick.org/www/Copyright.html for details.

Usage: convert [options ...] file [ [options ...] file …] [options ...] file

[más opciones]

  • Instalamos el módulo ImageAPI y lo activamos en /admin/build/modules (módulos ImageAPI e ImageAPI ImageMagick).
  • Accedemos a la configuración de ImageAPI en Administración > Configuración del sitio > ImageAPI (/admin/settings/imageapi) y veremos el siguiente mensaje: “The ImageAPI ImageMagick module is the only enabled image toolkit. Drupal will use it for resizing, cropping and other image manipulations“.
  • Si también hemos activado el módulo ImageAPI GD2, nos dará a elegir (seleccionamos ImageAPI ImageMagick como opción por defecto):

¡Y listo! Fácil y rápido. Podemos comprobar que funciona accediendo a Administración > Configuración del sitio > ImageAPI > Configurar (/admin/settings/imageapi/config), lo que nos mostrará información sobre GraphicsMagick (versión, tipos soportados, etc):

¿He hecho benchmarks para comprobar la ganancia de rendimiento con respecto a ImageMagick?

Pues no, lo siento. Pero los chicos de GraphicsMagick sí los han hecho, y son bastante jugosos.

Pero… ¿no es necesaria la extension imagick de PHP?

No, no es necesaria, ya que ImageAPI interactúa con ImageMagick/GraphicsMagick a través del binario
convert

Iré escribiendo algunos artículos más sobre técnicas fáciles para optimizar el rendimiento de Drupal.

Relanzando Caminayven, otra historia de éxito con WordPress

Caminayven

Como comentaba en el artículo Primeras experiencias con WordPress… y muchas nueces ;-) , me estoy introduciendo en el mundillo de WordPress. Nueces San Ignacio no ha sido el único proyecto que he realizado con este gestor de contenidos. Hoy quiero contarte mi experiencia en otro proyecto un poco más grande: Caminayven. Se trata de una revista católica on-line, que lleva más de cinco años on-line sobre PHP-Nuke con más de mil artículos, un montón de redactores y, afortunadamente, un número creciente de visitas.

Hace dos años que empecé a trabajar en un nuevo gestor de contenidos para Caminayven. Empecé programando desde cero un CMS bastante ambicioso, y aproveché para hacer mis primeros pinitos con JavaScript-RPC (una especie de alternativa a AJAX para comunicación asíncrona, basada en carga dinámica de scripts e iframes). El resultado fue desastroso, aunque aprendí la valiosa lección de que no es bueno reinventar la rueda.

Pero —cabezón de mí— volví a la carga a principios del año pasado ayudado por CakePHP, un framework que me facilitó bastante las cosas, pero que seguía sin ser suficiente. Por falta de tiempo e ilusión el proyecto volvió a quedar abandonado, hasta que hace unos pocos meses empecé a preguntarme si WordPress cubriría las necesidades básicas de Caminayven: publicación de artículos, galerías de fotos, acceso sencillo y elegante a la información (archivos, destacados, relacionados…) y poco más.

De nuevo, un análisis realista y concreto de la Arquitectura de la Información para el proyecto puso las cosas en su lugar. ¿Para qué programar una gestión de usuarios, workflow, editor WYSIWYG, feed RSS, categorías y tags… si WordPress ya lo hace? Dicho y hecho: convertir las antiguas plantillas de CakePHP en un theme para WordPress fue cuestión de dos tardes (copiando y modificando Kubrick).

Aunque he tenido algunos problemillas en el theming, quizá causados por mi ignorancia sobre WordPress o por su estructura interna —en mi opinión poco elegante—: el formato de las fechas y la internacionalización. El formato de las fechas, aunque se especifica en el dashboard no se respetaba en el tema Kubrick, además de que los nombres de los meses no se traducen. Con respecto a la internacionalización (más concretamente la traducción), utilizo la versión en castellano de WordPress, pero aun así muchos mensajes están sin traducir, por lo que he tenido que traducir manualmente algunos fragmentos del theme.

En lo que respecta a la funcionalidad, he utilizado el plug-in Author Image y un código propio en el theme para mostrar, a la derecha de cada artículo, una breve ficha de su autor.

Galería

Caminayven también destaca por sus reportajes gráficos, la mayoría de ellos realizados por nuestro querido Javier Cebreros. Para publicarlos probé en primera instancia Awsom PixGallery. Aunque era sencillo utilizarlo y bonito ver una página para cada álbum/foto, el rendimiento es horrible (regenera continuamente las miniaturas), el código es patético (un único y monolítico script de miles de líneas, mezclando PHP y HTML) y el manejo de URL fallaba como una escopeta de feria. Ahora utilizamos NextGEN Gallery, que es justamente lo contrario: un buen rendimiento, mucha flexibilidad a la hora de subir fotos (subida múltiple por HTTP, FTP, etc) y una forma de mostrar los álbumes muy chula. Una posible mejora a este plug-in sería la construcción de colecciones a partir de álbumes en Flickr o Facebook (en Caminayven alojamos algunas fotos en estas redes sociales), y lo añado a mi lista de cosas-que-haré-algún-día.

Otros plug-ins

También he añadido WP-Cache y All-in-One-SEO-Pack para optimizaciones de rendimiento y SEO, respectivamente. Ambos funcionando muy bien y sin problemas. WP-Cache y los consejos de YSlow me han ayudado a hacer Caminayven más rápida, y aún se puede optimizar mucho más.

Migración

Hasta aquí la parte bonita. Pero no hay que olvidar que Caminayven es un proyecto de cierta envergadura y con una trayectoria de cinco años sobre PHP-Nuke, un gestor de contenidos que odio por ser el perfecto compendio de malas prácticas (de hecho, hace dos años nos colaron una inyección SQL que permitió a un niñato destrozarnos la página…). Así que la migración no se plantea fácil. Por una parte, los artículos se escribieron desde el editor WYSIWYG de Internet Explorer y desde Word, así que te puedes hacer una idea de la impeorable calidad del marcado HTML. No se puede copiar y pegar el contenido de una tabla de PHP-Nuke en otra de WordPress, así que la solución aparente está filtrando el HTML con unas decenas de expresiones regulares caseras y las funciones de inserción del API de WordPress. Además, las categorías han cambiado, y he tenido que hacer una tabla de “traducción” de las viejas a las nuevas categorías.

Y, por si fuera poco, nuestros artículos salen en Google News y perder las antiguas URL (modules.php…) sería desastroso para nuestro posicionamiento, tanto en Google News como en los buscadores en general. Así que ha habido que hacer redirecciones 301 e intentar encontrar el artículo por su antiguo ID… también he tenido que añadir el ID de los artículos a las URL, por exigencia de Google News (un poco caprichoso, sí).

Analítica y redes sociales

Al igual que con Nueces San Ignacio, mi trabajo en este proyecto ha ido más allá de lo puramente ingenieril, ya que he diseñado, redactado y otras actividades. Las dos más importantes para el éxito del proyecto han sido la analítica web y la promoción en redes sociales. En cuanto a lo primero, Google Analytics es una gran herramienta pero se está quedando corta para alguna información que necesito saber (los referer exactos cuando un usuario encuentra un error 404 o cosas así), así que estoy introduciendo pequeños códigos de seguimiento propios en la parte del servidor.

Con respecto a las redes sociales, hemos redefinido nuestra estrategia en Facebook, pasando de grupo a usuario corriente. El motivo primario es la visibilidad, ya que todas las novedades que publiquemos con el usuario Caminayven aparecerán en el timeline de nuestros amigos. En cuanto al contenido, enlazamos los artículos más destacables en el status, publicamos todos los posts con NetworkedBlogs y subimos los reportajes gráficos. La estrategia está funcionando en parte, ya que las visitas desde esta fuente han aumentado, pero nuestros seguidores (alrededor de 200) no son muy activos corriendo la voz… es decir, que la regla del 90-90-1 no se cumple del todo.

También nos hemos dado de alta en Twitter (@caminayven), aunque todavía no hay mucha actividad. Esperemos que en los próximos meses la cosa vaya aumentando.

Conclusiones

  • WordPress mola. No es la panacea, pero mola.
  • Hacer una revista católica en internet no es fácil, y crear una comunidad alrededor de la misma mucho menos, pero estamos en el camino.
  • Caminayven es un proyecto apasionante al que siempre se le puede sacar punta, quedan muchas cosas por hacer, pero de momento he cumplido con mi deber ;-)
  • La audiencia está respondiendo: las visitas, en general, han aumentado, y en particular, el promedio de páginas por visita. Pero, sobre todo, los comentarios, ese elemento mágico y maravilloso que da vida a un sitio web :-)

En fin, otro proyecto cerrado por el momento… dentro de un mes te cuento el siguiente… ¡más ambicioso y emocionante todavía!.