Member of The Internet Defense League Últimos cambios
Últimos Cambios
Blog personal: El hilo del laberinto Geocaching

Persistencia de tráfico y cuotas de subida

Última Actualización: 13 de febrero de 2008 - Miércoles

Recientemente, uno de los lugares P2P que frecuento ha instaurado una política de cuota de tráfico de subida, que complementa su ya legendario SR ("Share Ratio"). El objetivo que busca esa medida es que los usuarios que disponen de poco ancho de banda de salida puedan mantener un SR decente, evitando que los que tienen un gran caudal les "pisen" los "peers" y les dejen sin nadie a quien subir.

Dado que mi cliente BitTorrent no permite esa funcionalidad, me tocaba o cambiar de cliente o -dado que el programa está escrito en Python- programarla yo mismo.

Sobra decir qué camino elegí :-)

Para poder realizar un control de la cuota, es preciso llevar contabilidad del tráfico. Dicha contabilidad debe conservarse entre ejecuciones del programa, así que utilizo tecnología de persistencia. Esta tecnología novedosa apenas se emplea por ser poco conocida, pero es muy potente y simple de utilizar. Qué lástima. ¡Ellos se lo pierden! :-).

Así pues, nos hacen falta tres cosas:

  • Dado que vamos a lanzar muchos clientes potenciales de BitTornado, y que queremos que compartan las estadísticas, necesitamos un proceso externo que haga de servidor de persistencia. Podría haber integrado el servidor en el cliente de BitTornado, y que cuando se lancen varios simultaneamente elijan entre sí un ganador, pero mantenerlo separado es trivial. Esto es algo a reevaluar en el futuro.

  • Debemos llevar control del tráfico de cada "torrent", teniendo en cuenta que a) el programa puede morir en cualquier instante (por ejemplo, se va la luz) y b) que un mismo "torrent" puede estar siendo servido desde varios clientes distintos, que deben acumular su tráfico.

  • Por último, necesitamos un sistema que evalúe el tráfico constantemente y compruebe la cuota de subida. Si estamos a punto de pasarnos y ser penalizados, debe tomar las medidas oportunas.

Aquí tienes el parche para BitTornado 0.3.17.

Como podemos ver, el parche es relativamente simple:

  • El parche sobre "DownloaderFeedback.py" se encarga de almacenar en memoria el tráfico máximo de subida que vamos a tolerar para ese "torrent". Si nos pasamos de tráfico, se invoca el "choker" para que bloquee las peticiones del resto de "peers". La consecuencia es que seguiremos conectados, pero no serviremos peticiones adicionales. Si no tenemos el "torrent" completo, podemos seguir haciendo peticiones, pero solo de bajada. Esto es preferible a desactivar el "torrent" por completo, como hacen otros programas.

    Otra ventaja de este sistema es que si hemos llegado a nuestra cuota no transmitiremos nada, pero seguimos pudiendo ver si hay demanda en el "swarm" (enjambre). Así, podemos hablar con alguien sin problemas de cuota y pedirle que reactive el "torrent".

  • El parche sobre "download_bt1.py" calcula el tamaño del "torrent", inicializa los contadores "persistentes" de tráfico y calcula la cuota que le corresponde al fichero. Si ya estamos en la zona peligrosa, arranca el "torrent" en modo "choked" directamente, para evitar mandar ni un byte de tráfico adicional.

    La forma de calcular la cuota que corresponde a cada "torrent" es como sigue:

    • Primero, comprobamos el "tracker" del "torrent". Si el "tracker" no impone cuotas de subida, fijamos una cuota de cero bytes, que indica "sin límite".

    • Si se trata del "tracker" en cuestión, éste impone una cuota de DIEZ veces el tamaño del fichero a recibir, siempre que dicho fichero mida más de 300 megabytes (los ficheros más pequeños no tienen control de cuota).

      Obsérvese que somos muy conservadores a la hora de calcular si estamos en zona de cuota o no, y de calcular la cuota correspondiente, dejando incluso un margen de seguridad de 10 megabytes, por si las moscas.

      Por supuesto, esta parte del código puede fijar una política arbitraria. Todo el meollo del asunto está aquí.

      Una vez decidida la cuota que corresponde a este "torrent", se almacena como un valor numérico fijo, a disposición del sistema de control de tráfico.

  • El parche sobre "CurrentRateMeasure.py" parece largo y complejo, pero básicamente duplica la clase "Measure" para crear la clase "MeasureDurus". Son prácticamente idénticas, pero la segunda es "persistente". De hecho lo suyo sería aprovechar la programación orientada a objetos y heredar de "Measure" para crear "MeasureDurus" con las mínimas modificaciones de código, pero ésta es tarea para otro día :-).

    La parte interesante son las dos rutinas "x". Como puede verse se utiliza el decorador "@monitor" para interactuar con la infraestructura Durus de la forma más transparente posible. La claridad y la sencillez son los paladines de la tecnología de persistencia. ¡¡Aquí se puede comprobar!!.

  • Por último tenemos el fichero "monitor.py". Este módulo crea un decorador empleado para marcar las rutinas que manejan datos persistentes, encargándose de todos los detalles: conexión al sistema de persistencia externo, reconexión si es necesario, gestión de conflictos (mediante reintentos), serialización de accesos a la infraestructura de Durus, abortar transacciones si surgen excepciones, etc.

    Es muy importante señalar que este módulo no guarda ninguna relación con BitTornado. Es un decorador genérico que uso siempre que empleo persistencia, para no tener que preocuparme de cosas como conflictos o errores de conexión. Podéis usarlo en vuestros propios proyectos, tal cual. Ésto es todo lo que se necesita, no hay que comerse más el coco.

La mayoría de mis programas "persistentes" integran dentro un servidor Durus. De esta forma tengo todo junto, pero sigo manteniendo la posibilidad de interacturar con el sistema de persistencia de forma manual y externa, si es necesario. En este caso, en cambio, podemos tener muchas instancias de BitTornado funcionando simultáneamente, así que he preferido segregar, en esta ocasión y sin que sirva de precedente, el servidor de persistencia en un proceso aparte y separado. El código del servidor de persistencia es trivial:

#!/usr/local/bin/python

import sys
sys.path.extend(("/video/0/durus-berkeleydbstorage",))

import berkeleydb_storage
storage=berkeleydb_storage.BerkeleyDBStorage("/video/0/durus-berkeleydbstorage/db", \
          durable=False,async_log_write=True,do_recover=True)

from durus.storage_server import StorageServer
StorageServer(storage, address="/video/0/durus-berkeleydbstorage/db/unix_socket").serve()

Este programa debe arrancarse antes de los BitTornado. Como veis, un servidor de persistencia simple es muy... simple :-). El único detalle a tener en cuenta es que, por razones de explicaré en otro artículo, configuro un "storage" ACI (no ACID), pero indico escrituras asíncronas de log (lo que indica que es "casi" ACID, salvo fallo del sistema operativo o que se corte la electricidad). Este programa utiliza mi "backend" para Durus y el propio Durus, claro.


Historia

  • 13/feb/08: Primera versión de esta página.



Python Zope ©2008 jcea@jcea.es

Más información sobre los OpenBadges

Donación BitCoin: 19niBN42ac2pqDQFx6GJZxry2JQSFvwAfS