ratemanager.py :  » Network » Torrent-Swapper » swapper » ABC » Scheduler » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Network » Torrent Swapper 
Torrent Swapper » swapper » ABC » Scheduler » ratemanager.py
import sys
import wx
import os

from threading import Event
from time import time,clock

#from traceback import print_exc
#from cStringIO import StringIO

from Utility.constants import *#IGNORE:W0611
fromUtility.helpersunion,difference
from safeguiupdate import DelayedEventHandler

################################################################
#
# Class: RateManger
#
# Keep the upload and download rates for torrents within
# the defined local and global limits
#
################################################################
class RateManager(DelayedEventHandler):
    def __init__(self, queue):
        DelayedEventHandler.__init__(self)
        self.doneflag = Event()
               
        self.queue = queue
        self.utility = queue.utility

        # For Upload Rate Maximizer
        # Time when the upload rate is lower than the URM threshold for the first time
        self.urm_time = { 'under' : 0.0, 
                          'checking'  : 0.0 }
                
        # bandwidth between reserved rate and measured rate
        # above which a torrent will have its reserved rate lowered
        self.calcupth1 = 2.3
        
        # bandwidth between reserved rate and measured rate
        # under which a torrent will have its reserved rate raised
        self.calcupth2 = 0.7
        
        self.meancalcupth = (self.calcupth1 + self.calcupth2) / 2
        
        self.lastmaxrate = -1

        # Minimum rate setting
        # 3 KB/s for uploads, 0.01KB/s for downloads
        # (effectively, no real limit for downloads)
        self.rateminimum = { "up": 3.0,
                             "down": 0.01 }

    def RunTasks(self):
        self.invokeLater(self.RateTasks)
            
    def RateTasks(self):
        self.doneflag.set()
        
        self.UploadRateMaximizer()
        
        self.CalculateBandwidth("down")
        self.CalculateBandwidth("up")
        
        self.doneflag.clear()

    def MaxRate(self, dir = "up"):
        Read = self.utility.config.Read
        
        if dir == "up":
            if self.utility.torrents["downloading"]:
                # Static overall maximum up rate when downloading
                return Read('maxuploadrate', "float")
            else:
                # Static overall maximum up rate when seeding
                return Read('maxseeduploadrate', "float")
        else:
            return Read('maxdownloadrate', "float")
           
    # See if any torrents are in the "checking existing data"
    # or "allocating space" stages
    def torrentsChecking(self):
        for torrent in self.utility.torrents["active"].keys():
            if torrent.status.isCheckingOrAllocating():
                return True
        return False

    def UploadRateMaximizer(self):
        Read = self.utility.config.Read
               
        # Don't do anything if URM isn't enabled
        if not Read('urm', "boolean"):
            return
        
        # Don't start:
        # - if no torrents are inactive
        # - if any torrents are still in the "checking data" phase
        # - if not enough time has passed after torrents finished checking
        if not self.utility.torrents["inactive"]:
            self.urm_time['under'] = 0
            return
        
        if not self.urm_time['checking'] or self.torrentsChecking():
            self.urm_time['checking'] = time()
            return
            
        # See how long to wait between checking torrents
        delay = Read('urmdelay', "int")
            
        # If a torrent was checking, allow it some time to start up
        # (wait a minimum of 45 seconds for a torrent to start up,
        #  longer if the delay between starting torrents is longer)
        if time() - self.urm_time['checking'] < max(delay, 45):
            return

        # Find the "low" value for upload rate that we're checking for
        lowupthreshold = self.MaxRate("up") - Read('urmupthreshold', "int")
        if lowupthreshold < 0:
            lowupthreshold = 0
        
        uploadrate = self.queue.totals_kb['up']
        
        # Upload rate is below the threshold
        if uploadrate < lowupthreshold or (uploadrate == 0.0 and lowupthreshold == 0.0):
            if self.urm_time['under'] == 0:
                self.urm_time['under'] = time()
            
            # Threshold exceeded for more than urmdelay s ?
            elif time() - self.urm_time['under'] > Read('urmdelay', "int"):
                # Get the next torrent to start
                inactivetorrents = self.utility.queue.getInactiveTorrents(1)
                if not inactivetorrents:
                    return
                
                self.urm_time['under'] = 0
                
                self.utility.actionhandler.procRESUME(inactivetorrents)
                
                self.queue.UpdateRunningTorrentCounters()
        
        # The upload rate is good for now
        else:
            self.urm_time['under'] = 0

##########################################################################################
            
    def CalculateBandwidth(self, dir = "up"):
        if dir == "up":
            workingset = union(self.utility.torrents["downloading"], self.utility.torrents["seeding"])
        else:
            workingset = self.utility.torrents["downloading"]

        # Limit working set to active torrents with connections:
        workingset = [torrent for torrent in workingset if torrent.status.isActive() and torrent.connection.engine.hasConnections]

        # No active file, not need to calculate
        if not workingset:
            return
        
        maxrate = self.MaxRate(dir)
        # See if global rate settings are set to unlimited
        if maxrate == 0:
            # Unlimited rate
            for torrent in workingset:
                torrent.connection.maxrate[dir] = torrent.connection.getLocalRate(dir)
                torrent.connection.setRate(torrent.connection.maxrate[dir], dir)
            return
        
        #print "====================== BEGINNING ALGO ======= th1=%.1f ; th2=%.1f =============================" % (self.calcupth1, self.calcupth2)

        #######################################################
        # - Find number of completed/incomplete torrent
        # - Number of torrent using local setting
        # - bandwidth already used by torrents
        # - Sorting of torrents in lists according to their will in matter of upload rate :
        #   (tobelowered, toberaisedinpriority, toberaised, nottobechanged)
        #######################################################

        # Option set in Preferences/Queue.
        # If not set, torrents with rate local settings are treated like other torrents, except they have their
        # own max rate they can't cross over. The consequence is a behaviour slightly different from the behavior of
        # ABC releases prior to 2.7.0.
        # If set, this gives the algorithm the behaviour of ABC releases prior to 2.7.0 : the torrents with an rate
        # local setting will be granted bandwidth in priority to fulfill their local setting, even is this one
        # is higher than the global max rate setting, and even if this bandwidth must be taken from other active
        # torrents wihout a local rate setting. These torrents will not take part in the rate exchange
        # between active torrents when all bandwidth has been distributed, since they will have been served in priority.
        prioritizelocal = self.utility.config.Read('prioritizelocal', "boolean")

        # torrents with local rate settings when prioritizelocal is set (see prioritizelocal)
        localprioactive = []       
    
        # torrents for which measured rate is lowering and so reserved rate can be lowered
        tobelowered = []
     
        # torrents for which measured rate is growing and so reserved rate can be raised,
        # (with reserved rate > 3 kB/s for uploading)
        toberaised  = []                           

        # torrents for which reserved rate can be raised, with reserved rate < 3 kB/s
        # These will always be raised even there's no available up bandwith, to fulfill the min 3 kB/s rate rule
        toberaisedinpriority = []                             

        # torrents for which reserved rate is not to be changed and is > rateminimum; (for these, the
        # measured rate is between (max upload reserved - calcupth1) and (max upload reserved - calcupth2)
        nottobechanged = []   

        meanrate = 0.0            # mean max rate for torrents to be raised or not to be changed ; it will be used to decide which torrents
                              # must be raised amongst those that want to get higher and that must share the available up bandwidth. The torrents
                              # that don't want to change their rate and that are below 3 kB/s are not taken into account on purpose, because
                              # these ones will never be lowered to give more rate to a torrent that wants to raise its rate, or to
                              # compensate for the rate given to torrents to be raised in priority.

        for torrent in workingset:
            # Active Torrent
            currentrate = torrent.connection.rate[dir]

            maxrate_float = torrent.connection.maxrate[dir]

            # Torrents dispatch
            if prioritizelocal and torrent.connection.getLocalRate(dir, True):
                localprioactive.append(torrent)
            elif currentrate < 0.05:
                # These are the torrents that don't want to have their rate changed and that have an allmost null rate
                # They will not go lower, we can reset their reserved rate until they want to get higher
                torrent.connection.maxrate[dir] = 0.0
            elif maxrate_float - currentrate > self.calcupth1:
                tobelowered.append(torrent)
            elif maxrate_float - currentrate <= self.calcupth2:
                if currentrate < self.rateminimum[dir]:
                    toberaisedinpriority.append(torrent)
                else:
                    toberaised.append(torrent)
                    meanrate += maxrate_float
            elif currentrate > self.rateminimum[dir]:
                nottobechanged.append(torrent)
                meanrate += maxrate_float

#        print "index: %i ; rate: %.1f ; reserved: %.1f ; maxlocal: %.1f" % (torrent.listindex, \
#              currentrate, maxrate_float, float(torrent.connection.maxlocalrate[dir]))

        ###############################################
        # Calculate rate for each torrent
        ###############################################
        
        availableratetobedistributed = maxrate
        
        if ((availableratetobedistributed != 0)
            and (1 - (self.queue.totals_kb[dir] / availableratetobedistributed) > 0.20)):
            #print "FREE WHEELING TORRENTS"
            # If there's still at least 20% of available bandwidth, let the torrents do want they want
            # Keep a reserved max rate updated not to have to reinitilize it when we'll switch later
            # from free wheeling to controlled status.
            # Give BitTornado the highest possible value for max rate to speed up the rate rising
            for torrent in workingset:
                newrate = torrent.connection.rate[dir] + self.meancalcupth
                maxlocalrate = float(torrent.connection.getLocalRate(dir))
                if maxlocalrate > 0:
                    rateToUse = maxlocalrate
                    if newrate > maxlocalrate:
                        newrate = maxlocalrate
                else:
                    rateToUse = self.MaxRate(dir)
                torrent.connection.maxrate[dir] = newrate
                # Send to BitTornado
                torrent.connection.setRate(rateToUse, dir)
            return

        ###########################################################################
        # First treat special torrents before going on sharing and distributing
        ###########################################################################

        # Treat in priority the torrents with rate below 3 kB/s and that want to get a higher rate
        grantedfortoberaisedinpriority = 0.0
        for torrent in toberaisedinpriority:
            newreservedrate = torrent.connection.rate[dir] + self.meancalcupth
            if newreservedrate > self.rateminimum[dir]:
                grantedfortoberaisedinpriority += self.rateminimum[dir] - torrent.connection.maxrate[dir]
                torrent.connection.maxrate[dir] = self.rateminimum[dir]
            else:
                grantedfortoberaisedinpriority += newreservedrate - torrent.connection.maxrate[dir]
                torrent.connection.maxrate[dir] = newreservedrate

        # Treat in priority the torrents with a local rate setting if "prioritize local" is set
        # As with the free wheeling torrents, keep on tracking the real value of rate while giving BitTornado
        # the highest max rate to speed up the rate rising.
        grantedforlocaltoberaised = 0.0
        for torrent in localprioactive:
            newrate = torrent.connection.rate[dir] + self.meancalcupth
            maxlocalrate = float(torrent.connection.getLocalRate(dir))
            if newrate > maxlocalrate:
                newrate = maxlocalrate
            grantedforlocaltoberaised += newrate - torrent.connection.maxrate[dir]
            torrent.connection.maxrate[dir] = newrate
            # Send to BitTornado
            torrent.connection.setRate(maxlocalrate, dir)

        # Torrents that want to be lowered in rate (and give back some reserved bandwidth)
        givenbackbytobelowered = 0.0
        for torrent in tobelowered:
            newreservedrate = torrent.connection.rate[dir] + self.meancalcupth
            givenbackbytobelowered += newreservedrate - torrent.connection.maxrate[dir]
            torrent.connection.maxrate[dir] = newreservedrate
        
        # Add to available rate to be distributed the rate given back by the torrents that have been lowered ;
        # Substract from available rate the rate used for each torrent that have been be raised in priority (torrents with rate
        # below 3 kB/s and that want to get higher).
        availableratetobedistributed += givenbackbytobelowered - grantedfortoberaisedinpriority - grantedforlocaltoberaised - self.queue.totals_kb[dir]
        #print "availableratetobedistributed is %.3f" % availableratetobedistributed

        # localprioactive torrents have already been updated if prioritizelocal is set
        toberegulated = [torrent for torrent in workingset if torrent not in localprioactive]

        # There's nothing to do if no torrent want to be raised
        if not toberaised:
            ################################################
            # Set new max rate to all active torrents
            ################################################
            for torrent in toberegulated:
                torrent.connection.setRate(torrent.connection.maxrate[dir], dir)
            return
               
        if availableratetobedistributed > 0:
            ###########################################################################
            #print "PHASE 1"
            ###########################################################################
            # Phase 1 : As long as there's available bandwidth below the total max to be distributed, I give some
            # to any torrents that asks for it.
            # There's a special case with torrents to be raised that have a local max rate : they must be topped to their max local
            # rate and the surplus part of the reserved bandwidth may be reused.
            # To sum up : In Phase 1, the measured rate is leading the reserved rate.

            # Check if all torrents that claim a higher reserved rate will be satisfied
            claimedrate = 0.0
            for torrent in toberaised:
                maxup = torrent.connection.maxrate[dir]
                newreservedrate = torrent.connection.rate[dir] + self.meancalcupth
                toadd = newreservedrate

                maxlocalup = float(torrent.connection.getLocalRate(dir))
                if maxlocalup > 0 and newreservedrate > maxlocalup:
                    toadd = maxlocalup
                
                claimedrate += toadd - maxup

            #print "Claimed rate :", claimedrate
            #print "Available rate :", availableratetobedistributed

            if claimedrate <= availableratetobedistributed:
                realupfactor = 1
            else:
                realupfactor = availableratetobedistributed / claimedrate

            # If all claims can be fulfilled ; we distribute and go to end.
            # If there's not enough remaining rate to fulfill all claims, the remaining available rate will be
            # distributed proportionally between all torrents that want to raise.
            for torrent in toberaised:
                maxup = torrent.connection.maxrate[dir]
                newreservedrate = torrent.connection.rate[dir] + self.meancalcupth
                newmaxup = maxup + (newreservedrate - maxup) * realupfactor

                maxlocalup = float(torrent.connection.getLocalRate(dir))
                if maxlocalup > 0 and newreservedrate > maxlocalup:
                    newmaxup = maxup + (maxlocalup - maxup) * realupfactor

                torrent.connection.maxrate[dir] = newmaxup
                #print "index :", torrent.listindex, "; rate raised from", maxup, "to", torrent.connection.maxrate[dir]
            
            ################################################
            # Set new max rate to all active torrents
            ################################################
            for torrent in toberegulated:
                torrent.connection.setRate(torrent.connection.maxrate[dir], dir)
            return

        ###########################################################################
        #print "PHASE 2"
        ###########################################################################
        # -a- Each torrent that wants its rate to be raised or not to be changed will have its reserved rate lowered
        #     to compensate the bandwidth given in priority to torrents with rate below 3 kB/s and torrents with local rate
        #     settings if "prioritize local" is set. This lowering must not bring the reserved rate of a torrent below 3 kB/s.
        #     Torrents will be sorted by their reserved rate and treated from the lower to the bigger. If a part of the lowering
        #     cannot be achieved with a torrent,it will be dispatched to the pool of the remaining torrents to be treated.
        #     After this, there may still be more total rate reserved than available rate because of the min 3 kB/s rule.
        ###########################################################################

        # -a-
        if availableratetobedistributed < 0:
            rate_id = []
            pooltobelowered = toberaised + nottobechanged
            
            rate_id = [torrent for torrent in pooltobelowered if torrent.connection.rate[dir] > self.rateminimum[dir]]

            sizerate_id = len(rate_id)
            # Sort by increasing reserved rate
            try:
                rate_id.sort(None, key = lambda x: x.connection.maxrate[dir])
            except:
                pass
            if rate_id:
                ratenotassignedforeach = availableratetobedistributed / sizerate_id
            # Compute new reserved rate
            i = 0
            for torrent in rate_id:
                # (availableratetobedistributed and ratenotassignedforeach are negative numbers)
                newmaxrate = torrent.connection.maxrate[dir] + ratenotassignedforeach
                i += 1
                if newmaxrate < self.rateminimum[dir]:
                    # if some rate lowering could not be dispatched, it will be distributed to the next torrents
                    # in the list (which are higher in max rate because the list is sorted this way)
                    if i != sizerate_id:
                        ratenotassignedforeach += (newmaxrate - self.rateminimum[dir]) / (sizerate_id - i)
                    newmaxrate = self.rateminimum[dir]
                torrent.connection.maxrate[dir] = newmaxrate
            #    print "%i lowered from %.3f to %.3f" % (t[1].listindex, t[0], newmaxrate)
            #print "availableratetobedistributed is now %.3f" % availableratetobedistributed

        ###########################################################################
        #print "PHASE 2 algo with mean rate"
        ###########################################################################
        # Phase 2 : There's no more available bandwidth to be distributed, I split the total max between active torrents.
        # -b- Compute the mean max rate for the pool of torrents that want their rate to be raised or not to be changed
        #     and list torrents below and above that mean value.
        # -c- The regulation for torrents that want to have their rate raised is computed this way :
        #     The idea is to target a mean rate for all torrents that want to raise their rate or don't want to have it
        #     changed, taking into account the max rates of local settings, and the fact that no torrent must be lowered down below
        #     3 kB/s.
        # To sum up : In Phase 2, the reserved rate is leading the real rate .

        # -b-
        # Mean reserved rate calculation
        # If prioritizelocal is set, all torrents with a local rate settting are completely excluded from
        # the bandwidth exchange phase between other torrents. This is because this phase
        # targets a mean rate between torrents that want to upload more, and when prioritizelocal
        # is set, torrents with a local rate settting can be very far from this mean rate and their
        # own purpose is not to integrate the pool of other torrents but to live their own life (!)

        if toberaised or nottobechanged:
            meanrate /= len(toberaised) + len(nottobechanged)
        #print "Mean rate over 3 kB/s : %.1f" % meanrate

        raisedbelowm = []         # ids for torrents to be raised and with reserved rate below mean max rate
        allabovem = []            # ids for torrents to be raised or not to be changed and with reserved rate above mean max rate
        
        for torrent in toberaised:
            if torrent.connection.maxrate[dir] > meanrate:
                allabovem.append(torrent)
            elif torrent.connection.maxrate[dir] < meanrate:
                raisedbelowm.append(torrent)
        for torrent in nottobechanged:
            if torrent.connection.maxrate[dir] > meanrate:
                allabovem.append(torrent)

        # -c-
        if raisedbelowm and allabovem:
            # Available bandwidth exchange :
            up = 0.0
            down = 0.0
            for torrent in raisedbelowm:
                toadd = meanrate - torrent.connection.maxrate[dir]

                maxlocalrate_float = float(torrent.connection.getLocalRate(dir))
                if maxlocalrate_float > 0 and maxlocalrate_float <= meanrate:
                    toadd = maxlocalrate_float - torrent.connection.maxrate[dir]
                up += toadd
            for torrent in allabovem:
                down += torrent.connection.maxrate[dir] - meanrate
            if up > down:
                limitup = down / up
            
            # Speed up slow torrents that want their rate to be raised :
            # Each one must have its reserved rate raised slowly enough to let it follow this raise if it really
            # wants to get higher. If we set the reserved to the max in one shot, these torrents will be then detected
            # in the next phase 1 as to be lowered, which is not what we want.
            realup = 0.0
            for torrent in raisedbelowm:
                maxup = torrent.connection.maxrate[dir]
                toadd = meanrate - maxup

                maxlocalrate_float = float(torrent.connection.getLocalRate(dir))
                if maxlocalrate_float > 0 and maxlocalrate_float <= meanrate:
                    toadd = maxlocalrate_float - maxup

                if up > down:
                    toadd *= limitup
                # step is computed such as if the torrent keeps on raising at the same rate as before, it will be
                # analysed as still wanting to raise by the next phase 1 check
                step = 2 * (torrent.connection.rate[dir] + self.calcupth2 - maxup)
                if toadd < step:
                    torrent.connection.maxrate[dir] = maxup + toadd
                    realup += toadd
                else:
                    torrent.connection.maxrate[dir] = maxup + step
                    realup += step
                #print "index :", torrent.listindex, "; rate raised from", maxup, "to", torrent.connection.maxrate[dir]          
            realup /= len(allabovem)
            # Slow down fast torrents :
            for torrent in allabovem:
                maxup = torrent.connection.maxrate[dir]
                torrent.connection.maxrate[dir] = maxup - realup
                #print "index :", torrent.listindex, "; rate lowered from", maxup, "to", torrent.listindex

        ################################################
        # Set new max rate to all active torrents
        ################################################
        for torrent in toberegulated:
            torrent.connection.setRate(torrent.connection.maxrate[dir], dir)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.