lunes, 29 de septiembre de 2008

Transacciones en MySQL

Las operaciones de escritura en MySQL (inserciones, actualizaciones y borrados) se realizan por defecto en modo autocommit, es decir, se actualizan automáticamente en la base de datos.

Este comportamiento es bueno para la mayoría de aplicaciones ya que los cambios se propagan muy rápido, sin embargo no son todo ventajas.

Si queremos realizar una operación compleja en la que actualizamos varios campos o tablas y falla alguna de las operaciones, la base de datos puede quedar en un estado inconsistente o no deseado.

En este caso resultaría útil poder deshacer los cambios, decirle de alguna forma que todas esas operaciones van 'unidas', o se ejecutan todas, o ninguna.

Existe una forma de hacer lo anterior, crear una transacción, los cambios se guardarán en la base de datos si todo ha ido bien, si no, volveremos al estado anterior.

MySQL soporta transacciones, pero no con todos los motores, MyISAM por ejemplo no lo soporta, InnoDB sí.

Un ejemplo de como funcionan las transacciones.

mysql> create table tabla (name char(20), unique(name)) engine = InnoDB;
Query OK, 0 rows affected (0.01 sec)

mysql> insert into tabla set name = 'primero';
Query OK, 1 row affected (0.01 sec)

mysql> insert into tabla set name = 'segundo';
Query OK, 1 row affected (0.01 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into tabla set name = 'tercero';
Query OK, 1 row affected (0.00 sec)

mysql> select * from tabla;
+---------+
| name |
+---------+
| primero |
| segundo |
| tercero |
+---------+
3 rows in set (0.00 sec)

mysql> insert into tabla set name = 'segundo';
ERROR 1062 (23000): Duplicate entry 'segundo' for key 1
mysql> rollback;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from tabla;
+---------+
| name |
+---------+
| primero |
| segundo |
+---------+
2 rows in set (0.00 sec)

Como se puede ver al hacer rollback volvemos al estado anterior de la base de datos, si hubiésemos querido guardar los cambios tendríamos que haber ejecutado commit.

Durante la transacción los resultados que se muestran están en memoria, pero no físicamente guardados en la base de datos, si abrimos otra sesión de mysql esos cambios no los veremos hasta que se realice un commit.

domingo, 28 de septiembre de 2008

Lector RSS en ruby

Para crear un lector RSS tan solo es necesario crear una conexión http hasta el sitio que queramos suscribirnos y después parsear el archivo xml que nos devuelve el servidor.

En ruby se puede hacer todo esto en unas pocas líneas.

#!/usr/bin/ruby
require 'net/http'
require 'rexml/document'

http = Net::HTTP.new 'servidor.com'
http.start

response = http.get('/path_to_rss')
xml = REXML::Document.new(response.body)
xml.each_element('rss/channel/item') do |elem|
puts elem.elements['title'].text
puts elem.elements['link'].text
puts "----------------"
end

jueves, 25 de septiembre de 2008

Capturar y redirigir tráfico con netcat

Cuando estamos comprobando el estado de la red o depurando el funcionamiento de un protocolo viene muy bien el comando netcat.

Una redirección de un puerto a otro con netcat se hace de la siguiente forma.

nc -l -p 5000 | nc localhost 5001

De esa forma se reenvía lo que llegue al puerto 5000 hasta el 5001.

Si agregamos el parámetro -o además podemos registrar el tráfico saliente.

nc -l -p 5000 -o log.txt | nc localhost 5001

La respuesta saldrá por consola, si tenemos una aplicación que se conecta a un host remoto y queremos saber que envía y que recibe de ese host lo podemos hacer de la siguiente forma.

iptables -t nat -A OUTPUT -p tcp -d host_remoto -j DNAT --to-destination 127.0.0.1:5000

nc -l -p 5000 -o log.txt | nc host_remoto puerto_remoto

Con esto redirigiremos todo el tráfico saliente a host_remoto a nuestro puerto 5000, guardaremos la petición en log.txt y volveremos a enviarlo a host_remoto, la respuesta a la petición nos aparecerá por consola.

domingo, 21 de septiembre de 2008

Guardar datos comprimidos en base de datos con rails

En ciertas ocasiones es posible que queramos tratar los datos que vienen de una aplicación rails antes de guardarlos en base de datos. Para ello podemos sobrecargar los métodos nombre_parametro y nombre_parametro= en el modelo correspondiente.

Si, por ejemplo, tenemos un campo de la base de datos que preveemos que ocupe mucho espacio y no va a leerse frecuentemente se puede comprimir de la siguiente forma.

class Post < ActiveRecord::Base

def text
Zlib::Inflate.inflate(read_attribute(:text))
end

def text=(new_text)
old_text=read_attribute(:text)
new_compress_text=Zlib::Deflate.deflate(new_text)
write_attribute(:text, new_compress_text) if old_text!=new_compress_text
end
end

Para comprimir/descomprimir los datos se usa la librería zlib así que es necesario hacer require 'zlib'.

Además es necesario que el campo de la base de datos esté marcado como binario.

sábado, 20 de septiembre de 2008

Decoradores en python

Para entender fácilmente los que son los decoradores en python podemos pensar que son una especie de filtros que se ejecutan antes de la llamada a una función.

Realmente un decorador se ejecuta antes de la función decorada (realizando cualquier tipo de acción) y devuelve la función a ejecutar (posiblemente la función decorada). Es más fácil de entender con un ejemplo.

#!/usr/bin/python

def filtro(f):
def permiso_denegado(*args):
print '%s no tiene permisos para realizar esa accion' % args[0]

def comprobacion(*args):
if args[0] == 'admin':
return f(*args)
else:
return permiso_denegado(*args)

return comprobacion


@filtro
def accion(usuario):
print 'Accion realizada por %s' % usuario

accion('guest')

En este caso se intenta realizar una acción pero si el usuario pasado como parámetro no es 'admin' no puede realizarla, en lugar de devolver una referencia a la función original, se devuelve la función de permiso denegado, filtro es el decorador.

También existe la posibilidad de combinar decoradores, lo importante es tener en cuenta cuando se programa un decorador que no tiene porqué ser el único (cuidado lo que se hace con los parámetros) y el orden, en el ejemplo debug(filtro(debug(accion))).

#!/usr/bin/python

_MODO_DEBUG = True

def debug(f):
if _MODO_DEBUG:
print 'Se ejecuta la funcion: %s' % f.__name__
return f

def filtro(f):
def permiso_denegado(*args):
print '%s no tiene permisos para realizar esa accion' % args[0]

def comprobacion(*args):
if args[0] == 'admin':
return f(*args)
else:
return permiso_denegado(*args)

return comprobacion

@debug
@filtro
@debug
def accion(usuario):
print 'Accion realizada por %s' % usuario

accion('guest')

Medir el rendimiento de una aplicación web

Es importante medir el rendimiento de todas las aplicaciones que creamos, en el caso de las aplicaciones web podemos usar una herramienta de hp llamada httperf.

Para ejecutarlo de la forma más sencilla se puede hacer.

httperf --server servidor_web --port puerto --num-conns numero_conexiones

servidor_web y puerto apuntan a la aplicación web que queramos probar y numero_conexiones es el número de intentos totales, entre 200 y 1000 suelen dar unos buenos resultados sin tests demasiado largos.

Si queremos probar alguna página que no sea el index se puede hacer incluyendo el parámetro --uri direccion.

Si la página que queremos probar necesita de registro también se puede solucionar con esta herramienta, primero necesitamos loguearnos con el navegador, observar la cookie que se nos ha creado y entonces añadir el parámetro --add-header='Cookie: nombre=valor', donde nombre y valor son los que hemos obtenido de la cookie del navegador.

miércoles, 17 de septiembre de 2008

Seleccionar parte de una imagen con javascript

En las redes sociales que permite etiquetar imágenes se suele elegir una sección de la imagen que representa la parte ocupada por cada usuario.

El problema es que a la mayoría (facebook, tuenti...) solo nos dejan seleccionar una sección cuadrada fija y muchas veces no se corresponde con lo que queremos.

Con jQuery y Jcrop podemos realizar selecciones más precisas de la siguiente manera.

<html>
<head>
<script src="jquery.pack.js"></script>
<script src="jquery.Jcrop.pack.js"></script>
<link rel="stylesheet" href="/css/jquery.Jcrop.css" type="text/css" />

<script language="Javascript">
jQuery(function(){ jQuery('#cropbox').Jcrop(); });
jQuery('#cropbox').Jcrop({
aspectRatio: 1,
onSelect: updateCoords
});
function updateCoords(c){
jQuery('#x').val(c.x);
jQuery('#y').val(c.y);
jQuery('#w').val(c.w);
jQuery('#h').val(c.h);
};
</script>
</head>

<body>
<img src="flowers.jpg" id="cropbox" />
</body>
</html>

En c.x y c.y tendremos las coordenadas dónde comienza la selección y en c.w y c.h el ancho y el alto.

Una vez que sabemos exactamente la selección del usuario se puede recortar la imagen o etiquetar cada zona.

domingo, 14 de septiembre de 2008

Implementación caché para minimizar cálculos

Una de las reglas de optimización más importantes es la de no repetir cálculos innecesariamente.

Guardando resultados intermedios y reutilizándolos cuando sea posible se mejora el rendimiento espectacularmente, en el ejemplo se consigue una ganancia casi lineal.

La clase NoCache sería la forma tradicional de implementar esta clase, a continuación la clase Cache optimizada para lecturas sucesivas sin repetir el cálculo si no es necesario.

#!/usr/bin/python

class NoCache:
def __init__(self):
self.lista = range(1,900000)

def getLista(self):
return self.lista

def setLista(self,value):
lista.append(self.value)

def getMedia(self):
return reduce((lambda n,m:n+m),self.lista) / len(self.lista)


class Cache:
def __init__(self):
self.lista = range(1,900000)
self.__actualizado = False

def getLista(self):
return self.lista

def setLista(self,value):
self.__actualizado = False
lista.append(self.value)

def getMedia(self):
if self.__actualizado:
return self.__sumaCache
else:
self.__sumaCache = reduce((lambda n,m:n+m),self.lista) / len(self.lista)
self.__actualizado = True
return self.__sumaCache


n = NoCache()
for i in range(5):
print n.getMedia()


c = Cache()
for i in range(5):
print c.getMedia()

El mismo ejemplo en ruby.

#!/usr/bin/ruby

class NoCache
def initialize
@lista = Array.new(900000){|i| i+1}
end

def getLista
return @lista
end

def setLista(value)
@lista << value
end

def getMedia
@lista.inject{|sum, n| sum+n} / @lista.size
end
end

class Cache
def initialize
@lista = Array.new(900000){|i| i+1}
@actualizado = false
end

def getLista
return @lista
end

def setLista(value)
@actualizado = false
@lista << value
end

def getMedia
if @actualizado
@sumaCache
else
@sumaCache = @lista.inject{|sum, n| sum+n} / @lista.size
@actualizado = true
@sumaCache
end
end
end

n = NoCache.new
5.times{puts n.getMedia}

c = Cache.new
5.times{puts c.getMedia}

Otro resultado inesperado en este ejemplo es la gran diferencia de velocidad entre estos dos lenguajes.

En python en mi máquina este ejemplo tarda en ejecutarse 7 segundos, en ruby 1 minuto y 28 segundos. Para este ejemplo python es ¡12 veces más rápido que ruby!.

sábado, 13 de septiembre de 2008

Variables de solo lectura en python

Hay algunos tipos de variables, como pueden ser variables de estado, que no interesa que se modifiquen desde fuera de los métodos de una clase, aunque sí que se pueda leer su valor.

Al intentar modificar el valor de una variable de un objeto se llama a la función __setattr__, si sobrescribimos esta función se puede variar el comportamiendo habitual. Lo mismo podemos hacer con __delattr__.

Así se podría mantener una distinción en una clase entre variables de solo lectura y lectura/escritura.

#!/usr/bin/python

class Clase:
__onlyReadVars=['x', 'y']

def __init__(self):
self.__dict__['x'] = 8
self.__dict__['y'] = 1
self.a = 2

def __setattr__(self, nombre, valor):
if nombre in self.__onlyReadVars:
raise Exception('No se puede sobrescribir %s' % nombre)
else:
self.__dict__[nombre] = valor

def __delattr__(self, nombre):
if nombre in self.__onlyReadVars:
raise Exception('No se puede eliminar %s' % nombre)
else:
del self.__dict__[nombre]

De esta forma si intentamos eliminar o sobrescribir alguna de las variables de solo lectura se lanzará una excepción.

Aun así, hay una manera de saltarse el filtro si hacemos lo siguiente.

c = Clase()
c.__dict__['x'] = 30


__dict__ es un diccionario con todas las variables del objeto y sus valores, al modificarlo no se llama a la función __setattr__, __getattr__ ni __delattr__.

jueves, 11 de septiembre de 2008

Trucos para bash

Hay ciertas situaciones al trabajar en consola que se hacen un poco pesadas y se pueden evitar con algunas combinaciones de teclas disponibles en bash.

Cuando necesitamos ir a un directorio y volver al que estábamos anteriormente se puede ejecutar.

cd -

Para ver todas las variables definidas en la shel simplemente escribiendo $ y pulsando dos veces al tabulador deberían aparecer.

Cuando ejecutamos un comando muy largo repetidas veces se puede hacer una búsqueda incremental en el historial pulsando Ctrl+R.

Una de las cosas más molestas de trabajar con consola es editar comandos muy largos, se puede hacer fácilmente con estos atajos de teclado.

Ctrl+A: pone el cursor al principio de la línea
Ctrl+E: pone el cursor al final de la línea
Ctrl+K: borra desde el cursor hasta el final de la línea
Ctrl+U: borra desde el principio hasta el cursor
Ctrl+Y: pega el texto que cortaste con Ctrl+K o Ctrl+U

martes, 9 de septiembre de 2008

Medir el tiempo de cada funcion en python

Cuando vamos a optimizar código lo primero en lo que pensamos es en medir el tiempo que tarda cada función de nuestro programa para ver donde se puede mejorar más (Ley de Amdahl).

En python el módulo timeit nos facilita mucho esta tarea, se puede usar de la siguiente manera.

import timeit
t = timeit.Timer("funcion()", "from __main__ import funcion")
t.timeit()


t.timeit() ejecuta múltiples veces la función y devuelve el tiempo transcurrido, si se le pasa un parámetro ejecutará la función ese número de veces.

También se puede ejecutar varias veces el test anterior para asegurarnos que no influye ningún otro proceso en la medida del tiempo, para ello se puede ejecutar t.repeat(numero_de_veces) y devolverá un array con los tiempos transcurridos.

domingo, 7 de septiembre de 2008

MapReduce

MapReduce es una técnica popularizada por Google (aquí el documento original) para el tratamiento de grandes cantidades de información en un tiempo razonable.

El nombre proviene de las funciones propias de la programación funcional map y reduce.

En python estas dos funciones se pueden combinar de la siguiente forma para contar el número de números impares de una lista:

reduce(lambda n,m: n+m, map(lambda n: n%2, [1,2,3,4,5,6,7,8,9]))

Primero, map aplica la función n%2 a toda la lista original y cambiandola de dominio (de números naturales a [0,1]), después reduce suma la nueva lista obteniendo el número de elementos impares.

¿Qué ventaja tiene esta manera de hacerlo el recuento a la tradicional?
Lo primero que se nos hubiera ocurrido tradicionalmente sería recorrer la lista en aumentando un contador si un número es impar.
Con MapReduce hacemos un procedimiento intermedio (cambiar la lista de dominio) pero a cambio es un diseño muy sencillo de paralelizar.

Por definición map aplica una función a cada elemento de una lista, implicitamente no hay relación entre cada cálculo realizado por map, así que cada operación la puede realizar un nodo.

Tras eso se necesita almacenar todos los resultados en un nodo central para obtener el resultado final, mayor eficiencia obtendremos en este algoritmo cuanto más porcentaje del cálculo lo hagamos mediante map y menos mediante reduce.

Crear un disco virtual usando la memoria RAM

En muchas ocasiones se trabaja intensivamente con un conjunto no demasiado grande de ficheros, se puede acelerar mucho el acceso a estos ficheros si se almacenan en la memoria RAM.

Para ello, en linux se puede crear un nuevo punto de montaje que acceda a la RAM, tan solo es necesario hacer lo siguiente.

mkdir /mnt/ramdisk
mount tmpfs /mnt/ramdisk -t tmpfs


Ya podemos trabajar en el directorio ramdisk, eso sí, la memoria RAM es volátil, si queremos guardar los cambios antes de apagar el pc es necesario copiarlo a cualquier otro directorio del disco duro.

sábado, 6 de septiembre de 2008

Leer los parámetros de la lista de comandos en python

La mayoría de programas que se usan mediante la línea de comandos aceptan parámetros y eso es lo que le da a estos programas mayor flexibilidad que a los que se usan mediante interfaz gráfica.

Para leer los parámetros de un programa en python nos puede servir la librería optparse.

Tiene bastantes opciones e incluso genera la ayuda de forma automática al llamar al programa con -h.
Un ejemplo de como se utiliza.

#!/usr/bin/python

from optparse import OptionParser

parser=OptionParser()
parser.add_option("-a", "--all", dest="todos", action="store_true", default=False,
help="afecta a todos los elementos")
parser.add_option("-f", action="store_false", dest="todos",
help="affecta solo al primer elemento")
parser.add_option("--without-output", action="store_false", dest="output", default=True,
help="sin mostrar salida")
parser.add_option("-i", "--insert", action="store", dest="newValue",
help="inserta un elemento al final de la lista")

(options, args)=parser.parse_args()

lista=[1,2,3,4,5,6,7,8,9];
nuevaLista=lista

if options.todos:
nuevaLista=map(lambda n: n*2, lista)
else:
nuevaLista[0]=lista[0]*2

if options.newValue:
nuevaLista.append(int(options.newValue))

if options.output:
print nuevaLista

Se pueden combinar parámetros, agrupar, hacerlos excluyentes... casi todo lo que se ocurra, si necesitas algo más específico mira en el enlace anterior.