MarcosBL

Aprendiz de todo, maestro de nada

Twitterposter on Steroids

Hay un nuevo servicio en la ciudad, nena

Twitterposter on steroids

El 31 de Octubre salía a la luz TwitterPoster, un mashup basado en Twitter estilo «Top» donde podemos ver, de forma gráfica, a los «Twitteros» con más fans. La idea en sí me gustó cuando la vi presentada en el blog de Raúl, era simple, era fresca, y presentaba una visión muy clara del panorama Twitter. Luego vinieron sectorización por países, la ampliación de los mismos, continentes, etc… como comenta Jose Luis en su blog, todas ellas ampliaciones interesantes y agradables.

¡¡¡ CUIDADO !!!

Lo que sigue a continuación es un auténtico y original TOCHO SEMITÉCNICO DEL COPÓN, si no estás interesado en el desarrollo web es mejor que saltes directamente a la demo o te vayas a rascarla a otra parte.

Una de las primeras cosas que se me ocurrieron al ver la interface y la posibilidad de búsqueda, fue la necesidad de un «enlace permanente» para cada usuario, donde apareciese su «thumb» resaltado sobre los demás en la carga directa, sin necesidad de esperar la (desesperante) carga de 608 imágenes y luego esperar de nuevo al filtrado de imágenes por javascript. Lo comenté a Raúl en uno de sus post sobre twitterposter, y tras un breve intercambio de comentarios rápidos, me sorprendió gratamente el post de Luis Sacristán en el que comentaban que ya habían implementado el sistema de forma funcional… ¡ eso es velocidad de reacción !

Cuando llega la resaca o… ¿ quien es esta que está en mi cama ?

Alta VelocidadUna vez pasada la excitación de la novedad, y aunque el sistema me encanta, la lentitud de carga del servicio resulta ser salvaje, brutal, incomensurable… tiempos de carga completa del entorno de alrededor de 2/3 minutos son, para mi gusto, absolutamente inaceptables.

Si encima añadimos que, aparte de la carga de red de descargar la salvajada de 608 imágenes, tenemos que sumar la carga que supone para el navegador la gestión de efectos de mootools sobre 608 enlaces, con sus eventos, etc… acabamos con una página que no sólo tarda mucho en cargar, sino que además deja el navegador «tonto», lanzando las instancias del navegador a consumos de más de 200 Mb para gestionar una sóla ventana y haciéndo que casi no respondan.

Twitterposter

Girl… you’ll be a woman… soon

Obviamente, como todo producto «beta», no se puede esperar que lo hayan diseñado pensando en optimización, es lógico y natural que estas cosas pasen y en este caso concreto, tampoco parece que tenga mucha otra solución: Hay que mostrar 608 thumbs, asi que hay que cargar 608 imágenes, hay que añadir eventos para los tooltips de 608 enlaces, con la consecuente carga del navegador, hay que realizar búsquedas en el DOM del navegador, que siempre son lentas…. no ? Pues… si a casi todo… y no al mismo tiempo :)

Embadurnado de harina… con las manos en la masa

Con la idea en mente de mejorar este servicio colaborando en lo que pudiera, crucé unas cuantas bromas/puyas amistosas con la gente de Bitácoras.com en el blog de Raúl, y me jugué unas cervecitas a que sería capaz de reducir ese tiempo de carga significativamente. Como a diferencia de los desarrolladores, yo tengo la suerte de no tener prisa y poder ver el asunto desde fuera, sin apretones, a ver qué se nos ocurre para mejorar este servicio.

En el proceso de «análisis» me he encontrado varios caballos de batalla:

  • Ralentización de carga por 608 Imágenes Pequeñas, si, alojadas externamente, si (por cuenta de Twitter en Amazon S3), pero 608 al fin y al cabo. El primer problema no es ya lo que ocupen esas 608 imágenes, sino la barbaridad de conexiones independientes que tiene que abrir el navegador para descargarlas una a una. Hay que solucionar esto pero ya, en primer lugar.

    Alta Velocidad

  • Ralentización de carga por Twitter Callback Mi afirmación de que Twitter va lento no es algo que vaya a sorprender a nadie, de eso estoy seguro. Twitterposter hace una llamada a la API de Twitter para recoger un callback cuya única función es mostrar el último mensaje del usuario poster, y que tarda una media de 5 segundos en terminarse, «colgando» el navegador entre tanto. Para más inri, en Internet Explorer, este JS externo… no funciona en absoluto. Como además no creo que dicho usuario esté escribiendo mensajes cada 10 segundos o menos, marcaré este punto como muy optimizable.

    Twitterposter Callback

  • Ralentización de carga por Google Analytics Google Analytics es un servicio de Google que nos permite generar estadísticas de nuestros sitios web por medio de la inclusión de un fichero javascript, http://www.google-analytics.com/urchin.js. Me parece una elección más que acertada para las estadíticas de Twitterposter, pero tiene un problema: el urchin.js es un fichero no demasiado grande, pero si hiper-mega-saturado, con millones de enlaces desde sitios web de todo el mundo, lo que hace que la carga del mismo en ocasiones sea más lenta que el caballo del malo, interfiriendo en ocasiones con la correcta renderización de la página.

    Twitter Poster Urchin

  • Ralentización del navegador en la creación de Tooltips
  • El generar un Tooltip «adornado» para cada imagen fuerza al navegador a buscar en el DOM los elementos «a», hacer esto y encontrar 608 candidatos es doloroso para el navegador, y mucho más si la selección, paso crítico del proceso, es lenta. Mootools deja la página prácticamente tonta en los primeros instantes de carga de la misma, buscando los elementos. Hay que acelerar esto.

    Mootools vs jQuery

  • Ralentización del navegador en la búsqueda La caja de búsqueda de thumbs instalada en la parte superior derecha permite resaltar en color los thumbs coincidentes con nuestro término y hacer semitransparentes los no coincidentes, permitiendo asi una búsqueda visual de thumbs. El problema es que la caja apenas responde, escribir en ella es mareante, ya que la búsqueda salta contínuamente, sin dejarnos terminar de teclear, y encima dichas búsquedas son lentas de cuidado, de nuevo por el mismo motivo que el punto anterior, buscar determinados elementos entre una colección tan grande es muy intenso y lento para la CPU. Además es poco intuitivo, ya que en esos períodos de «cuelgue» el usuario no sabe qué está pasando, no hay indicador visual de «Estoy buscando, espérate majo». Hay que hacer que la respuesta sea más rápida y la caja más inteligente, dejando más tiempo entre tecla y tecla para la búsqueda, y meter algún tipo de indicador visual.

    Búqueda de thumbs en Twitterposter

  • Pequeños Bugs varios, entre los que mencionaré:
    • El último status del usuario poster, abajo a la derecha, no funciona ni en IE6 ni en IE7, y falla a menudo al dar timeout Twitter, dejándonos con un hueco blanco que pone «Cargando…»
    • En el buscador, tras buscar un thumb, si seleccionamos el texto y lo borramos con «Backspace», la búsqueda se mantiene, cuando lo que esperamos es que se muestren todos los thumbs de nuevo.
    • La búsuqeda de thumbs por url no funciona ni en IE6 ni en IE7.
    • En la búsqueda por url, recien implementada (imagino que esto será un parche temporal) se define el atributo de transparencia a golpe de meter style=» una vez por imagen, muy poco eficiente, engorda mucho el html, mejor aplicar una clase a las imágenes contenidas en la tabla/capa, u otra solución que luego aplicaré, basada en JS
    • Se utiliza una función JS para escribir el tiempo pasado desde el último status del usuario poster. La idea es original, pero no soportaría un Twitterposter «en tu idioma» (traducciones locales del entorno según país que estés visitando, o idioma del navegador, etc), ya que los textos «minutes ago», «days ago», etc… están «hardcoded» (metidos a mano) en el JS. Además, depende de que el callback a twitter funcione bien, y añade código innecesario a nuestro fichero JS.

Vamos a ver a continuación las soluciones, explicando un poco una a una.

 

Ralentización de carga por 608 Imágenes

PHP Powered
El primer «caballo de batalla» es en realidad la que parece la mula más tozuda, y la que más sencilla resulta de resolver a la hora de la verdad.

Mi solución fue escribir un rápido script en php que se ejecuta cada 10 minutos (ok, ya no es Twitterposter a tiempo real, pero la diferencia es nimia y las ventajas, como vereis, tremendas), este script coge las urls de las 608 imágenes (regexp), se las baja todas a una carpeta (via php , aqui incluso podríamos no bajarnos las que no hayan cambiado, hacer caché, etc), las combina todas en una grandota (utilizando la libreria GD creamos combined-es.jpg para España, etc…) y la guarda en la carpeta de imágenes.

Acto seguido creamos una imagen transparente de 1×1 pixels «t.gif» y modificamos todo el HTML de la página para que en lugar de cargar los thumbs de Twitter/Amazon, cargue sólo el «t.gif». Y aplicamos la magia… ponemos el «combined-es.jpg» como fondo de la tabla de thumbs.

¿ Qué cambia esto ?

Del lado malo nada… nuestro html es semántico, sigue habiendo un elemento por thumb para generar enlaces y tooltips… todo sigue igual.

Del lado bueno… de cargar 608 imágenes pequeñas (Unos 600 Kb) pasamos a cargar 2, una de un pixel (ridiculamente diminuta) y una grandota (en mis test, unos 100Kb), manteniendo la visualización de página exactamente igual Además reducimos (en mi ejemplo) 45Kbs de código HTML al reducir la longitud de las url de imagen a cargar, ya no hay más largas urls de Amazon, basta con «img/t.gif».

Un ahorro de 606 conexiones y unos 545 Kb de carga.

Sólo con esto, la carga de la página completa salta de 2/3 minutos a unos 2/3 segundos en mi cable de 6 Mb, una mejora instantánea del 6000%

 

Ralentización de carga por Twitter Callback

Imagino que la inmediatez de mostrar el último mensaje del usuario poster es lo que ha llevado a los desarrolladores a utilizar el sistema de llamada e inclusión de javascript remoto en Twitterposter. Sin embargo, como comentaba antes, esto no funcionaba en IE6 ni IE7, y ralentizaba mucho la carga (alrededor de una media de 4 segundos) al tener que esperar a Twitter para mostrar dicha información. Creo que sería mucho más eficiente escribir esta información en el propio HTML, por varios motivos:

  • Accesibilidad El texto sería visible con javascript deshabilitado, por lectores en pantalla, etc… y además, desde el punto de vista SEO, sería visible por los buscadores.
  • Velocidad: La carga sería instantánea
  • Localización Siguiendo con la idea de «Twitterposter en tu idioma», el generar el HTML con el mensaje incluido permite que se pueda incluir en el idioma nativo del visitante, un puntito a favor, en lugar de mostrar un mensaje en inglés tanto a usuarios de EEUU, como de Francia, China o Teruel (existe!)

Así que en mi versión, ese texto va como fijo en el HTML, un pequeño sacrificio que nos permite ahorrar 4 segundos más y localizar los mensajes.

Twitterposter en Españolo !

 

Ralentización de carga por Google Analytics

Esto es lo más sencillo, porque ya había implementado anteriormente un método para acelerar la carga del fichero remoto de Analytics, visto no hace mucho en AskApache (Speed Up Google Analytics with urchin.js).

La idea es bien simple: Al tratarse de un simple fichero .js, no tenemos necesidad de cargarlo de los servidores de Google, asi que lo descargaremos a nuestro propio servidor y lo enlazaremos de alli. Esto permitirá al navegador no tener que abrir una conexión más a Google para accedera este fichero, ya que puede utilizar una de las ya abiertas (Apache KeepAlive para saber más) y descargarlo rápidamente.

Sería en parte jugar con ventaja, asi que no voy a contabilizar este tiempo como ahorro, ya que si nuestro servidor es lento será peor el remedio que la enfermedad, pero al menos con el fichero en nuestro servidor, ahorramos una conexión y nos aseguramos de la disponibilidad del archivo, sin depender de terceros. Como en todo, hay una pequeña pega: si google cambia ese fichero .js (y lo hace de vez en cuando para añadir funcionalidades), deberemos bajarlo de nuevo o nuestras estadísticas podrían no funcionar. Para solucionar esto, en el mismo fichero metido en cron en que nos bajábamos los thumbs de twitter, podemos bajarnos tambien dicho fichero .js, teniendo asi siempre la última versión perfectamente actualizada y cacheada. De hecho, en el post de AskApache antes referenciado, nos ofrecen un script en bash ya escrito que hace exactamente esto, asi que no entraré en más detalles por esta parte (si aún queda alguien leyendo esto a estas alturas, al menos tengo un compañero de fatigas, porque empiezo a cansarme de escribir).

Speedup Google Anaytics

 

Ralentización del navegador en la creación de Tooltips

Doy por terminada la optimización de la página, aunque podría ganarse muchísimo cacheando del lado servidor y cliente, enviando tanto los ficheros HTML del índice como los de javascript comprimidos con gzip, etc, etc.. pero eso ya es optimización del lado del servidor y requeriría un tiempo del que no dispongo. Me voy a centrar ahora en la velocidad de «ejecución» de la página, tooltips, búsquedas, efectos, etc…

He probado (hace tiempo) Mootools como libreria javascript, asi como otras varias docenas más, y al final (y el tiempo creo que me ha convencido de haber hecho lo correcto) me decidí por jQuery. ¿ Por qué ? Diría que por su sencillez y lógica, pero en eso es muy similar a Mootools, mi segunda preferencia, asi que me ceñiré a nuestro caso concreto: No hay niguna tan rápida con los selectores (quizá Ext, pero aún no la he testeado lo suficiente), y eso es muy importante en Twitterposter, donde hay que lidiar con 608 elementos y buscar y parsear en varias ocasiones a través de todos ellos. El tamaño mayor del fichero (unos 20Kb más en jQuery) se verá sobradamente justificado a continuación.

Tras portar el código de Twitterposter a jQuery, unas cuantas pruebas con los selectores para cambiar opacidad, buscar en los thumbs, etc… me confirmaron que jQuery realizaba todos los procesos al menos un 50% más rápido, y con un par de ajustes (no me gustaban los Tooltips como estaban, es en el único aspecto de diseño que no he sido fiel al Twitterposter original, creando los mios propios) a medida de jQuery , aún se ganaron unos milisegundos más. En este momento la «aplicación javascript» seguía siendo algo lenta, pero respondía mucho, muchísimo mejor que antes.

Twitterposter Tooltip Personalizado

Para mi, Jquery 1 – MooTools 0
En este partido concreto, no quiero flames de frameworks javascript :)

 

Ralentización del navegador en la búsqueda

Este problema es heredado del anterior, de nuevo jQuery hace un mejor trabajo a la hora de buscar, «opacar» y «translucir» los thumbs buscados, asi que esta mejora tampoco tiene mucha más explicación:

  • He reescrito la función de búsqueda utilizando jQuery, con buenas mejoras de velocidad
  • He hecho la caja más cómoda alargando la espera antes de buscar, esto hace que haya una espera de medio segundo desde que pulsas la última tecla hasta que se realiza la búsqueda, pero por contra el navegador responde muchísimo mejor.
  • He corregido el bug por el que, tras realizar una búsqueda y borrar el término buscado, no se volvian a ver todos los thumbs
  • He añadido en el HTML un indicador visual «Buscando…» que se inicia oculto y sólo se muestra durante la búsqueda, para ocultarse de nuevo después. Esto permite no sólo una indicación visual de lo que está pasando, para evitar desesperaciones del usuario, sino que además, al ir incrustado en el HTML, es una simple cadena de texto traducible a múltiples idiomas (de nuevo, la localización, al fin y al cabo es un proyecto con vocación internacional, ¿ no ?)

 

Los pequeños bugs

  • El último status del usuario poster, abajo a la derecha, no funciona ni en IE6 ni en IE7, y falla a menudo al dar timeout Twitter, dejándonos con un hueco blanco que pone “Cargando…â€? Resuelto al incrustar el texto desde PHP, aparte de recordar las ventajas de localización de idioma y velocidad de carga, aqui podemos añadir la compatibilidad entre navegadores.
  • En el buscador, tras buscar un thumb, si seleccionamos el texto y lo borramos con “Backspaceâ€?, la búsqueda se mantiene, cuando lo que esperamos es que se muestren todos los thumbs de nuevo. Corregido al reescribir la función con jQuery
  • Exceso de carga HTML en la búsqueda por url, recien implementada Tan simple como comprobar si hay alguna búsqueda que hacer en base a la url, y en caso afirmativo, lanzar la función JS que se encarga de ella, no he tenido que tocar el HTML en absoluto, ahorrando peso al código.
  • Se utiliza una función JS para escribir el tiempo pasado desde el último status del usuario poster. Al ir incrustado en el HTML, esta función ya no es necesaria, la elimino, menos peso y localización en varios idiomas para este texto.

 

Hablar es sencillo, los hombres de verdad ponen DEMOS

Twitterposter Original:

Twitterposter on steroids

Mi Twitterposter on Steroids:

 

Y creo que esto es todo, me he dejado muchas posibles optimizaciones en el tintero, del lado del servidor, del css (ni siquiera he borrado las clases que ya no se usan), he pasado olímpicamente de comprobar la visualización más que en Firefox , IE6, IE7 y Opera, etc… pero como ejercicio (al menos para mi) ha sido interesante… lo peor de todo ha sido escribir este ladrillo con el proceso y razonamiento… sinceramente, he perdido más del doble de tiempo en redactar este «estudio» que en programarlo… de lo que empiezo a deducir que hablo mejor en código que en «humano»… me lo haré mirar O_o

Disclaimer: REPITO, es muy facil abrir la boca para opinar sobre el trabajo de otros, como estoy haciendo yo. La parte que yo he hecho, retocar, es la fácil, lo dificil es idear y llevar a cabo; esto no es una crítica a cómo está hecho el servicio, ni mucho menos, sólo pretende ayudar a mejorar la experiencia del usuario final, en caso de que alguna de estas técnicas parezcan interesantes a los desarrolladores, algo que no tiene por qué ser asi ya que implica varios cambios de código y comportamiento y filosofía que no tienen porqué satisfacer sus gustos/necesidades concretas.

Sólo añadir que además, no se ha dañado a ningún animal en la realización de este texto, salvo que contemos como fauna un paquete de Chesterfield y dos tazas de Cafés Candelas. Un saludo a todos los que hayan llegado al final de este tocho, os habeis ganado el cielo.

3 comentarios en “Twitterposter on Steroids

Comments are closed.