[tickets] use slots in ticket (saves memory, and have better performance); uses the same ticket between Fail- and BanManagers, so makes possible operate with more actual data between workers (e. g. ban count and time by increment from observer)

pull/1460/head
sebres 2015-12-30 16:57:08 +01:00
parent b893356c49
commit 157a85451a
3 changed files with 42 additions and 24 deletions

View File

@ -256,7 +256,7 @@ class BanManager:
# we should always use correct time to calculate correct end time (ban time is variable now, # we should always use correct time to calculate correct end time (ban time is variable now,
# + possible double banning by restore from database and from log file) # + possible double banning by restore from database and from log file)
# so use as lastTime always time from ticket. # so use as lastTime always time from ticket.
return BanTicket(ticket=ticket) return BanTicket.wrap(ticket)
## ##
# Add a ban ticket. # Add a ban ticket.

View File

@ -27,7 +27,7 @@ __license__ = "GPL"
from threading import Lock from threading import Lock
import logging import logging
from .ticket import FailTicket from .ticket import FailTicket, BanTicket
from ..helpers import getLogger, BgService from ..helpers import getLogger, BgService
# Gets the instance of the logger. # Gets the instance of the logger.
@ -103,13 +103,13 @@ class FailManager:
fData.setMatches(matches[-self.maxEntries:]) fData.setMatches(matches[-self.maxEntries:])
except KeyError: except KeyError:
# not found - already banned - prevent to add failure if comes from observer: # not found - already banned - prevent to add failure if comes from observer:
if observed: if observed or isinstance(ticket, BanTicket):
return return
# if already FailTicket - add it direct, otherwise create (using copy all ticket data): # if already FailTicket - add it direct, otherwise create (using copy all ticket data):
if isinstance(ticket, FailTicket): if isinstance(ticket, FailTicket):
fData = ticket; fData = ticket;
else: else:
fData = FailTicket(ticket=ticket) fData = FailTicket.wrap(ticket)
if count > ticket.getAttempt(): if count > ticket.getAttempt():
fData.setRetry(count) fData.setRetry(count)
self.__failList[fid] = fData self.__failList[fid] = fData

View File

@ -33,6 +33,7 @@ logSys = getLogger(__name__)
class Ticket(object): class Ticket(object):
__slots__ = ('_ip', '_flags', '_banCount', '_banTime', '_time', '_data', '_retry', '_lastReset')
MAX_TIME = 0X7FFFFFFFFFFF ;# 4461763-th year MAX_TIME = 0X7FFFFFFFFFFF ;# 4461763-th year
@ -59,36 +60,44 @@ class Ticket(object):
self._data[k] = v self._data[k] = v
if ticket: if ticket:
# ticket available - copy whole information from ticket: # ticket available - copy whole information from ticket:
self.__dict__.update(i for i in ticket.__dict__.iteritems() if i[0] in self.__dict__) self.update(ticket)
#self.__dict__.update(i for i in ticket.__dict__.iteritems() if i[0] in self.__dict__)
def __str__(self): def __str__(self):
return "%s: ip=%s time=%s bantime=%s bancount=%s #attempts=%d matches=%r" % \ return "%s: ip=%s time=%s bantime=%s bancount=%s #attempts=%d matches=%r" % \
(self.__class__.__name__.split('.')[-1], self.__ip, self._time, (self.__class__.__name__.split('.')[-1], self._ip, self._time,
self._banTime, self._banCount, self._banTime, self._banCount,
self._data['failures'], self._data.get('matches', [])) self._data['failures'], self._data.get('matches', []))
def __repr__(self): def __repr__(self):
return str(self) return str(self)
def __eq__(self, other): def __eq__(self, other):
try: try:
return self.__ip == other.__ip and \ return self._ip == other._ip and \
round(self._time, 2) == round(other._time, 2) and \ round(self._time, 2) == round(other._time, 2) and \
self._data == other._data self._data == other._data
except AttributeError: except AttributeError:
return False return False
def update(self, ticket):
for n in ticket.__slots__:
v = getattr(ticket, n, None)
if v is not None:
setattr(self, n, v)
def setIP(self, value): def setIP(self, value):
# guarantee using IPAddr instead of unicode, str for the IP # guarantee using IPAddr instead of unicode, str for the IP
if isinstance(value, basestring): if isinstance(value, basestring):
value = IPAddr(value) value = IPAddr(value)
self.__ip = value self._ip = value
def getID(self): def getID(self):
return self._data.get('fid', self.__ip) return self._data.get('fid', self._ip)
def getIP(self): def getIP(self):
return self.__ip return self._ip
def setTime(self, value): def setTime(self, value):
self._time = value self._time = value
@ -204,21 +213,21 @@ class FailTicket(Ticket):
def __init__(self, ip=None, time=None, matches=None, data={}, ticket=None): def __init__(self, ip=None, time=None, matches=None, data={}, ticket=None):
# this class variables: # this class variables:
self.__retry = 0 self._retry = 0
self.__lastReset = None self._lastReset = None
# create/copy using default ticket constructor: # create/copy using default ticket constructor:
Ticket.__init__(self, ip, time, matches, data, ticket) Ticket.__init__(self, ip, time, matches, data, ticket)
# init: # init:
if ticket is None: if ticket is None:
self.__lastReset = time if time is not None else self.getTime() self._lastReset = time if time is not None else self.getTime()
if not self.__retry: if not self._retry:
self.__retry = self._data['failures']; self._retry = self._data['failures'];
def setRetry(self, value): def setRetry(self, value):
""" Set artificial retry count, normally equal failures / attempt, """ Set artificial retry count, normally equal failures / attempt,
used in incremental features (BanTimeIncr) to increase retry count for bad IPs used in incremental features (BanTimeIncr) to increase retry count for bad IPs
""" """
self.__retry = value self._retry = value
if not self._data['failures']: if not self._data['failures']:
self._data['failures'] = 1 self._data['failures'] = 1
if not value: if not value:
@ -229,10 +238,10 @@ class FailTicket(Ticket):
""" Returns failures / attempt count or """ Returns failures / attempt count or
artificial retry count increased for bad IPs artificial retry count increased for bad IPs
""" """
return max(self.__retry, self._data['failures']) return max(self._retry, self._data['failures'])
def inc(self, matches=None, attempt=1, count=1): def inc(self, matches=None, attempt=1, count=1):
self.__retry += count self._retry += count
self._data['failures'] += attempt self._data['failures'] += attempt
if matches: if matches:
# we should duplicate "matches", because possibly referenced to multiple tickets: # we should duplicate "matches", because possibly referenced to multiple tickets:
@ -249,15 +258,24 @@ class FailTicket(Ticket):
return self._time return self._time
def getLastReset(self): def getLastReset(self):
return self.__lastReset return self._lastReset
def setLastReset(self, value): def setLastReset(self, value):
self.__lastReset = value self._lastReset = value
@staticmethod
def wrap(o):
o.__class__ = FailTicket
return o
## ##
# Ban Ticket. # Ban Ticket.
# #
# This class extends the Ticket class. It is mainly used by the BanManager. # This class extends the Ticket class. It is mainly used by the BanManager.
class BanTicket(Ticket): class BanTicket(FailTicket):
pass
@staticmethod
def wrap(o):
o.__class__ = BanTicket
return o