~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/services/timeline/timeline.py

  • Committer: Danilo Segan
  • Date: 2011-04-22 14:02:29 UTC
  • mto: This revision was merged to the branch mainline in revision 12910.
  • Revision ID: danilo@canonical.com-20110422140229-zhq4d4c2k8jpglhf
Ignore hidden files when building combined JS file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2010 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
"""Coordinate a sequence of non overlapping TimedActionss."""
 
5
 
 
6
__all__ = ['Timeline']
 
7
 
 
8
__metaclass__ = type
 
9
 
 
10
import datetime
 
11
 
 
12
from pytz import utc as UTC
 
13
 
 
14
from timedaction import TimedAction
 
15
from nestingtimedaction import NestingTimedAction
 
16
 
 
17
 
 
18
class OverlappingActionError(Exception):
 
19
    """A new action was attempted without finishing the prior one."""
 
20
    # To make analysis easy we do not permit overlapping actions by default:
 
21
    # each action that is being timed and accrued must complete before the next
 
22
    # is started. This means, for instance, that sending mail cannot do SQL
 
23
    # queries, as both are timed and accrued. OTOH it makes analysis and
 
24
    # serialisation of timelines simpler, and for the current use cases in 
 
25
    # Launchpad this is sufficient. This constraint should not be considered
 
26
    # sacrosanct - if, in future, we desire timelines with overlapping actions,
 
27
    # as long as the OOPS analysis code is extended to generate sensible
 
28
    # reports in those situations, this can be changed. In the interim, actions
 
29
    # can be explicitly setup to permit nesting by passing allow_nested=True
 
30
    # which will cause the action to be recorded with 0 duration and a -start
 
31
    # and -stop suffix added to its category. This is potentially lossy but
 
32
    # good enough to get nested metrics and can be iterated on in the future to
 
33
    # do an actual stacked/tree model of actions - if needed.
 
34
 
 
35
 
 
36
class Timeline:
 
37
    """A sequence of TimedActions.
 
38
 
 
39
    This is used for collecting expensive/external actions inside Launchpad
 
40
    requests.
 
41
 
 
42
    :ivar actions: The actions.
 
43
    :ivar baseline: The point the timeline starts at.
 
44
    """
 
45
 
 
46
    def __init__(self, actions=None):
 
47
        """Create a Timeline.
 
48
        
 
49
        :param actions: An optional object to use to store the timeline. This
 
50
            must implement the list protocol.
 
51
        """
 
52
        if actions is None:
 
53
            actions = []
 
54
        self.actions = actions
 
55
        self.baseline = datetime.datetime.now(UTC)
 
56
 
 
57
    def start(self, category, detail, allow_nested=False):
 
58
        """Create a new TimedAction at the end of the timeline.
 
59
 
 
60
        :param category: the category for the action.
 
61
        :param detail: The detail for the action.
 
62
        :param allow_nested: If True treat this action as a nested action -
 
63
            record it twice with 0 duration, once at the start and once at the
 
64
            finish.
 
65
        :return: A TimedAction for that category and detail.
 
66
        """
 
67
        if allow_nested:
 
68
            result = NestingTimedAction(category, detail, self)
 
69
        else:
 
70
            result = TimedAction(category, detail, self)
 
71
        if self.actions and self.actions[-1].duration is None:
 
72
            raise OverlappingActionError(self.actions[-1], result)
 
73
        self.actions.append(result)
 
74
        return result