Entrando en la página de Erlang Central en su chat había un mensaje que decía literalmente: mnesia_ext_eleveldb ,how to download? (mnesia_ext_eleveldb, ¿cómo lo descargo?). La alusión de que Mnesia pueda trabajar con LevelDB me resultó curiosa, tanto que he decidido investigarlo y dedicarle una entrada en el blog.
LevelDB es una librería que permite trabajar con base de datos en fichero y localmente. Tal y como hacen otras como SQLite o BerkeleyDB. El motivo por el que LevelDB se ha destacado de otras similares es porque se comporta como una base de datos NoSQL (del estilo clave/valor), usa el compresor Snappy para ocupar menos espacio en disco asegurando una compresión rápida y tiene un buen rendimiento, sobre todo a nivel de escritura tal y como puede verse en el banco de pruebas realizado por Google.
Mnesia por su parte tiene como backends por defecto dos posibilidades: ETS y DETS. Las primeras se emplean para uso de tablas en memoria y las segundas como tablas de almacenamiento en disco. El problema de las primeras es el espacio disponible en la máquina donde se ejecute y el problema de las segundas es la limitación del fichero de almacenamiento a 2GiB.
LevelDB de cerca
La entrada de LevelDB como backend para tablas de Mnesia supone agregar un nuevo formato de almacenamiento libre de las limitaciones de las anteriores. LevelDB genera internamente varios ficheros, según la cantidad de claves que se vayan almacenando por tabla. Además permite realizar modificaciones diferidas lo cual lo hace ideal para sistemas en los que la escritura es más frecuente que la lectura.
Como curiosidad, en MariaDB se ha agregado LevelDB como nuevo engine. En el blog de making.change.org, como colofón, argumentan sobre el uso de LevelDB:
Los trabajos que antes tomaban de 40 minutos a 4 horas son ahora terminados entre 10 y 20 minutos. En el futuro, quizás elijamos algo como Riak o Cassandra pero por ahora me he librado de las interrupciones y el problema original que las causaba.
Hay una comparativa entre MySQL y LevelDB. Es algo extraña porque muestra datos al final algo confusos. Las líneas de LevelDB retroceden en lugar de avanzar. ¿Significa que su rendimiento baja? Sea como fuere, tener algo parecido al rendimiento de MySQL dentro de Mnesia, o incluso mejor por la idea de no-bloqueante, me parece más positivo que negativo.
Para probar vamos a descargar la librería de LevelDB que hay para Erlang desde github:
git clone git@github.com:/norton/lets.git
Como nota importante, necesitaremos la versión de Erlang R16B como mínimo. Entramos en el directorio compilamos y desde el directorio ebin lanzamos el intérprete de comandos. Algo como lo siguiente:
rebar compile
cd ebin
erl -pa ../deps/*/ebin
Dentro del intérprete necesitaremos lanzar las aplicaciones:
application:start(sasl).
application:start(lets).
OptsDB = [{path, "mytable"}, create_if_missing]}],
lets:new(mytable, [named_table, compressed, {db, OptsDB}]).
% mytable
lets:insert(mytable, {"hi", "hello"}).
% true
lets:insert(mytable, {"bye", "goodbye"}).
% true
lets:lookup(mytable, "hi").
% [{"hi", "hello"}]
Las funciones que pueden emplearse son muy parecidas a las disponibles tanto para ETS como para DETS.
Al salir veremos que en el directorio ebin se ha creado un nuevo directorio mytable en el que se deposita el contenido de la tabla. Hay un fichero de LOG donde se anotan los cambios y las acciones llevadas a cabo, un fichero de bloqueo (LOCK) y el resto son ficheros de datos en snapshots.
Si queremos tener este sistema como backend de nuestra aplicación en Erlang solo habrá que incluir la ruta de github.com en el fichero rebar.config, en la sección de dependencias (deps). El uso una vez iniciemos la aplicación de lets será el mostrado en las líneas anteriores.
Backend de LevelDB en Mnesia
El proyecto que se encarga de hacer la magia es mnesia_dets_to_lets. En su fichero de README podemos ver cómo implementar paso a paso la solución. En este caso bajamos el proyecto:
git clone https://github.com/odo/mnesia_dets_to_lets.git
Seguimos estas líneas de compilación:
cd mnesia_dets_to_lets
make
En este punto me ha saltado un fallo. Esto es debido a que la regla de rebar.config no me permite usar R16B, pero si bajo a R15B02 me dice que necesito R16B al compilar una de las dependencias. La solución es editar el fichero rebar.config y agregar el R16B.
Llegados a este punto, según la documentación sería entrar en el entorno a través de make start
y después ejecutar dentro las pruebas que indican si todo funciona correctamente:
dets_to_lets:test(durability).
dets_to_lets:test(all).
Para finalmente ejecutar la maqueta dets_to_lets:init().
y poder comenzar a jugar creando tablas basadas en DETS y que realmente se almacenen en LETS o LevelDB.
Conclusiones
El proyecto promete. Digo promete porque he tenido muchos errores para poder hacerlo funcionar y finalmente no he podido hacer muchas pruebas con él. El código se basa en meck
, un sistema para crear mocks y stubs. Facilita la sustitución de DETS pero al mismo tiempo inhabilita su uso.
Personalmente preferiría instalar un sistema Mnesia paralelo basado en LevelDB a modificar el comportamiento nativo de las DETS para emplear LevelDB en su lugar. El código es experimental y tiene aún mucho por perfilar. Esperaremos a ver la evolución que va tomando el proyecto.