|  | @@ -26,11 +26,14 @@ from heapq import heappush, heappop
 | 
	
		
			
				|  |  |  from itertools import islice
 | 
	
		
			
				|  |  |  from operator import itemgetter
 | 
	
		
			
				|  |  |  from time import time
 | 
	
		
			
				|  |  | +from weakref import ref
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  from kombu.clocks import timetuple
 | 
	
		
			
				|  |  | +from kombu.utils import cached_property
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  from celery import states
 | 
	
		
			
				|  |  | -from celery.five import items, values
 | 
	
		
			
				|  |  | +from celery.five import class_property, items, values
 | 
	
		
			
				|  |  | +from celery.utils import deprecated
 | 
	
		
			
				|  |  |  from celery.utils.functional import LRUCache
 | 
	
		
			
				|  |  |  from celery.utils.log import get_logger
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -92,14 +95,14 @@ def with_unique_field(attr):
 | 
	
		
			
				|  |  |  @with_unique_field('hostname')
 | 
	
		
			
				|  |  |  class Worker(object):
 | 
	
		
			
				|  |  |      """Worker State."""
 | 
	
		
			
				|  |  | +    clock = 0
 | 
	
		
			
				|  |  |      heartbeat_max = 4
 | 
	
		
			
				|  |  |      expire_window = HEARTBEAT_EXPIRE_WINDOW
 | 
	
		
			
				|  |  |      pid = None
 | 
	
		
			
				|  |  | -    _defaults = {'hostname': None, 'pid': None, 'freq': 60}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    _fields = ('hostname', 'pid', 'freq', 'heartbeats', 'clock')
 | 
	
		
			
				|  |  |      if not PYPY:
 | 
	
		
			
				|  |  | -        __slots__ = ('hostname', 'pid', 'freq',
 | 
	
		
			
				|  |  | -                     'heartbeats', 'clock', '__dict__')
 | 
	
		
			
				|  |  | +        __slots__ = _fields + ('event', '__dict__', '__weakref__')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def __init__(self, hostname=None, pid=None, freq=60):
 | 
	
		
			
				|  |  |          self.hostname = hostname
 | 
	
	
		
			
				|  | @@ -142,24 +145,6 @@ class Worker(object):
 | 
	
		
			
				|  |  |          for k, v in items(dict(f, **kw) if kw else f):
 | 
	
		
			
				|  |  |              setattr(self, k, v)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def update_heartbeat(self, received, timestamp):
 | 
	
		
			
				|  |  | -        self.event(None, timestamp, received)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def on_online(self, timestamp=None, local_received=None, **fields):
 | 
	
		
			
				|  |  | -        """Deprecated, to be removed in 3.1, use:
 | 
	
		
			
				|  |  | -        ``.event('online', timestamp, local_received, fields)``."""
 | 
	
		
			
				|  |  | -        self.event('online', timestamp, local_received, fields)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def on_offline(self, timestamp=None, local_received=None, **fields):
 | 
	
		
			
				|  |  | -        """Deprecated, to be removed in 3.1, use:
 | 
	
		
			
				|  |  | -        ``.event('offline', timestamp, local_received, fields)``."""
 | 
	
		
			
				|  |  | -        self.event('offline', timestamp, local_received, fields)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def on_heartbeat(self, timestamp=None, local_received=None, **fields):
 | 
	
		
			
				|  |  | -        """Deprecated, to be removed in 3.1, use:
 | 
	
		
			
				|  |  | -        ``.event('heartbeat', timestamp, local_received, fields)``."""
 | 
	
		
			
				|  |  | -        self.event('heartbeat', timestamp, local_received, fields)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |      def __repr__(self):
 | 
	
		
			
				|  |  |          return R_WORKER.format(self)
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -180,19 +165,46 @@ class Worker(object):
 | 
	
		
			
				|  |  |      def id(self):
 | 
	
		
			
				|  |  |          return '{0.hostname}.{0.pid}'.format(self)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @deprecated('3.2' '3.3')
 | 
	
		
			
				|  |  | +    def update_heartbeat(self, received, timestamp):
 | 
	
		
			
				|  |  | +        self.event(None, timestamp, received)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  | +    def on_online(self, timestamp=None, local_received=None, **fields):
 | 
	
		
			
				|  |  | +        self.event('online', timestamp, local_received, fields)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  | +    def on_offline(self, timestamp=None, local_received=None, **fields):
 | 
	
		
			
				|  |  | +        self.event('offline', timestamp, local_received, fields)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  | +    def on_heartbeat(self, timestamp=None, local_received=None, **fields):
 | 
	
		
			
				|  |  | +        self.event('heartbeat', timestamp, local_received, fields)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @class_property
 | 
	
		
			
				|  |  | +    def _defaults(cls):
 | 
	
		
			
				|  |  | +        """Deprecated, to be removed in 3.2"""
 | 
	
		
			
				|  |  | +        source = cls()
 | 
	
		
			
				|  |  | +        return dict((k, getattr(source, k)) for k in cls._fields)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  @with_unique_field('uuid')
 | 
	
		
			
				|  |  |  class Task(object):
 | 
	
		
			
				|  |  |      """Task State."""
 | 
	
		
			
				|  |  | +    name = received = sent = started = succeeded = failed = retried = \
 | 
	
		
			
				|  |  | +        revoked = args = kwargs = eta = expires = retries = worker = result = \
 | 
	
		
			
				|  |  | +        exception = timestamp = runtime = traceback = exchange = \
 | 
	
		
			
				|  |  | +        routing_key = None
 | 
	
		
			
				|  |  | +    state = states.PENDING
 | 
	
		
			
				|  |  | +    clock = 0
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    _fields = ('uuid', 'name', 'state', 'received', 'sent', 'started',
 | 
	
		
			
				|  |  | +               'succeeded', 'failed', 'retried', 'revoked', 'args', 'kwargs',
 | 
	
		
			
				|  |  | +               'eta', 'expires', 'retries', 'worker', 'result', 'exception',
 | 
	
		
			
				|  |  | +               'timestamp', 'runtime', 'traceback', 'exchange', 'routing_key',
 | 
	
		
			
				|  |  | +               'clock')
 | 
	
		
			
				|  |  |      if not PYPY:
 | 
	
		
			
				|  |  | -        __slots__ = (
 | 
	
		
			
				|  |  | -            'uuid', 'name', 'state', 'received', 'sent', 'started',
 | 
	
		
			
				|  |  | -            'succeeded', 'failed', 'retried', 'revoked', 'args', 'kwargs',
 | 
	
		
			
				|  |  | -            'eta', 'expires', 'retries', 'worker', 'result',
 | 
	
		
			
				|  |  | -            'exception', 'timestamp', 'runtime',
 | 
	
		
			
				|  |  | -            'traceback', 'exchange', 'routing_key', 'clock',
 | 
	
		
			
				|  |  | -            '__dict__',
 | 
	
		
			
				|  |  | -        )
 | 
	
		
			
				|  |  | +        __slots__ = _fields + ('__dict__', '__weakref__')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      #: How to merge out of order events.
 | 
	
		
			
				|  |  |      #: Disorder is detected by logical ordering (e.g. :event:`task-received`
 | 
	
	
		
			
				|  | @@ -202,77 +214,18 @@ class Task(object):
 | 
	
		
			
				|  |  |      #: that state. ``(RECEIVED, ('name', 'args')``, means the name and args
 | 
	
		
			
				|  |  |      #: fields are always taken from the RECEIVED state, and any values for
 | 
	
		
			
				|  |  |      #: these fields received before or after is simply ignored.
 | 
	
		
			
				|  |  | -    merge_rules = {
 | 
	
		
			
				|  |  | -        states.RECEIVED: ('name', 'args', 'kwargs',
 | 
	
		
			
				|  |  | -                          'retries', 'eta', 'expires'),
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    merge_rules = {states.RECEIVED: ('name', 'args', 'kwargs',
 | 
	
		
			
				|  |  | +                                     'retries', 'eta', 'expires')}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      #: meth:`info` displays these fields by default.
 | 
	
		
			
				|  |  | -    _info_fields = ('args', 'kwargs', 'retries', 'result',
 | 
	
		
			
				|  |  | -                    'eta', 'runtime', 'expires', 'exception',
 | 
	
		
			
				|  |  | -                    'exchange', 'routing_key')
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def __init__(self, uuid=None, name=None, state=states.PENDING,
 | 
	
		
			
				|  |  | -                 received=None, sent=None, started=None, succeeded=None,
 | 
	
		
			
				|  |  | -                 failed=None, retried=None, revoked=None, args=None,
 | 
	
		
			
				|  |  | -                 kwargs=None, eta=None, expires=None, retries=None,
 | 
	
		
			
				|  |  | -                 worker=None, result=None, exception=None, timestamp=None,
 | 
	
		
			
				|  |  | -                 runtime=None, traceback=None, exchange=None,
 | 
	
		
			
				|  |  | -                 routing_key=None, clock=0, **fields):
 | 
	
		
			
				|  |  | -        self.uuid = uuid
 | 
	
		
			
				|  |  | -        self.name = name
 | 
	
		
			
				|  |  | -        self.state = state
 | 
	
		
			
				|  |  | -        self.received = received
 | 
	
		
			
				|  |  | -        self.sent = sent
 | 
	
		
			
				|  |  | -        self.started = started
 | 
	
		
			
				|  |  | -        self.succeeded = succeeded
 | 
	
		
			
				|  |  | -        self.failed = failed
 | 
	
		
			
				|  |  | -        self.retried = retried
 | 
	
		
			
				|  |  | -        self.revoked = revoked
 | 
	
		
			
				|  |  | -        self.args = args
 | 
	
		
			
				|  |  | -        self.kwargs = kwargs
 | 
	
		
			
				|  |  | -        self.eta = eta
 | 
	
		
			
				|  |  | -        self.expires = expires
 | 
	
		
			
				|  |  | -        self.retries = retries
 | 
	
		
			
				|  |  | -        self.worker = worker
 | 
	
		
			
				|  |  | -        self.result = result
 | 
	
		
			
				|  |  | -        self.exception = exception
 | 
	
		
			
				|  |  | -        self.timestamp = timestamp
 | 
	
		
			
				|  |  | -        self.runtime = runtime
 | 
	
		
			
				|  |  | -        self.traceback = traceback
 | 
	
		
			
				|  |  | -        self.exchange = exchange
 | 
	
		
			
				|  |  | -        self.routing_key = routing_key
 | 
	
		
			
				|  |  | -        self.clock = clock
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def update(self, state, timestamp, fields,
 | 
	
		
			
				|  |  | -               _state=states.state, RETRY=states.RETRY):
 | 
	
		
			
				|  |  | -        """Update state from new event.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        :param state: State from event.
 | 
	
		
			
				|  |  | -        :param timestamp: Timestamp from event.
 | 
	
		
			
				|  |  | -        :param fields: Event data.
 | 
	
		
			
				|  |  | +    _info_fields = ('args', 'kwargs', 'retries', 'result', 'eta', 'runtime',
 | 
	
		
			
				|  |  | +                    'expires', 'exception', 'exchange', 'routing_key')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        """
 | 
	
		
			
				|  |  | -        time_received = fields.get('local_received') or 0
 | 
	
		
			
				|  |  | -        if self.worker and time_received:
 | 
	
		
			
				|  |  | -            self.worker.update_heartbeat(time_received, timestamp)
 | 
	
		
			
				|  |  | -        if state != RETRY and self.state != RETRY and \
 | 
	
		
			
				|  |  | -                _state(state) < _state(self.state):
 | 
	
		
			
				|  |  | -            # this state logically happens-before the current state, so merge.
 | 
	
		
			
				|  |  | -            self.merge(state, timestamp, fields)
 | 
	
		
			
				|  |  | -        else:
 | 
	
		
			
				|  |  | -            self.state = state
 | 
	
		
			
				|  |  | -            self.timestamp = timestamp
 | 
	
		
			
				|  |  | -            for key, value in items(self.fields):
 | 
	
		
			
				|  |  | -                setattr(self, key, value)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    def merge(self, state, timestamp, fields):
 | 
	
		
			
				|  |  | -        """Merge with out of order event."""
 | 
	
		
			
				|  |  | -        keep = self.merge_rules.get(state)
 | 
	
		
			
				|  |  | -        if keep is not None:
 | 
	
		
			
				|  |  | -            fields = dict((k, v) for k, v in items(fields) if k in keep)
 | 
	
		
			
				|  |  | -        for key, value in items(fields):
 | 
	
		
			
				|  |  | -            setattr(self, key, value)
 | 
	
		
			
				|  |  | +    def __init__(self, uuid=None, **kwargs):
 | 
	
		
			
				|  |  | +        self.uuid = uuid
 | 
	
		
			
				|  |  | +        if kwargs:
 | 
	
		
			
				|  |  | +            for k, v in items(kwargs):
 | 
	
		
			
				|  |  | +                setattr(self, k, v)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def event(self, type_, timestamp=None, local_received=None, fields=None,
 | 
	
		
			
				|  |  |                precedence=states.precedence, items=items, dict=dict,
 | 
	
	
		
			
				|  | @@ -335,49 +288,62 @@ class Task(object):
 | 
	
		
			
				|  |  |      def ready(self):
 | 
	
		
			
				|  |  |          return self.state in states.READY_STATES
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  |      def on_sent(self, timestamp=None, **fields):
 | 
	
		
			
				|  |  | -        """Deprecated, to be removed in 3.2, use:
 | 
	
		
			
				|  |  | -        ``.event('sent', timestamp, fields)``."""
 | 
	
		
			
				|  |  |          self.event('sent', timestamp, fields)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  |      def on_received(self, timestamp=None, **fields):
 | 
	
		
			
				|  |  | -        """Deprecated, to be removed in 3.2, use:
 | 
	
		
			
				|  |  | -        ``.event('received', timestamp, fields)``."""
 | 
	
		
			
				|  |  |          self.event('received', timestamp, fields)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  |      def on_started(self, timestamp=None, **fields):
 | 
	
		
			
				|  |  | -        """Deprecated, to be removed in 3.2, use:
 | 
	
		
			
				|  |  | -        ``.event('started', timestamp, fields)``."""
 | 
	
		
			
				|  |  |          self.event('started', timestamp, fields)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  |      def on_failed(self, timestamp=None, **fields):
 | 
	
		
			
				|  |  | -        """Deprecated, to be removed in 3.2, use:
 | 
	
		
			
				|  |  | -        ``.event('failed', timestamp, fields)``."""
 | 
	
		
			
				|  |  |          self.event('failed', timestamp, fields)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  |      def on_retried(self, timestamp=None, **fields):
 | 
	
		
			
				|  |  | -        """Deprecated, to be removed in 3.2, use:
 | 
	
		
			
				|  |  | -        ``.event('retried', timestamp, fields)``."""
 | 
	
		
			
				|  |  |          self.event('retried', timestamp, fields)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @deprecated('3.2' '3.3')
 | 
	
		
			
				|  |  |      def on_succeeded(self, timestamp=None, **fields):
 | 
	
		
			
				|  |  | -        """Deprecated, to be removed in 3.2, use:
 | 
	
		
			
				|  |  | -        ``.event('succeeded', timestamp, fields)``."""
 | 
	
		
			
				|  |  |          self.event('succeeded', timestamp, fields)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  |      def on_revoked(self, timestamp=None, **fields):
 | 
	
		
			
				|  |  | -        """Deprecated, to be removed in 3.2, use:
 | 
	
		
			
				|  |  | -        ``.event('revoked', timestamp, fields)``."""
 | 
	
		
			
				|  |  |          self.event('revoked', timestamp, fields)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  |      def on_unknown_event(self, shortype, timestamp=None, **fields):
 | 
	
		
			
				|  |  | -        """Deprecated, to be removed in 3.2, use:
 | 
	
		
			
				|  |  | -        ``.event(type, timestamp, fields)``."""
 | 
	
		
			
				|  |  |          self.event(shortype, timestamp, fields)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  | +    def update(self, state, timestamp, fields,
 | 
	
		
			
				|  |  | +               _state=states.state, RETRY=states.RETRY):
 | 
	
		
			
				|  |  | +        return self.event(state, timestamp, None, fields)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @deprecated('3.2', '3.3')
 | 
	
		
			
				|  |  | +    def merge(self, state, timestamp, fields):
 | 
	
		
			
				|  |  | +        keep = self.merge_rules.get(state)
 | 
	
		
			
				|  |  | +        if keep is not None:
 | 
	
		
			
				|  |  | +            fields = dict((k, v) for k, v in items(fields) if k in keep)
 | 
	
		
			
				|  |  | +        for key, value in items(fields):
 | 
	
		
			
				|  |  | +            setattr(self, key, value)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @class_property
 | 
	
		
			
				|  |  | +    def _defaults(cls):
 | 
	
		
			
				|  |  | +        """Deprecated, to be removed in 3.2."""
 | 
	
		
			
				|  |  | +        source = cls()
 | 
	
		
			
				|  |  | +        return dict((k, getattr(source, k)) for k in source._fields)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class State(object):
 | 
	
		
			
				|  |  |      """Records clusters state."""
 | 
	
		
			
				|  |  | +    Worker = Worker
 | 
	
		
			
				|  |  | +    Task = Task
 | 
	
		
			
				|  |  |      event_count = 0
 | 
	
		
			
				|  |  |      task_count = 0
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -394,7 +360,11 @@ class State(object):
 | 
	
		
			
				|  |  |          self.max_tasks_in_memory = max_tasks_in_memory
 | 
	
		
			
				|  |  |          self._mutex = threading.Lock()
 | 
	
		
			
				|  |  |          self.handlers = {}
 | 
	
		
			
				|  |  | -        self._event = self._create_dispatcher()
 | 
	
		
			
				|  |  | +        self._seen_types = set()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @cached_property
 | 
	
		
			
				|  |  | +    def _event(self):
 | 
	
		
			
				|  |  | +        return self._create_dispatcher()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def freeze_while(self, fun, *args, **kwargs):
 | 
	
		
			
				|  |  |          clear_after = kwargs.pop('clear_after', False)
 | 
	
	
		
			
				|  | @@ -441,8 +411,8 @@ class State(object):
 | 
	
		
			
				|  |  |                  worker.update(kwargs)
 | 
	
		
			
				|  |  |              return worker, False
 | 
	
		
			
				|  |  |          except KeyError:
 | 
	
		
			
				|  |  | -            worker = self.workers[hostname] = Worker(
 | 
	
		
			
				|  |  | -                hostname=hostname, **kwargs)
 | 
	
		
			
				|  |  | +            worker = self.workers[hostname] = self.Worker(
 | 
	
		
			
				|  |  | +                hostname, **kwargs)
 | 
	
		
			
				|  |  |              return worker, True
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def get_or_create_task(self, uuid):
 | 
	
	
		
			
				|  | @@ -450,7 +420,7 @@ class State(object):
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  |              return self.tasks[uuid], False
 | 
	
		
			
				|  |  |          except KeyError:
 | 
	
		
			
				|  |  | -            task = self.tasks[uuid] = Task(uuid=uuid)
 | 
	
		
			
				|  |  | +            task = self.tasks[uuid] = self.Task(uuid)
 | 
	
		
			
				|  |  |              return task, True
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def event(self, event):
 | 
	
	
		
			
				|  | @@ -468,14 +438,18 @@ class State(object):
 | 
	
		
			
				|  |  |      def _create_dispatcher(self):
 | 
	
		
			
				|  |  |          get_handler = self.handlers.__getitem__
 | 
	
		
			
				|  |  |          event_callback = self.event_callback
 | 
	
		
			
				|  |  | -        get_or_create_worker = self.get_or_create_worker
 | 
	
		
			
				|  |  | -        get_or_create_task = self.get_or_create_task
 | 
	
		
			
				|  |  |          wfields = itemgetter('hostname', 'timestamp', 'local_received')
 | 
	
		
			
				|  |  |          tfields = itemgetter('uuid', 'hostname', 'timestamp', 'local_received')
 | 
	
		
			
				|  |  |          taskheap = self._taskheap
 | 
	
		
			
				|  |  |          maxtasks = self.max_tasks_in_memory * 2
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        def _event(event, timetuple=timetuple):
 | 
	
		
			
				|  |  | +        add_type = self._seen_types.add
 | 
	
		
			
				|  |  | +        tasks, Task = self.tasks, self.Task
 | 
	
		
			
				|  |  | +        workers, Worker = self.workers, self.Worker
 | 
	
		
			
				|  |  | +        # avoid updating LRU entry at getitem
 | 
	
		
			
				|  |  | +        get_worker, get_task = workers.data.__getitem__, tasks.data.__getitem__
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        def _event(event,
 | 
	
		
			
				|  |  | +                   timetuple=timetuple, KeyError=KeyError, created=True):
 | 
	
		
			
				|  |  |              self.event_count += 1
 | 
	
		
			
				|  |  |              if event_callback:
 | 
	
		
			
				|  |  |                  event_callback(self, event)
 | 
	
	
		
			
				|  | @@ -493,30 +467,39 @@ class State(object):
 | 
	
		
			
				|  |  |                  except KeyError:
 | 
	
		
			
				|  |  |                      pass
 | 
	
		
			
				|  |  |                  else:
 | 
	
		
			
				|  |  | -                    worker, created = get_or_create_worker(hostname)
 | 
	
		
			
				|  |  | +                    try:
 | 
	
		
			
				|  |  | +                        worker, created = get_worker(hostname), False
 | 
	
		
			
				|  |  | +                    except KeyError:
 | 
	
		
			
				|  |  | +                        worker = workers[hostname] = Worker(hostname)
 | 
	
		
			
				|  |  |                      worker.event(subject, timestamp, local_received, event)
 | 
	
		
			
				|  |  |                      return created
 | 
	
		
			
				|  |  |              elif group == 'task':
 | 
	
		
			
				|  |  |                  uuid, hostname, timestamp, local_received = tfields(event)
 | 
	
		
			
				|  |  |                  # task-sent event is sent by client, not worker
 | 
	
		
			
				|  |  |                  is_client_event = subject == 'sent'
 | 
	
		
			
				|  |  | -                task, created = get_or_create_task(uuid)
 | 
	
		
			
				|  |  | +                try:
 | 
	
		
			
				|  |  | +                    task, created = get_task(uuid), False
 | 
	
		
			
				|  |  | +                except KeyError:
 | 
	
		
			
				|  |  | +                    task = tasks[uuid] = Task(uuid)
 | 
	
		
			
				|  |  |                  if not is_client_event:
 | 
	
		
			
				|  |  | -                    worker, _ = get_or_create_worker(hostname)
 | 
	
		
			
				|  |  | +                    try:
 | 
	
		
			
				|  |  | +                        worker, created = get_worker(hostname), False
 | 
	
		
			
				|  |  | +                    except KeyError:
 | 
	
		
			
				|  |  | +                        worker = workers[hostname] = Worker(hostname)
 | 
	
		
			
				|  |  |                      task.worker = worker
 | 
	
		
			
				|  |  |                      if worker is not None and local_received:
 | 
	
		
			
				|  |  |                          worker.event(None, local_received, timestamp)
 | 
	
		
			
				|  |  |                  clock = 0 if is_client_event else event.get('clock')
 | 
	
		
			
				|  |  | -                heappush(
 | 
	
		
			
				|  |  | -                    taskheap, timetuple(clock, timestamp, worker.id, task),
 | 
	
		
			
				|  |  | -                )
 | 
	
		
			
				|  |  | -                while len(taskheap) > maxtasks:
 | 
	
		
			
				|  |  | +                heappush(taskheap,
 | 
	
		
			
				|  |  | +                         timetuple(clock, timestamp, worker.id, ref(task)))
 | 
	
		
			
				|  |  | +                if len(taskheap) > maxtasks:
 | 
	
		
			
				|  |  |                      heappop(taskheap)
 | 
	
		
			
				|  |  | -                #if len(taskheap) > maxtasks:
 | 
	
		
			
				|  |  | -                #    heappop(taskheap)
 | 
	
		
			
				|  |  |                  if subject == 'received':
 | 
	
		
			
				|  |  |                      self.task_count += 1
 | 
	
		
			
				|  |  |                  task.event(subject, timestamp, local_received, event)
 | 
	
		
			
				|  |  | +                task_name = task.name
 | 
	
		
			
				|  |  | +                if task_name is not None:
 | 
	
		
			
				|  |  | +                    add_type(task_name)
 | 
	
		
			
				|  |  |                  return created
 | 
	
		
			
				|  |  |          return _event
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -531,10 +514,12 @@ class State(object):
 | 
	
		
			
				|  |  |          in ``(uuid, Task)`` tuples."""
 | 
	
		
			
				|  |  |          seen = set()
 | 
	
		
			
				|  |  |          for evtup in islice(reversed(self._taskheap), 0, limit):
 | 
	
		
			
				|  |  | -            uuid = evtup[3].uuid
 | 
	
		
			
				|  |  | -            if uuid not in seen:
 | 
	
		
			
				|  |  | -                yield uuid, evtup[3]
 | 
	
		
			
				|  |  | -                seen.add(uuid)
 | 
	
		
			
				|  |  | +            task = evtup[3]()
 | 
	
		
			
				|  |  | +            if task is not None:
 | 
	
		
			
				|  |  | +                uuid = task.uuid
 | 
	
		
			
				|  |  | +                if uuid not in seen:
 | 
	
		
			
				|  |  | +                    yield uuid, task
 | 
	
		
			
				|  |  | +                    seen.add(uuid)
 | 
	
		
			
				|  |  |      tasks_by_timestamp = tasks_by_time
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def tasks_by_type(self, name, limit=None):
 | 
	
	
		
			
				|  | @@ -561,8 +546,7 @@ class State(object):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def task_types(self):
 | 
	
		
			
				|  |  |          """Return a list of all seen task types."""
 | 
	
		
			
				|  |  | -        return list(sorted(set(task.name for task in values(self.tasks)
 | 
	
		
			
				|  |  | -                               if task.name is not None)))
 | 
	
		
			
				|  |  | +        return sorted(self._seen_types)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def alive_workers(self):
 | 
	
		
			
				|  |  |          """Return a list of (seemingly) alive workers."""
 |