in web we trust

también en color en: blat.lacoctelera.com

24 Febrero 2008

Límites en claves y valores de Memcached

Una cuestión que hay que tener presente a la hora de trabajar con Memcached y que no se suele tener en cuenta es el tamaño de las claves y los valores:

  • claves: máximo de 250 caracteres
  • valores alojados: máximo 1 Megabyte

Es muy difícil que una aplicación Rails las claves generadas a partir de las URL's tengan tanto tamaño, pero puede darse el caso y hay que tenerlo presente.

En el grupo de discusión acts_as_cached esta semana han propuesto hashear (con MD5, por ejemplo) las claves antes de guardarlas para curarnos en salud.

servido por Fernando 2 comentarios compártelo favorito

16 Enero 2008

Sirviendo feeds cacheados con Nginx directamente

Reconozco que me ha constado dios y ayuda dar con la regla adecuada, así que por eso me he decidido a postearla.

El problema es el siguiente: en unvlog.com (sí, ese proyecto del que no he hablado) somos muy Rails y tenemos para todos los recursos que tiene sentido una ruta que responde a un formato u otro según se le solicite pero con esta forma:

En concreto, esta URL corresponde al feed de un blog, feed que está cacheado en disco gracias a la caché de página de Rails. Pues bien, la idea es que el servidor web sirva directamente esta caché, así que una regla podría ser:


  if (-f /cache/$request_filename) {
          rewrite (.*) /cache/$1 break;
  }

(Esto, asumiendo que tenemos la caché en public/cache)

El problema es que esta regla no funciona, y creo que se debe a que blat.atom lleva un '.' y no es capaz de asignar la URL a la variable $request_filename.

Al final ha sido tan fácil como utilizar otra variable que he encontrado en la parte rusa del wiki, $request_uri:


  if (-f $document_root/cache/$request_uri) {
          rewrite ^/(.*).atom$ /cache/$1.atom break;
  }

Con esto ha funcionado perfectamente y ya tenemos cacheados y servidos por Nginx los feeds en unvlog.com.

Tags: unvlog, nginx

servido por Fernando 2 comentarios compártelo favorito

15 Enero 2008

Comprendiendo las estadísticas del Memcached

Memcached (o Memcache) incorpora un sistema de estadísticas interno que nos permite conocer el estado del demonio, su capacidad restante de almacenamiento, uso, conextiones, etcétera.

Para acceder a él a través del cliente de Ruby, por ejemplo, basta con cargar una consola en el entorno deseado e invocar al método stats del objeto de caché.

Por ejemplo, si nuestra configuración es la siguiente:


  CACHE = MemCache.new 'localhost:11211', :namespace => 'wadus_fragments'

Podemos utilizar el objeto CACHE de la siguiente manera:


  pp CACHE.stats
  {"bytes"=>746532339,
   "pid"=>10303,
   "connection_structures"=>19,
   "time"=>1200349556,
   "limit_maxbytes"=>1073741824,
   "cmd_get"=>689486,
   "version"=>"1.1.12",
   "bytes_written"=>3296488315,
   "cmd_set"=>265986,
   "get_misses"=>265168,
   "total_connections"=>1238,
   "curr_connections"=>17,
   "curr_items"=>66977,
   "uptime"=>56490,
   "get_hits"=>424318,
   "total_items"=>265986,
   "rusage_system"=>95.012555,
   "rusage_user"=>17.16339,
   "bytes_read"=>3226552602}

La interpretación de todos estos valores se puede encontrar en la definición del protocolo:

  Name              Type     Meaning
  ----------------------------------
  pid               32u      Process id of this server process
  uptime            32u      Number of seconds this server has been running
  time              32u      current UNIX time according to the server
  version           string   Version string of this server
  pointer_size      32       Default size of pointers on the host OS
                             (generally 32 or 64)
  rusage_user       32u:32u  Accumulated user time for this process 
                             (seconds:microseconds)
  rusage_system     32u:32u  Accumulated system time for this process 
                             (seconds:microseconds)
  curr_items        32u      Current number of items stored by the server
  total_items       32u      Total number of items stored by this server 
                             ever since it started
  bytes             64u      Current number of bytes used by this server 
                             to store items
  curr_connections  32u      Number of open connections
  total_connections 32u      Total number of connections opened since 
                             the server started running
  connection_structures 32u  Number of connection structures allocated 
                             by the server
  cmd_get           64u      Cumulative number of retrieval requests
  cmd_set           64u      Cumulative number of storage requests
  get_hits          64u      Number of keys that have been requested and 
                             found present
  get_misses        64u      Number of items that have been requested 
                             and not found
  evictions         64u      Number of valid items removed from cache                                                                           
                             to free memory for new items                                                                                       
  bytes_read        64u      Total number of bytes read by this server 
                             from network
  bytes_written     64u      Total number of bytes sent by this server to 
                             network
  limit_maxbytes    32u      Number of bytes this server is allowed to
                             use for storage. 
  threads           32u      Number of worker threads requested.
                             (see doc/threads.txt)

En concreto cuatro parámetros me han parecido interesantes de observar:

  • el parámetro bytes junto con el de limit_maxbytes: el primero representa el total de bytes ocupados actualmente, frente el máximo.
  • el número de fallos de caché que refleja el valor del parámetro get_misses junto con el número de aciertos get_hits

Los primeros nos permiten saber si hemos llegado al límite el almacenamiento que necesita nuestra aplicación y el segundo nos permite saber si nuestra política de caché es adecuada: diviendo hits entre misses obtenemos la proporción de fragmentos encontrados frente a fragmentos que ya no existían. Un valor de 1 indica que por cada dos búsquedas, una tiene éxito y otra no.

Por supuesto un valor inferior a uno muestra que la caché no sirve de nada (indicaría por ejemplo que necesitaríamos ampliar el Memcache o que tenemos una política de borrado "exagerada").

Tags: cache, memcached, ruby

servido por Fernando sin comentarios compártelo favorito

30 Noviembre 2007

Caché en Rails

Rompo este silencio para subir una de mis charlas de la Conferencia Rails :

Caché en Rails

Pronto, todas las demás, colgadas en la web de la Conferencia,

servido por Fernando sin comentarios compártelo favorito

3 Septiembre 2007

Un pequeña benchmark sobre servidores web + aplicación con Rails y Merb

Testing Various Configurations of Rails, Merb, Swiftiply, and Nginx es una más que interesante comparativa entre el clásico Mongrel y una alternativa que ha surgido hace poco Swiftiply: un servidor proxy que además, nos regala una variante de Mongrel, el evented Mongrel, totalmente compatible, pero que basa su funcionamiento en gestión de eventos (EventMachine), en lugar de utilizar los clásicos threads.

Además, en el post utilizan Nginx como servidor web y Ruby on Rails y Merb, como frameworks de desarrollo, así como Memcached para almacenar sesiones.

Los resultados, muy interesantes:

  • Merb es mucho más rápido que Rails, a costa de perder parte de la magia
  • Nginx es una gran alternativa a Apache, sobretodo cuando la memoria es un recurso escaso (en mi nuevo Slicehost, por ejemplo)
  • y, el más importante, el evented Mongrel funciona bastante mejor que el Mongrel en situaciones de alta concurrencia

Así que si te gusta "jugar" y quieres probarlo, lo recomiendo: la instalación es muy sencilla y los resultados saltan a la vista.

servido por Fernando sin comentarios compártelo favorito

18 Agosto 2007

Sobre httperf y las pruebas de carga

Andaba yo viendo esta mañana el screencast de Peepcode Benchmarking with httperf bastante emocionado (ya que la fama precede a estos screencasts) para ver cómo podía mejorar las pruebas de rendimiento que estoy haciendo contra una aplicación Rails que he instalado en mi recién adquirido Slicehost.

La situación es la siguiente: es una cuenta Slicehost de 256Mb de memoria, en el que tú instalas lo que quieres. El problema es que debes de instalar un servidor web, los servidores de aplicaciones y la base de datos, y todos ellos han de luchar por esos 256Mb. Así que al final me he decidido por instalar esto:

  • servidor web: Nginx, que tenía ganas de probarlo tras leer a uno de los sysadmins decir que Nginx era lo más estable que había visto nunca, y que recomendaba usar Apache sólo en el caso de querer que un mismo servidor web compartiera diferentes aplicaciones y en distintos lenguajes (PHP y Rails, por ejemplo). Además, su uso de memoria es ridículo.
  • servidor de aplicaciones: Mongrel. Considerando los 60Mb que viene a consumir un Mongrel de memoria, el número de Mongrels en el clúster lo he establecido a 3.
  • servidor de base de datos: MySQL. No había otra opción.

Además de todo esto, hay un servidor Memcached de 32Mb para guardar las sesiones.

Y claro, quería hacer pruebas de carga contra el stack completo para ver de rendimiento qué tal.

Las pruebas se han realizado contra una página sin caché y con bastantes consultas a la base de datos, cuyo tiempo de respuesta medio es de 0.005 segundos o lo que es lo mismo, unas 20 req/s.

El screencast

El screencast se queda algo corto en cuanto a uso de la herramienta httperf, ya que el autor pierde mucho tiempo explicando conceptos teóricos y muy básicos de Estadística: media y desviación estándar, así como cómo mostrar luego los resultados a través de gráficas (unas muy bonitas generadas con gruff).

Posteriormente las pruebas las realiza contra un único Mongrel, explicando con todo detalle:

  • la importancia de utilizar datos reales: la muestra debe de ser representativa para que los resultados lo sean
  • la importancia de obtener muchas muestras: cuantas más muestras, la media y la varianza son más significativas también
  • la importancia de anotar todos los resultados
  • la importancia de la interpretación de los mismos en función de los resultados obtenidos y entender qué significan

Las pruebas las realiza para comparar el rendimiento de su aplicación sin usar caché, utilizando caché de acción y utilizando caché de página. Así que establece el baseline en los resultados obtenidos sin utilizar caché y a partir de los obtenidos utilizando caché establece porcentajes de mejora.

Otros aspectos a tener en cuenta

Hay otras muchas situaciones en las que es recomendable hacer pruebas de rendimiento contra tu propia aplicación:

  • calcular el número de Mongrels a lanzar en el servidor de aplicaciones. Una buena explicación (aunque breve también), la da Zed en la página de Mongrel: How many Mongrels
  • comparar el rendimiento entre distintos sistemas de almacenamiento de sesiones
  • comparar distintos algoritmos de balanceo en el módulo de proxy
  • determinar el tope máximo de ficheros servidos a través de una unidad NFS
  • ...

En estos casos es posible que nos interese conocer algún parámetro más de httperf que los que se han explicado en el vídeo:

  • --rate: determinar el número de peticiones concurrentes lanzadas en un segundo
  • --wsess: determina cada cuántas peticiones se inicia una nueva sesión

Y además, si vais a hacer pruebas de carga, hay que tener en cuenta estas perogrulladas, que a veces no lo son tanto:

  1. el número de Mongrels en el clúster
  2. la conexión: ¡no hagas las pruebas con un ADSL! En mi caso he pasado de 30 req/s a más de 50 req/s sólo por lanzar las pruebas desde una máquina con una conexión decente
  3. testea el stack entero: sobre todo porque es interesante ver si hay problemas de conectividad entre una capa (web) y la otra (aplicación), o si el proxy que reparte la carga está mal configurado, etcétera

servido por Fernando sin comentarios compártelo favorito

13 Agosto 2007

Trabajando con BackgroundDRB en el entorno de test

BackgrounDRB es un proyecto en desarrollo que pretende crear un servicio estable para ejecutar tareas en segundo plano, sin que estas entorpezcan la navegación del usuario por la aplicación, relentizando sus tiempos de respuesta.

La verdad es que era un completo desconocido para mí hasta que encontré la presentación de Sergio Espeja en la conferencia de Rails: Tareas en background con RoR y BackgrounDRb.

En este año que ha pasado, hay una nueva versión que corrige algunos fallos y deja otros abiertos, pero que es, dicen, bastante estable. Y la verdad es que con la anterior tuve muchos problemas para crear mis workers pero con esta estoy encantado.

Pero este post no era para hablar de las bondades del proyecto, que son muchas, sino para contar cómo hacer que en los tests no necesitemos tener lanzado un servidor BackgroundDRB para que estos funcionen, sino que podemos utilizar un mock.

He encontrado uno entre los tickets del proyecto, y que se ve que aún no está incluído en la última versión.

Basta con descargarlo en test/mocks/test e incluir la siguiente línea en el test_helper.rb:


require File.dirname(__FILE__) + '/mocks/test/backgroundrb_mock.rb'

Y voilá, tests funcionando de nuevo.

servido por Fernando sin comentarios compártelo favorito

12 Agosto 2007

Testeando helpers

El otro día, buscando información sobre cómo testear helpers en Rails di con este post: Test your helpers, en el que el autor habla de un plugin desarrollado por él mismo que facilita sobremanera la labor.

El plugin se llama helper_test y es muy sencillo de utilizar:

  1. descargar
  2. editar tests/test_helper.rb e incluír la línea: require File.expand_path(File.dirname(__FILE__) + '/helper_testcase')
  3. utilizar el generador que incluye: ./script/generate helper_test Foo

El test generado tendrá este aspecto:


  require File.dirname(__FILE__) + '/../../test_helper'

  class FooHelperTest < HelperTestCase

    include FooHelper

    #fixtures :users, :articles

    def setup
      super
    end

  end

A destacar que podemos utilizar fixtures.

Y la forma de proceder también es muy sencilla: creamos un test y en su interior podemos invocar al helper como si de un método más se tratara y comparar el resultado obtenido con los valores esperados. Si el helper tiene argumentos, ampliaremos el abanico de pruebas.

Una pequeña reflexión

La verdad es que no he podido pensar mucho sobre la efectividad de este plugin ni tampoco tengo experiencia utilizándolo (de hecho los desarrolladores de The-Shaker estábamos algo preocupados sobre el vacío que teníamos en los helpers de la aplicación, pues no tienen tests). En principio y echando un pequeño vistazo a los helpers que tenemos, parece que sí que se van a poder testear todos utilizando este plugin.

Sin embargo, hay que notar dos cosas:

La primera es que si en tu helper has utilizado una variable de instancia (confiando en que en la vista donde vas a utilizar dicho helper, la variable ya esté instanciada y con valor), debes de asignar una variable de igual nombre antes de invocar al helper en el test. Por ejemplo:


def available_tabs
  tabs =  "<ul>\n"
  tabs << " <li id=\"tab_blog\">" + link_to(_("Blog"), :controller => 'posts',
           :action => 'index', :blog_nicetitle => @blog.nicetitle) + "</li>\n"
  tabs << "</ul>\n"
end

Y el test:


def test_avaible_tabs
  @blog = blogs(:el_blog_de_quentin)
  tabs = available_tabs
  assert_equal tabs, '....'
end

Y la segunda es más bien una sugerencia: muchas veces los helpers generan HTML, y todos sabemos lo coñazo que puede ser comprobar la estructura del mismo, y las bondades del assert_select. Pues bien, se puede hacer un hack no muy elegante, pero bastante práctico para poder utilizar assert_select con el valor obtenido por un helper. Basta con editar el fichero helper_testcase.rb e incluír la línea:


  @response   = ActionController::TestResponse.new

dentro de la función setup. Y ahora, con que asignemos el helper a @response.body ya lo tenemos. Veamos un ejemplo:


def test_avaible_tabs
  @blog = blogs(:el_blog_de_quentin)
  @response.body = available_tabs
  assert_select 'ul'
end

Es sucio, poco semántico, y muy DRY, porque lo tienes que asignar tras cada invocación al helper, pero parece que de momento no hay otra forma. ¿Alguien se atreve a redefinir?

servido por Fernando sin comentarios compártelo favorito


Sobre mí

Avatar de Fernando

in web we trust

Valencia y Madrid, España
ver perfil »
contacto »
Blog personal de Fernando Blat, desarrollador en The Cocktail, sobre programación web y Ruby on Rails. Puedes seguir a "mi otro yo" en blat.

Buscar

suscríbete

Selecciona el agregador que utilices para suscribirte a este blog (también puedes obtener la URL de los feeds):

¿Qué es esto?

Crea tu blog gratis en La Coctelera