~ahasenack/landscape-client/landscape-client-1.5.5-0ubuntu0.9.04.0

« back to all changes in this revision

Viewing changes to landscape/accumulate.py

  • Committer: Bazaar Package Importer
  • Author(s): Rick Clark
  • Date: 2008-09-08 16:35:57 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080908163557-l3ixzj5dxz37wnw2
Tags: 1.0.18-0ubuntu1
New upstream release 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
The accumulation logic generates data points for times that are a
 
3
multiple of a step size.  In other words, if the step size is 300
 
4
seconds, any data reported by the accumulation code will always be for
 
5
a timestamp that is a multiple of 300.  The purpose of this behaviour
 
6
is to (a) limit the amount of data that is sent to the server and (b)
 
7
provide data in a predictable format to make server-side handling of
 
8
the data straight-forward.  A nice side-effect of providing data at a
 
9
known step-interval is that the server can detect blackholes in the
 
10
data simply by testing for the absence of data points at step
 
11
intervals.
 
12
 
 
13
Limiting the amount of data sent to the server and making the data
 
14
format predictable are both desirable attributes, but we need to
 
15
ensure the data reported is accurate.  We can't rely on plugins to
 
16
report data exactly at step boundaries and even if we could we
 
17
wouldn't necessarily end up with data points that are representative
 
18
of the resource being monitored.  We need a way to calculate a
 
19
representative data point from the set of data points that a plugin
 
20
provided during a step period.
 
21
 
 
22
Suppose we want to calculate data points for timestamps 300 and 600.
 
23
Assume a plugin runs at an interval less than 300 seconds to get
 
24
values to provide to the accumulator.  Each value received by the
 
25
accumulator is used to update a data point that will be sent to the
 
26
server when we cross the step boundary.  The algorithm, based on
 
27
derivatives, is:
 
28
 
 
29
  (current time - previous time) * value + last accumulated value
 
30
 
 
31
If the 'last accumulated value' isn't available, it defaults to 0.
 
32
For example, consider these timestamp/load average measurements:
 
33
300/2.0, 375/3.0, 550/3.5 and 650/0.5.  Also assume we have no data
 
34
prior to 300/2.0.  This data would be processed as follows:
 
35
 
 
36
  Input       Calculation                   Accumulated Value
 
37
  -----       -----------                   -----------------
 
38
  300/2.0     (300 - 300) * 2.0 + 0         0.0
 
39
 
 
40
  375/3.0     (375 - 300) * 3.0 + 0.0       225.0
 
41
 
 
42
  550/3.5     (550 - 375) * 3.5 + 225.0     837.5
 
43
 
 
44
  650/0.5     (600 - 550) * 0.5 + 837.5     862.5
 
45
 
 
46
Notice that the last value crosses a step boundary; the calculation
 
47
for this value is:
 
48
 
 
49
  (step boundary time - previous time) * value + last accumulated value
 
50
 
 
51
This yields the final accumulated value for the step period we've just
 
52
traversed.  The data point sent to the server is generated using the
 
53
following calculation:
 
54
 
 
55
  accumulated value / step interval size
 
56
 
 
57
The data point sent to the server in our example would be:
 
58
 
 
59
  862.5 / 300 = 2.875
 
60
 
 
61
This value is representative of the activity that actually occurred
 
62
and is returned to the plugin to queue for delivery to the server.
 
63
The accumulated value for the next interval is calculated using the
 
64
portion of time that crossed into the new step period:
 
65
 
 
66
  Input       Calculation                   Accumulated Value
 
67
  -----       -----------                   -----------------
 
68
  650/0.5     (650 - 600) * 0.5 + 0         25
 
69
 
 
70
And so the logic goes, continuing in a similar fashion, yielding
 
71
representative data at each step boundary.
 
72
"""
 
73
 
 
74
class Accumulator(object):
 
75
 
 
76
    def __init__(self, persist, step_size):
 
77
        self._persist = persist
 
78
        self._step_size = step_size
 
79
 
 
80
    def __call__(self, new_timestamp, new_free_space, key):
 
81
        previous_timestamp, accumulated_value = self._persist.get(key, (0, 0))
 
82
        accumulated_value, step_data = \
 
83
            accumulate(previous_timestamp, accumulated_value,
 
84
                       new_timestamp, new_free_space, self._step_size)
 
85
        self._persist.set(key, (new_timestamp, accumulated_value))
 
86
        return step_data
 
87
 
 
88
 
 
89
def accumulate(previous_timestamp, accumulated_value,
 
90
               new_timestamp, new_value,
 
91
               step_size):
 
92
    previous_step = previous_timestamp // step_size
 
93
    new_step = new_timestamp // step_size
 
94
    step_boundary = new_step * step_size
 
95
    step_diff = new_step - previous_step
 
96
    step_data = None
 
97
 
 
98
    if step_diff == 0:
 
99
        diff = new_timestamp - previous_timestamp
 
100
        accumulated_value += diff * new_value
 
101
    elif step_diff == 1:
 
102
        diff = step_boundary - previous_timestamp
 
103
        accumulated_value += diff * new_value
 
104
        step_value = float(accumulated_value) / step_size
 
105
        step_data = (step_boundary, step_value)
 
106
        diff = new_timestamp - step_boundary
 
107
        accumulated_value = diff * new_value
 
108
    else:
 
109
        diff = new_timestamp - step_boundary
 
110
        accumulated_value = diff * new_value
 
111
 
 
112
    return accumulated_value, step_data