Author Archive for

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.

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
20
May
08

Simplificación de polígonos con el algoritmo Douglas-Peucker

Hace bastante tiempo ya nos encontramos con un problema del que no teníamos ni idea: nuestra fuente de datos nos pasaba datos de áreas geográficas (más concreto, los contornos) como listas de miles de puntos. En aquel entonces, la API de Google Maps dejaba frito el navegador cuando se intentaba dibujar un polígono con tal número de vértices (hoy ha mejorado y en vez de pintar un polígono vía Javascript, la API te tira una imagen a partir de la segunda petición).

¿Qué tuvimos que hacer? Reducir el número de puntos que definían cada uno de los polígonos intentando ser lo más fieles posible a la forma original porque pintar una provincia cualquiera como un cuadrado no es solución. No sólo teníamos datos de provincias: también teníamos de distritos y barrios. Y eso hacía que esta simplificación de polígonos fuera algo bastante intratable de hacer a mano. Fue así como descubrimos que existía una forma de hacerlo automáticamente y cuyo algoritmo fue desarrollado por David Douglas & Thomas Peucker y es conocido como Douglas-Peucker polyline simplification algorythm.

Partimos de un trozo de código en Pascal que encontramos en algún lugar y lo portamos a Python, que era el lenguaje con el que hicimos todo el script de importación de datos. Se quedó pendiente de publicación el asunto, ya que en su día nadie había portado dicho código a Python (ni Ruby ni Perl ni ningún lenguaje de script medianamente actual). El caso es que eso ha sido hasta hace un par de semanas Schuyler Erle publicó su versión en Mapping Hacks. Su código es mucho más bonito que el nuestro, así que nos ahorraremos el attachment 😉

23
Feb
08

Sería una buena opción, si fuera posible

Podría parecer más fácil definir una rutina almacenada e invocarla desde el disparador utilizando una simple sentencia CALL. Esto sería ventajoso también si se deseara invocar la misma rutina desde distintos disparadores. Pero no se puede.

De la documentación de triggers en MySQL.

¿Quieres un caramelo? Espero que no, porque no tengo.