Get Firefox

Firefox 4.0

stopsoftwarepatents.eu petition banner Manifiesto por la liberación de la cultura 
No a la traza privada
Últimos cambios
Últimos Cambios
Vote for Public Maps - Reject INSPIRE! Geocaching
Mi estado actual en Jabber/XMPP: - jabberES - jabber.org

Optimización en el sistema de persistencia, y gestión de errores de conexión

Última Actualización: 15 de marzo de 2008 - Sábado

El sistema de persistencia que he desplegado hace un mes funciona a la perfección, conforme a lo esperado. Pero he detectado algunas áreas mejorables en el uso concreto de la persistencia en este proyecto.

El principal de ellos es el rendimiento.

Tengo varias sesiones de BitTornado abiertas constantemente, algunas de las cuales con más de 500 torrents. Si recalculamos la pantalla una vez por segundo y tenemos 500 torrents, estamos haciendo unas 1000 transacciones por segundo. La contabilidad de tráfico para cada torrent supone dos transacciones: la primera para sincronizar la caché persistente local, y la segunda para actualizar los datos.

Debido a ello todos los procesos me estaban consumiendo el 27% de la CPU Una máquina antigua, eso sí: Pentium 4 a 1.4 Ghz, de 2001, con 640MB de RAM. la máquina no se dedica a nada más, así que no es un problema incluso en un caso extremo como éste (miles de torrents).

La cuestión es que el consumo de CPU es proporcional al número de torrents en el sistema, cuando debería ser proporcional al número de torrents activos en cada momento (o, más concretamente, proporcional al tráfico cursado). Esto es especialmente importante porque, en general, el número de torrents activos en un momento dado es muy bajo comparado con el número de torrents existentes.

La solución es bastante simple: refrescamos la caché local de persistencia una vez por segundo, no una vez por torrent existente en el sistema. Adicionalmente, hacemos "commit" solo cuando hay cambios.

Conforme a este nuevo diseño, si no hay actividad sólo se realiza una sincronización por segundo, independientemente del número de torrents en el sistema. Y si hay actividad, el tráfico de persistencia será proporcional al tráfico P2P (típicamente un "commit" cada 16 Kbytes). Con estos cambios, el consumo de CPU ha pasado del 27% a ser inapreciable.

Los cambios son:

--- BitTornado/CurrentRateMeasure.py    (revision 7)
+++ BitTornado/CurrentRateMeasure.py    (working copy)
@@ -3,7 +3,7 @@

 from clock import clock

-from monitor import monitor
+from monitor import monitor_opt

 from time import time

@@ -16,18 +16,18 @@
         self.infohash=infohash
         self.up=0 if up else 1

-        @monitor
+        @monitor_opt
         def x(conn,infohash,up) :
           speed=conn.get_root()["BT"]
           if infohash not in speed :
             speed[infohash]=[0,0,time()]
           grand_total=speed[infohash][up]
-          return grand_total
+          return (True,grand_total)

         self.total=x(self.infohash,self.up)

     def update_rate(self, amount):
-        @monitor
+        @monitor_opt
         def x(conn,infohash,up,amount) :
           speed=conn.get_root()["BT"]
           x=speed[infohash]
@@ -36,7 +36,8 @@
             x[up]=total
             x[2]=time()
             speed[infohash]=x
-          return total
+            return (True,total)
+          return (False,total)

         self.total=x(self.infohash,self.up,amount)

Index: BitTornado/monitor.py
===================================================================
--- BitTornado/monitor.py       (revision 7)
+++ BitTornado/monitor.py       (working copy)
@@ -3,6 +3,12 @@

 persistencia_mutex=threading.Lock()

+visualizar_mensaje=None
+
+def set_display(handler) :
+  global visualizar_mensaje
+  visualizar_mensaje=handler
+
 def conecta_storage() :
   from durus.client_storage import ClientStorage
   from durus.connection import Connection
@@ -12,39 +18,66 @@
   try :
     persistencia = conecta_storage()
     break
+  except KeyboardInterrupt :
+    raise
   except :
     time.sleep(0.1)

-def monitor(func) :
+last_durus_sync=0
+
+def monitor_opt(func) :
   def _monitor(*args, **kwargs) :
     global persistencia
     global persistencia_mutex
+    global last_durus_sync
     from durus.error import ConflictError
-    import socket
+    import socket,time
+    must_reconnect=False
     while True : # Reintenta si hay conflictos
       persistencia_mutex.acquire()
       try : # Nos aseguramos de liberar el lock
+        t=time.time()
+        if must_reconnect :
+          must_reconnect=False
+          if visualizar_mensaje!=None :
+            visualizar_mensaje("PROBLEMAS CON LA CONEXION DURUS... ABRIENDO UNA CONEXION NUEVA...")
+          last_durus_sync=t
+          while True :
+            try :
+              persistencia = conecta_storage()
+              break
+            except KeyboardInterrupt :
+              raise
+            except :
+              time.sleep(0.1)
+          if visualizar_mensaje!=None :
+            visualizar_mensaje("Conexion DURUS recuperada.")
         try : # Conflictos
-
+          t=time.time()
           try : # Nos aseguramos de estar conectados
-            persistencia.abort() # Hacemos limpieza de cache
+            if t-last_durus_sync>1.0 :
+              last_durus_sync=t
+              persistencia.abort() # Hacemos limpieza de cache
           except socket.error :
-            import sys
-            import time
-            print >>sys.stderr,"PROBLEMAS CON LA CONEXION DURUS... ABRIENDO UNA CONEXION NUEVA"
-            while True :
-              try :
-                persistencia = conecta_storage()
-                break
-              except :
-                time.sleep(0.1)
+            must_reconnect=True
+            continue  # Vuelve a intentarlo

           ret=func(persistencia,*args, **kwargs)
-          persistencia.commit()
-          return ret
+          if ret[0] :
+            last_durus_sync=time.time()
+            try :
+              persistencia.commit()
+            except socket.error :
+              must_reconnect=True
+              continue  # Vuelve a intentarlo
+
+          return ret[1]
+
         except ConflictError :
+          last_durus_sync=0
           pass # El abort ya se hace en el bucle
         except :
+          last_durus_sync=t
           persistencia.abort()
           import sys
           import time
@@ -55,13 +88,14 @@

   return _monitor

-@monitor
+@monitor_opt
 def inicializa(conn) :
   from durus.btree import BTree
   from durus.persistent_dict import PersistentDict
   root=conn.get_root()
   if "BT" not in root :
     root["BT"]=BTree()
+  return (True,None)

 inicializa()

Index: btlaunchmanycurses.py
===================================================================
--- btlaunchmanycurses.py       (revision 6)
+++ btlaunchmanycurses.py       (working copy)
@@ -273,7 +273,10 @@


 def LaunchManyWrapper(scrwin, config):
-    LaunchMany(config, CursesDisplayer(scrwin))
+    ventana=CursesDisplayer(scrwin)
+    from BitTornado import monitor
+    monitor.set_display(ventana.message)
+    LaunchMany(config, ventana)


 if __name__ == '__main__':

Detalles:


Historia



Firefox 4.0 Python Zope ©2008 jcea@jcea.es