13
Feb
09

nos mudamos

Después de ver que el blog iba tirando para delante (poco a poco…) pensamos darle un poco más de oficialidad al asunto, y de paso hacer algunas mejoras tanto visuales como técnicas al blog, por lo que finalmente nos mudarnos a otro blog alojado en nuestros propios servidores.

Para todos aquellos que nos sigan, a partir de ahora os esperamos en el nuevo blog técnico de 11870.com por lo que será necesario actualizar los feeds, delicious o lo que usemos.

Saludos y os esperamos en el nuevo blog.

31
Ene
09

Temporary tables y replicación a esclavos

El título de esta entrada debería ser más bien «Cómo perder tus servidores esclavos haciendo el tonto» y ahora veremos porqué.

Supongamos que estamos haciendo tareas de mantenimiento en la base de datos, supongamos que tenemos que hacer un UPDATE sobre algunas filas de determinada tabla (area mismo). Con la particularidad de que determinar sobre qué filas tenemos que hacer el UPDATE no lo podemos hacer con una única query y decidimos hacer uso de las temporary tables o tablas temporales de MySQL. Las tablas temporales son tablas MyISAM que sólo perduran durante sesión en la que estamos conectados (si nos desconectamos y conectamos de nuevo, hemos perdido la tabla), lo que las hace especialmente útiles para estas tareas de mantenimiento.

Así que creamos la tabla:

mysql> CREATE TEMPORARY TABLE idsChungos (id integer primary key);
Query OK, 0 rows affected (0.00 sec)
mysql>

Y ahora vamos insertando los ids de las filas que queremos tocar:

mysql> INSERT INTO idsChungos VALUES (SELECT id FROM area WHERE …);

mysql> INSERT INTO idsChungos VALUES (SELECT id FROM area WHERE …);

mysql> INSERT INTO idsChungos VALUES (SELECT id FROM area WHERE …);

Justo en este ejemplo no se ve la necesidad de utilizar una tabla para almacenar los ids, pero no podemos usar una subquery sobre la misma tabla sobre la que vamos a hacer el UPDATE (aplica también para DELETEs).

Y ahora que ya tenemos los ids:

mysql> UPDATE area SET boundaries = NULL WHERE id IN (SELECT id FROM idsChungos);
Query OK, 7263475 rows affected (0.00 sec)
mysql>

Y nos ha ido todo a la perfección, salvo porque 30 segundos después empiezan a saltar las alarmas de servidores MySQL esclavos perdidos. En ese momento es cuando dices: aaaahhhhh y te pegas una palmadita en la frente: las tablas temporales NO se replican a los servidores esclavos. Cuando el esclavo intenta hacer el último UPDATE dice: «la tabla area no sé ni lo que es» y se para la replicación. Pensándolo dos minutos, tiene todo el sentido del mundo.

Un diez oye, pero había que aprenderlo de alguna manera. Menos mal que podemos recuperar los esclavos en caliente.

18
Ene
09

Cómo crear un comando para ubiquity

Ubiquity es una extensión de Firefox desarrollada por Mozilla que permite ejecutar verbos o comandos al estilo de un lanzador de aplicaciones de escritorio como QuickSilver o Gnome-Do. Estos comandos son los que añaden funcionalidad a Ubiquity, y los que deben especificarse para realizar alguna acción. Se pueden programar sin necesidad de reiniciar el navegador en ningún momento, a diferencia de lo que ocurre cuando se programan extensiones para firefox, pudiendo probar los cambios inmediatamente. No hay más que utilizar el Command Editor que proporciona Ubiquity al que se puede acceder a través del comando help.

El esqueleto básico de un comando el el siguiente:

CmdUtils.CreateCommand({
     name: "11870",
     icon: "http://11870.com/favicon.ico",
     takes: {"search term": noun_arb_text},
     preview: function(pblock, search_term) {
          ...
     },
     execute: function(search_term) {
          ...
     }
})

CmdUtils es un espacio de nombres que proporciona Ubiquity y contiene todas las herramientas disponibles para crear un comando. Para ello deberemos ejecutar el método CreateCommand pasándole un hash que deberá contener los siguientes elementos:

  • name: Nombre del comando, se usará cuando se quiera ejecutar.
  • icon: Favicon del comando, aparecerá al lado del nombre cuando se ejecuta.
  • takes: Parámetros del comando. Es un hash parámetro, tipo de datos. Esto lo explicaremos más abajo.
  • preview: Método que se ejecutan mientras se están escribiendo los parámetros a el comando. Esta función recibe 2 parámetros. El primero es el nodo DOM donde debe insertarse el resultado, y el segundo es el parámetro que ha introducido el usuario, en nuestro caso un término de búsqueda.
  • execute: Método que se ejecuta cuando pulsamos enter sobre el comando. Sólo recibe el parámetro.

Otro punto interesante son los modificadores o modifiers que nos permiten recibir más parámetros en el comando especificando una palabra clave a partir de la cual vendrá el modificador. Por ejemplo, cuando un usuario escriba:

11870 restaurante en madrid

la palabra clave “en” identificará a madrid como un modificador y se recibirá en las funciones preview y execute como un nuevo parámetro. Para especificar un modificador en nuestro comando deberemos añadir la siguiente linea:

modifiers: {"en": noun_type_ooslocality},

donde “en” es la palabra clave y noun_type_ooslocality es el tipo de dato que se va a recibir. También deberemos añadir un nuevo parámetro a preview y execute donde recibir los modificadores. Este parámetro es un hash donde la clave es el modificador.

Estos tipos de datos también son personalizables y se pueden crear fácilmente siguiendo la siguiente estructura:

var noun_type_ooslocality = {
     _name: "localidad",
     suggest: function(text, html){
          ...
          return return [ CmdUtils.makeSugg(text, null, date) ];
     }
}

Cuándo el comando recibe como parámetro un tipo de datos lo primero que hace es ejecutar el método suggest, el cual devuelve una lista con todos los posibles valores para esa entrada. En nuestro caso hemos creado un noun_type llamado noun_type_ooslocality que, a través de la API de 11870, comprobará las localidades que disponemos que se corresponden con la entrada y se mostrará al usuario una lista para que pueda concretar su búsqueda.

Ubiquity también proporciona:

  • Geolocalización: Podemos obtener nuestra localización actual usando CmdUtils.getGeoLocation() para lo cual actualmente usa la API de MaxMind.
  • JavascriptTemplates: Que nos facilita la tarea de generar los preview, para lo cual usan TrimPath’s JavascriptTemplates.
  • JQuery: especialmente para las llamadas AJAX.

Y para acabar, os dejo aquí el comando 11870 para Ubiquity a modo de ejemplo, para que jueguéis con él todo lo que queráis 😀

11
Dic
08

Instalación desatendida de Java JRE/JDK en Ubuntu/Debian

¿Necesitas instalar java en 40 servidores? ¿Has creado un script y al lanzarlo has caído en que es inútil ya que debes aceptar la licencia de Sun para completar la instalación?

Tenemos la solución a tus problemas. Ejecuta el siguiente script antes de la instalación y se saltará el diálogo de conformidad con la licencia:

#!/bin/bash
. /usr/share/debconf/confmodule
db_version 2.0
db_capb backup
license=sun-dlj-v1-1
db_get shared/accepted-$license
if [ “$RET” = “true” ]; then
echo “$license license has already been accepted” >&2
exit 0
fi
db_set shared/accepted-$license true
exit 0

De nada.

21
Nov
08

pybuddy: hacking ambient devices (for fun & profit)

Aquellos que sigáis nuestro blog corporativo ya habréis visto a nuestro i-buddy tuneado. Ese post no tenía mucho detalle técnico, así que aquí vamos a dar alguna pista más.

En realidad todo empezó en El corte inglés. Sí, en serio. Un buen amigo y geek, luna, y un servidor estaban echando un vistazo a la sección de informática cuando nos encontramos con el i-buddy. Y nos dijimos: seguro que se puede hacer algo, si alguien no lo ha hecho ya. Una semana más tarde, teníamos un juguete nuevo cada uno y un código del que partir. El fruto del cacharreo se llama pybuddy: un demonio escrito en python que atiende a comandos enviados vía UDP por red.

Esto significa que una vez lanzado el demonio podemos controlar el buddy con comandos tal que así:

echo “DEMO” | nc -q0 -u localhost 8888

En principio, todos los comandos se han implementado con lo que el juego ahora viene de hacer combinaciones con ellos. Por ejemplo, en el vídeo explicativo usamos este archivo de comandos y lo lanzamos así:

cat commands | nc -q0 -u localhost 8888

Como se puede ver, el timing fue perfecto 😉

En la oficina, de vez en cuando a alguien le dar por juguetear con el bicho que está enchufado a mi ordenador y recibe peticiones de cualquier ordenador de la red local, con lo que suele estar bastante agitado. Pero también lo tenemos recibiendo información importante de lo que ocurre en nuestras máquinas en producción, con el script que se curró Pablo en un momento: access-log-client.

El procedimiento de instalación no está documentado por ahora, pero es bastante sencillo:

  1. Hacemos un checkout del proyecto: svn checkout http://pybuddy.googlecode.com/svn/trunk/ pybuddy-read-only
  2. Editamos el fichero de configuración pybuddy.cfg, si queremos cambiar alguno de los parámetros por defecto (tipo de buddy, usuario con el que correr, logging, etc).
  3. Lanzamos el demonio, como root (luego cambiará de usuario según lo configurado): sudo src/pybuddy-daemon.py pybuddy.cfg
$ sudo src/pybuddy-daemon.py
2008-11-21 12:15:07,662 INFO Read config file: /usr/local/etc/pybuddy.cfg
2008-11-21 12:15:07,662 INFO Starting search…
2008-11-21 12:15:07,808 INFO ibuddy found! vend: 4400 prod: 1
2008-11-21 12:15:07,808 INFO endpoint
2008-11-21 12:15:08,424 INFO Starting daemon…
$

A partir de este momento, el buddy está listo para recibir comandos. A ver quién es el primero que se curra un plugin para Pidgin, gajim o similar.

Happy hacking.

16
Oct
08

Manteniendo el orden de resultados SQL según un ‘in’

Si queremos obtener resultados ordenados con una query SQL y la condición del where es un IN, tenemos un problema. Pongamos que tenemos una query como la siguiente:

mysql> select * from category where status in (‘inactive’, ‘active’, ‘mutant’);

Con esto pretendemos obtener todos los campos de todas las filas de category, estando éstas agrupadas y ordenadas en el mismo orden en el que las hemos puesto en el IN. Si añadimos un GROUP BY nos aseguramos de que se agrupan, pero no de que se respete el orden que hemos puesto en la condición.

El orden del resultado será indeterminado (yo pensaba que iba a ser por id en un principio, pero no). La cosa tenía pinta de que íbamos a tener que realizar la ordenación a posteriori en la aplicación, aunque ha resultado ser que no. En MySQL podemos hacer lo siguiente:

mysql> select * from category where status in (‘inactive’, ‘active’, ‘mutant’) order by field(status,’inactive’, ‘active’, ‘mutant’) ;

Que funciona gracias a la función field de MySQL. Y problema resuelto.

07
Oct
08

Recuperando un servidor esclavo de MySQL en caliente

A veces, shit happens… lo sabemos todos. Y pasa en todos los lados y en el momento más inoportuno. Hoy he descubierto que podía recuperar un esclavo de MySQL en caliente porque utilizamos un engine transaccional (InnoDB).

maestro$ mysqldump --single-transaction --databases db1 db2 --master-data=1 -u root -R -p > /tmp/dump.sql

scp de rigor a la máquina destino, stop slave, carga, start slave:

maestro$ scp /tmp/dump.sql esclavo:/tmp
esclavo$ echo “stop slave;” | mysql -u root -p
Enter password:
esclavo$ mysql -u root -p < /tmp/dump
esclavo$ echo “start slave;” | mysql -u root -p

La iluminación ha venido por parte de High Performance MySQL: Optimization, Backups, Replication, and More, un hayquetener donde los haya y libro que recomiendo prácticamente a cualquiera que se tenga que pelear con este servidor de base de datos.

En el libro, además, usan pipes:

maestro$ mysqldump --single-transaction --databases db1 db2 --master-data=1 -u root -R -p | mysql -u root -h esclavo -p